From b4f2a13f7ff2cf7057cddf006b6608bd415cd272 Mon Sep 17 00:00:00 2001 From: retro100 Date: Sat, 6 Feb 2021 16:06:31 +0100 Subject: [PATCH] Sync to DOSBox SVN r4301 --- COPYING | 11 +- ChangeLog | 86 + INSTALL | 93 +- Makefile | 3 +- NEWS | 26 + README | 48 +- VERSION | 2 +- acinclude.m4 | 32 +- autogen.sh | 0 configure.in => configure.ac | 196 +- docs/README.video | 2 +- docs/dosbox.1 | 42 +- include/Makefile.am | 2 +- include/bios.h | 10 +- include/bios_disk.h | 22 +- include/callback.h | 13 +- include/control.h | 8 +- include/cpu.h | 11 +- include/cross.h | 9 +- include/debug.h | 8 +- include/dma.h | 16 +- include/dos_inc.h | 31 +- include/dos_system.h | 34 +- include/dosbox.h | 8 +- include/fpu.h | 32 +- include/hardware.h | 10 +- include/inout.h | 8 +- include/ipx.h | 8 +- include/ipxserver.h | 8 +- include/joystick.h | 8 +- include/keyboard.h | 8 +- include/logging.h | 8 +- include/mapper.h | 10 +- include/mem.h | 8 +- include/midi.h | 67 + include/mixer.h | 25 +- include/modules.h | 180 -- include/mouse.h | 11 +- include/paging.h | 14 +- include/pci_bus.h | 8 +- include/pic.h | 12 +- include/programs.h | 8 +- include/regs.h | 10 +- include/render.h | 12 +- include/serialport.h | 8 +- include/setup.h | 99 +- include/shell.h | 13 +- include/support.h | 9 +- include/timer.h | 8 +- include/vga.h | 16 +- include/video.h | 8 +- scripts/dosbox-installer.nsi | 2 +- src/Makefile.am | 3 +- src/cpu/callback.cpp | 148 +- src/cpu/core_dyn_x86.cpp | 191 +- src/cpu/core_dyn_x86/Makefile.am | 4 +- src/cpu/core_dyn_x86/cache.h | 90 +- src/cpu/core_dyn_x86/decoder.h | 720 +++-- src/cpu/core_dyn_x86/dyn_fpu.h | 162 +- src/cpu/core_dyn_x86/dyn_fpu_dh.h | 199 +- src/cpu/core_dyn_x86/helpers.h | 16 +- src/cpu/core_dyn_x86/risc_x64.h | 1278 ++++++++ src/cpu/core_dyn_x86/risc_x86.h | 43 +- src/cpu/core_dyn_x86/string.h | 16 +- src/cpu/core_dynrec.cpp | 39 +- src/cpu/core_dynrec/Makefile.am | 4 +- src/cpu/core_dynrec/cache.h | 14 +- src/cpu/core_dynrec/decoder.h | 10 +- src/cpu/core_dynrec/decoder_basic.h | 36 +- src/cpu/core_dynrec/decoder_opcodes.h | 27 +- src/cpu/core_dynrec/dyn_fpu.h | 37 +- src/cpu/core_dynrec/operators.h | 8 +- src/cpu/core_dynrec/risc_armv4le-common.h | 8 +- src/cpu/core_dynrec/risc_armv4le-o3.h | 878 +++--- src/cpu/core_dynrec/risc_armv4le-s3.h | 918 ------ src/cpu/core_dynrec/risc_armv4le-thumb-iw.h | 484 ++- src/cpu/core_dynrec/risc_armv4le-thumb-niw.h | 484 ++- src/cpu/core_dynrec/risc_armv4le-thumb.h | 462 ++- src/cpu/core_dynrec/risc_armv4le.h | 26 +- src/cpu/core_dynrec/risc_armv8le.h | 1238 ++++++++ src/cpu/core_dynrec/risc_mipsel32.h | 8 +- src/cpu/core_dynrec/risc_x64.h | 365 ++- src/cpu/core_dynrec/risc_x86.h | 8 +- src/cpu/core_full.cpp | 8 +- src/cpu/core_full/ea_lookup.h | 8 +- src/cpu/core_full/load.h | 22 +- src/cpu/core_full/loadwrite.h | 8 +- src/cpu/core_full/op.h | 8 +- src/cpu/core_full/optable.h | 20 +- src/cpu/core_full/save.h | 8 +- src/cpu/core_full/string.h | 15 +- src/cpu/core_full/support.h | 11 +- src/cpu/core_normal.cpp | 10 +- src/cpu/core_normal/helpers.h | 8 +- src/cpu/core_normal/prefix_0f.h | 8 +- src/cpu/core_normal/prefix_66.h | 20 +- src/cpu/core_normal/prefix_66_0f.h | 8 +- src/cpu/core_normal/prefix_none.h | 20 +- src/cpu/core_normal/string.h | 15 +- src/cpu/core_normal/support.h | 8 +- src/cpu/core_normal/table_ea.h | 8 +- src/cpu/core_prefetch.cpp | 10 +- src/cpu/core_simple.cpp | 13 +- src/cpu/cpu.cpp | 90 +- src/cpu/flags.cpp | 8 +- src/cpu/instructions.h | 71 +- src/cpu/lazyflags.h | 14 +- src/cpu/modrm.cpp | 8 +- src/cpu/modrm.h | 8 +- src/cpu/paging.cpp | 8 +- src/debug/debug.cpp | 709 +++-- src/debug/debug_disasm.cpp | 3 +- src/debug/debug_gui.cpp | 70 +- src/debug/debug_inc.h | 8 +- src/debug/debug_win32.cpp | 11 +- src/debug/disasm_tables.h | 8 +- src/dos/Makefile.am | 2 +- src/dos/cdrom.cpp | 8 +- src/dos/cdrom.h | 8 +- src/dos/cdrom_aspi_win32.cpp | 14 +- src/dos/cdrom_image.cpp | 48 +- src/dos/cdrom_ioctl_linux.cpp | 8 +- src/dos/cdrom_ioctl_os2.cpp | 8 +- src/dos/cdrom_ioctl_win32.cpp | 12 +- src/dos/dev_con.h | 85 +- src/dos/dos.cpp | 75 +- src/dos/dos_classes.cpp | 45 +- src/dos/dos_codepages.h | 6 +- src/dos/dos_devices.cpp | 15 +- src/dos/dos_execute.cpp | 128 +- src/dos/dos_files.cpp | 317 +- src/dos/dos_ioctl.cpp | 41 +- src/dos/dos_keyboard_layout.cpp | 12 +- src/dos/dos_keyboard_layout_data.h | 6 +- src/dos/dos_memory.cpp | 45 +- src/dos/dos_misc.cpp | 10 +- src/dos/dos_mscdex.cpp | 178 +- src/dos/dos_programs.cpp | 857 ++++-- src/dos/dos_tables.cpp | 20 +- src/dos/drive_cache.cpp | 108 +- src/dos/drive_fat.cpp | 286 +- src/dos/drive_iso.cpp | 79 +- src/dos/drive_local.cpp | 47 +- src/dos/drive_overlay.cpp | 1193 ++++++++ src/dos/drive_virtual.cpp | 17 +- src/dos/drives.cpp | 46 +- src/dos/drives.h | 85 +- src/dosbox.cpp | 291 +- src/fpu/fpu.cpp | 8 +- src/fpu/fpu_instructions.h | 56 +- src/fpu/fpu_instructions_x86.h | 531 ++-- src/gui/dosbox_logo.h | 8 +- src/gui/midi.cpp | 78 +- src/gui/midi_alsa.h | 15 +- src/gui/midi_coreaudio.h | 111 +- src/gui/midi_coremidi.h | 57 +- src/gui/midi_oss.h | 8 +- src/gui/midi_win32.h | 47 +- src/gui/render.cpp | 26 +- src/gui/render_loops.h | 8 +- src/gui/render_scalers.cpp | 8 +- src/gui/render_scalers.h | 8 +- src/gui/render_simple.h | 8 +- src/gui/render_templates.h | 14 +- src/gui/render_templates_hq.h | 14 +- src/gui/render_templates_hq2x.h | 8 +- src/gui/render_templates_hq3x.h | 8 +- src/gui/render_templates_sai.h | 8 +- src/gui/sdl_gui.cpp | 19 +- src/gui/sdl_mapper.cpp | 181 +- src/gui/sdlmain.cpp | 575 ++-- src/hardware/Makefile.am | 2 +- src/hardware/adlib.cpp | 171 +- src/hardware/adlib.h | 20 +- src/hardware/cmos.cpp | 8 +- src/hardware/dbopl.cpp | 98 +- src/hardware/dbopl.h | 20 +- src/hardware/disney.cpp | 20 +- src/hardware/dma.cpp | 28 +- src/hardware/gameblaster.cpp | 461 +-- src/hardware/gus.cpp | 246 +- src/hardware/hardware.cpp | 12 +- src/hardware/iohandler.cpp | 18 +- src/hardware/ipx.cpp | 26 +- src/hardware/ipxserver.cpp | 8 +- src/hardware/joystick.cpp | 183 +- src/hardware/keyboard.cpp | 26 +- src/hardware/mame/Makefile.am | 9 + src/hardware/mame/emu.h | 142 + src/hardware/mame/fmopl.cpp | 2577 ++++++++++++++++ src/hardware/mame/fmopl.h | 113 + src/hardware/mame/saa1099.cpp | 471 +++ src/hardware/mame/saa1099.h | 120 + src/hardware/mame/sn76496.cpp | 518 ++++ src/hardware/mame/sn76496.h | 181 ++ src/hardware/mame/ymdeltat.cpp | 650 ++++ src/hardware/mame/ymdeltat.h | 87 + src/hardware/mame/ymf262.cpp | 2792 ++++++++++++++++++ src/hardware/mame/ymf262.h | 40 + src/hardware/memory.cpp | 10 +- src/hardware/mixer.cpp | 308 +- src/hardware/mpu401.cpp | 83 +- src/hardware/opl.cpp | 6 +- src/hardware/opl.h | 2 +- src/hardware/pci_bus.cpp | 8 +- src/hardware/pci_devices.h | 8 +- src/hardware/pcspeaker.cpp | 15 +- src/hardware/pic.cpp | 440 +-- src/hardware/sblaster.cpp | 245 +- src/hardware/serialport/directserial.cpp | 8 +- src/hardware/serialport/directserial.h | 8 +- src/hardware/serialport/libserial.cpp | 65 +- src/hardware/serialport/libserial.h | 11 +- src/hardware/serialport/misc_util.cpp | 12 +- src/hardware/serialport/misc_util.h | 8 +- src/hardware/serialport/nullmodem.cpp | 10 +- src/hardware/serialport/nullmodem.h | 8 +- src/hardware/serialport/serialdummy.cpp | 8 +- src/hardware/serialport/serialdummy.h | 8 +- src/hardware/serialport/serialport.cpp | 98 +- src/hardware/serialport/softmodem.cpp | 40 +- src/hardware/serialport/softmodem.h | 8 +- src/hardware/tandy_sound.cpp | 295 +- src/hardware/timer.cpp | 14 +- src/hardware/vga.cpp | 8 +- src/hardware/vga_attr.cpp | 96 +- src/hardware/vga_crtc.cpp | 10 +- src/hardware/vga_dac.cpp | 23 +- src/hardware/vga_draw.cpp | 139 +- src/hardware/vga_gfx.cpp | 8 +- src/hardware/vga_memory.cpp | 46 +- src/hardware/vga_misc.cpp | 52 +- src/hardware/vga_other.cpp | 259 +- src/hardware/vga_paradise.cpp | 8 +- src/hardware/vga_s3.cpp | 8 +- src/hardware/vga_seq.cpp | 8 +- src/hardware/vga_tseng.cpp | 30 +- src/hardware/vga_xga.cpp | 14 +- src/ints/bios.cpp | 83 +- src/ints/bios_disk.cpp | 119 +- src/ints/bios_keyboard.cpp | 32 +- src/ints/ems.cpp | 62 +- src/ints/int10.cpp | 67 +- src/ints/int10.h | 21 +- src/ints/int10_char.cpp | 273 +- src/ints/int10_memory.cpp | 306 +- src/ints/int10_misc.cpp | 34 +- src/ints/int10_modes.cpp | 214 +- src/ints/int10_pal.cpp | 48 +- src/ints/int10_put_pixel.cpp | 148 +- src/ints/int10_vesa.cpp | 64 +- src/ints/int10_video_state.cpp | 8 +- src/ints/int10_vptable.cpp | 8 +- src/ints/mouse.cpp | 171 +- src/ints/xms.cpp | 8 +- src/ints/xms.h | 8 +- src/libs/zmbv/drvproc.cpp | 10 +- src/libs/zmbv/makedll.mk | 17 + src/libs/zmbv/resource.h | 15 +- src/libs/zmbv/resource.rc | 40 + src/libs/zmbv/zmbv.cpp | 8 +- src/libs/zmbv/zmbv.h | 8 +- src/libs/zmbv/zmbv_mingw.def | 2 + src/libs/zmbv/zmbv_vfw.cpp | 55 +- src/libs/zmbv/zmbv_vfw.rc | 2 +- src/misc/cross.cpp | 114 +- src/misc/messages.cpp | 10 +- src/misc/programs.cpp | 53 +- src/misc/setup.cpp | 317 +- src/misc/support.cpp | 23 +- src/platform/visualc/config.h | 54 +- src/platform/wii/config.h | 10 +- src/shell/shell.cpp | 246 +- src/shell/shell_batch.cpp | 73 +- src/shell/shell_cmds.cpp | 268 +- src/shell/shell_misc.cpp | 70 +- src/winres.rc | 6 +- visualc_net/dosbox.vcproj | 44 +- 278 files changed, 22563 insertions(+), 8449 deletions(-) mode change 100644 => 100755 autogen.sh rename configure.in => configure.ac (72%) create mode 100644 include/midi.h delete mode 100644 include/modules.h create mode 100644 src/cpu/core_dyn_x86/risc_x64.h delete mode 100644 src/cpu/core_dynrec/risc_armv4le-s3.h create mode 100644 src/cpu/core_dynrec/risc_armv8le.h create mode 100644 src/dos/drive_overlay.cpp create mode 100644 src/hardware/mame/Makefile.am create mode 100644 src/hardware/mame/emu.h create mode 100644 src/hardware/mame/fmopl.cpp create mode 100644 src/hardware/mame/fmopl.h create mode 100644 src/hardware/mame/saa1099.cpp create mode 100644 src/hardware/mame/saa1099.h create mode 100644 src/hardware/mame/sn76496.cpp create mode 100644 src/hardware/mame/sn76496.h create mode 100644 src/hardware/mame/ymdeltat.cpp create mode 100644 src/hardware/mame/ymdeltat.h create mode 100644 src/hardware/mame/ymf262.cpp create mode 100644 src/hardware/mame/ymf262.h create mode 100644 src/libs/zmbv/makedll.mk create mode 100644 src/libs/zmbv/resource.rc create mode 100644 src/libs/zmbv/zmbv_mingw.def diff --git a/COPYING b/COPYING index d60c31a..6b29640 100644 --- a/COPYING +++ b/COPYING @@ -1,8 +1,8 @@ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA Also add information on how to contact you by electronic and paper mail. diff --git a/ChangeLog b/ChangeLog index 3e912f8..c99e11b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,89 @@ +0.74-3 + - Implement basic file access control for files available from within + the emulation, so that programs running inside DOSBox can't access + /proc (e.g. /proc/self/mem). (CVE-2019-12594) + - Unify unmounting code and add some protections against out of bound access + when DOS_DRIVES is not 26. + - Use correct type for return value of strlen. + - Change scan3x to a bit brighter. + - Fix unitialized access to some isoDrive fields and improve stability + when switching CD images. + - Small fix to hq3x. + - Fix 256 colour mode encoding in zmbv.dll. + - Some small aliassing fix. + - Change SET to check the size of the MCB when adding variables. Fixes + hangs with Norton Commander. + - Check buffer length before doing tab completion. + - Correct buffer overflow with very long lines in bat files. (CVE-2019-7165) + - Correct the boundary check for the internal programs array. + - Increase stability in max cycles mode by increasing the lower bound. + - Fix command prompt in windows 3.11 with dynrec core. + - Fix Win64 dynrec core. + - Always clear buffers before drawing in OpenGL mode. Fixes artifacts with + drivers that have more than 2 buffers and overlays. + - Fix red border that appeared when scaling the image in OpenGL + mode with Nvidia on Linux and Mac. + - Change default output to opengl on Mac, as surface is very slow there in 64 bit. + - Add workaround for the mouse unlocking problem with X.org 1.20.1. + - Fix table access when USE_FULL_TLB is undefined (non-default configuration). + - Several fixes to prevent compilation errors. + - Update Visual studio file to fix missing files from 0.74-2. + +0.74-2 + - Rewrite auto/max cycles algorithm to work better with windows 7, other + OSes might benefit as well. + - Update 64bit recompiler to work on OSX and Linux. + - Several improvements to make the recompilers work with newer compilers and + add some workarounds about clang confusing itself. + - Fix several variables being the wrong size in the recompiler. + - Support absolute 64 bit addressing. (DRC:64 bit error messages) + - Zero extend data in dynrec core for LLVM compilation/ + - Reduce overhead of the Mac version with a lot. Results in a speed increase. + - Replace NV_PixelDataRange with the more common ARB_PixelBufferObject + extension. Should help with output=opengl. + - Reuse graphics window if possible instead of always creating a new one! + - Add patches to work better when called by WINE: + - support WINE style namemangling. + - allow Z:\ to be moved to a different drive. + - Try to fix stuttering audio with opengl output on Linux. (or at least + improve it. Linux users might need to increase the prebuffer option a bit) + - Fix automake and autoconf problems. + - Fix problems related to packed struct layouts. + - Fix compilation on gcc 4.4 and gcc 4.6.1. + - Fix compilation with -DPIC. + - Hopefully fix crash on shutdown, when unknown condition is encountered. + - Fix -lto with gcc. + - Fix clang compliation with asm fpu core. + - Fix mapper crash on startup and when changing the mapper key. + - Fix compilation in VS2015. + - Fix compilation on mingw64. + - Fix compilation on Frisbee and newer clang. + - Fix compilation machines that have X11 libraries installed, but use a SDL + without X11. + - Fix a few problems relating to video capturing: + - Writing out the index too often. (slowdown with longer captures) + - Not being aware of when only the refresh rate changed. + - Fix call order when bitshifting the return order (VS C /O2 builds). + - Fix sign-extension error in S3 draw funcion, i.e. win3.1 analog clock. + - Fix expanddot not caring about the size of the inputbuffer. + - Some fixes for the debugger related to starting a program through debug.com. + - Bring OS/2 port code up to date. + - Reduce the amount of warning when compiling with quite a bit. + - Allow 15/26/32 bits input to the opengl output. + - Add cmd-q as exit key for macs. + - Fix icon and titlebar on very old setups. + - Fix off by one display size calculation errors with very large displays. + - Fix out of bounds access the unused byte in the 32 bit colour value of the + hardware mouse cursor pixel was affected. + - Fix depreciated warnings on Mac, allow soundfont loading for coreaudio. + - Keep repeating a pressed key if another key was unpressed. + - Fix crash on OS X related to putting junk in the titlebar. + - Extend fullresolution=0x0 to work on Linux and Mac. + - Change gameblaster and tandy sound core to latest mame version. + - Improve numlock and capslock support on Linux and Macs. Still not perfect + on Windows. + - Added even more code to workaround Windows behaving weirdly with alt-tab. + 0.74 - Several small game specific fixes/hacks/support. (Offensive, Roadhog, GTA installer, Kingdom O' Magic soundcard detection, diff --git a/INSTALL b/INSTALL index f067f4b..7d0f8db 100644 --- a/INSTALL +++ b/INSTALL @@ -1,90 +1,97 @@ -Things needed for compilation. +Things needed for compilation: -SDL - The Simple DirectMedia Library available at http://www.libsdl.org +SDL + The Simple DirectMedia Library available at https://www.libsdl.org/ The dll distributed with the windows version of DOSBox is slightly modified. You can find the changes in the sourcepackage of DOSBox (src/platform/sdl-win32.diff). If you want the patched sourcetree send us an email. (see README) - Licensed under LGPL Note that only version 1.2 and its subversions (1.2.8, 1.2.13 etc.) are currently supported. + License: LGPLv2+ Curses (optional) If you want to enable the debugger you need a curses library. ncurses should be installed on just about every unix distro. - For win32 get pdcurses at http://pdcurses.sourceforge.net - License: Open source + For win32 get pdcurses at https://pdcurses.org/ + License: Public Domain Libpng (optional) Needed for the screenshots. For win32 get libpng from http://gnuwin32.sourceforge.net/packages.html See http://www.libpng.org/pub/png/ for more details. - License: Open Source + License: zlib/libpng Zlib (optional) Needed by libpng. For win32 get libz (rename to zlib) from http://gnuwin32.sourceforge.net/packages.html - See http://www.zlib.net for more details. - License: Open Source + See https://www.zlib.net/ for more details. + License: zlib SDL_Net (optional) - For modem/ipx support. Get it from http://www.libsdl.org/projects/SDL_net/ - Licensed under LGPL + For modem/ipx support. + Get it from https://www.libsdl.org/projects/SDL_net/release-1.2.html + License: LGPLv2+ -SDL_Sound - For compressed audio on diskimages. (optional) +SDL_Sound (optional) + For compressed audio on diskimages (cue sheets) support. This is for cue/bin cdrom images with compressed (mp3/ogg) audio tracks. Get it from http://icculus.org/SDL_sound - Licenced under LGPL + Licence: LGPLv2+ -ALSA_Headers - (optional) +ALSA_Headers (optional) for Alsa support under linux. Part of the linux kernel sources - Licensed under LGPL + License: LGPLv2+ -If you want compile from developer sources (SVN) under a unix system, you'll also need -automake (>=1.6), autoconf(>=2.50). Should be available at http://www.gnu.org +If you want compile from developer sources (SVN) under a unix system, you'll +also need automake (>=1.6) and autoconf(>=2.50). +Should be available at https://www.gnu.org/software/ For building on unix systems. -If you are building from developer sources run ./autogen.sh first before doing the following. +If you are building from developer sources run ./autogen.sh first before doing +the following: -1. ./configure -2. make + $ ./configure + $ make -In step 1 you could add the following switches: ---enable-debug - enables the internal debugger. --enable-debug=heavy enables even more - debug options. DOSBox should then be run from a xterm and when the sdl- - window is active press alt-pause to enter the debugger. +You can also run "./configure --help" for extra features to enable/disable. +Some of them are: ---enable-core-inline - enables some memory increasing inlines. This greatly increases - compiletime for maybe a increase in speed. +--enable-debug + enables the internal debugger. --enable-debug=heavy enables even more + debug options. To use the debugger, DOSBox should be run from an xterm + and when the sdl-window is active press alt-pause to enter the + debugger. + +--disable-core-inline + disables some memory increasing inlines. This speeds up compilation, + but may result in a slower dosbox. --disable-fpu - disables the emulated fpu. Although the fpu emulation code isn't - finished and isn't entirely accurate it's advised to leave it on. + disables the emulated fpu. Although the fpu emulation code isn't + finished and isn't entirely accurate, it's advised to leave it on. --disable-fpu-x86 - disables the assembly fpu core. Although relatively new the x86 fpu - core has more accuracy then the regular fpu core. +--disable-fpu-x64 + disables the assembly fpu core. Although relatively new, the x86/x64 + fpu core has more accuracy than the regular fpu core. --disable-dynamic-x86 - disables the dynamic x86 specific cpu core. Although it might be - be a bit unstable, it can greatly improve the speed of dosbox on x86 - hosts. - Please note that this option on x86 will result in a different - dynamic/recompiling cpu core being compiled then the default. + disables the dynamic x86/x64 specific cpu core. Although it might be + a bit unstable, it can greatly improve the speed of dosbox on x86 and + x64 hosts. + Please note that this option on x86/x64 will result in a different + dynamic/recompiling cpu core being compiled than the default. For more information see the option --disable-dynrec --disable-dynrec - disables the recompiling cpu core. Currently x86 and x86_64 only. - You can activate this core on x86 by disabling the dynamic-x86 core. + disables the recompiling cpu core. Currently x86/x64 and arm only. + You can activate this core on x86/x64 by disabling the dynamic-x86 + core. --disable-dynamic-core - disables all dynamic cores. (same effect as - --disable-dynamic-x86 --disable-dynrec) + disables all dynamic cores (same effect as --disable-dynamic-x86 + or --disable-dynrec). --disable-opengl disables OpenGL-support (output mode that can be selected in the diff --git a/Makefile b/Makefile index 482dbbb..8577b45 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,8 @@ include $(DEVKITPPC)/wii_rules TARGET := $(notdir $(CURDIR)) BUILD := build SOURCES := src src/cpu src/debug src/dos src/fpu src/gui \ - src/hardware src/hardware/serialport src/ints src/libs \ + src/hardware src/hardware/mame \ + src/hardware/serialport src/ints src/libs \ src/misc src/platform/wii src/platform/wii/fonts \ src/platform/wii/images src/platform/wii/libwiigui \ src/platform/wii/sounds src/shell diff --git a/NEWS b/NEWS index adc6875..c603070 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,29 @@ +0.74-3 +A security release for DOSBox 0.74: +- Fixed that a very long line inside a bat file would overflow the parsing + buffer (CVE-2019-7165 by Alexandre Bartel) +- Added a basic permission system so that a program running inside DOSBox + can't access the contents of /proc (e.g. /proc/self/mem) when / or /proc + were (to be) mounted (CVE-2019-12594 by Alexandre Bartel) +- Several other fixes for out of bounds access and buffer overflows. +- Some fixes to the OpenGL rendering. + +It's recommended to use config -securemode when dealing with untrusted +files. +See the Changelog for all the changes. + +0.74-2 +A maintenance release for DOSBox 0.74, which solves the following problems: + +- Windows: Fix auto/max cycles algorithm on Windows 7, which helps with + stuttering audio. +- Mac OS X: Bring a 64 bit version and improve performance. +- Linux: Fix the 64bit dynrec cpu core and a lot of compilation problems. + Add patches for the WINE Team. + +The game compatibility should be identical to 0.74. +See the Changelog for a full list of changes. + 0.74 - Several small game specific fixes/hacks/support. (Offensive, Roadhog, GTA installer, Kingdom O' Magic soundcard detection, diff --git a/README b/README index 635fc9b..7a8c749 100644 --- a/README +++ b/README @@ -22,6 +22,7 @@ A port of DOSBox to the Wii using SDL Wii. [current master - 2021] * Updated to latest libogc and devkitPPC * Convert CRLF line terminators to unix line terminators (like at original SVN repo) +* Sync to DOSBox SVN r4301 [1.7 - June 30, 2012] @@ -112,7 +113,7 @@ you want to play on the PC version of DOSBox first to discover the optimum settings. ------------------------------------------------------------------------------- -DOSBox v0.74 Manual (always use the latest version from www.dosbox.com) +DOSBox v0.74-2 Manual (always use the latest version from www.dosbox.com) @@ -161,7 +162,7 @@ It is essential that you get familiar with the idea of mounting, DOSBox does not automatically make any drive (or a part of it) accessible to the emulation. See the FAQ entry "How to start?" as well as the description of the MOUNT command (Section 4: "Internal Programs"). If you have your game on a cdrom you may try -this guide: http://vogons.zetafleet.com/viewtopic.php?t=8933 +this guide: https://www.vogons.org/viewtopic.php?t=8933 @@ -172,6 +173,7 @@ this guide: http://vogons.zetafleet.com/viewtopic.php?t=8933 START: How to start? AUTOMATION: Do I always have to type these "mount" commands? FULLSCREEN: How do I change to fullscreen? +FULLSCREEN: My fullscreen is too large. CD-ROM: My CD-ROM doesn't work. CD-ROM: The game/application can't find its CD-ROM. MOUSE: The mouse doesn't work. @@ -217,6 +219,26 @@ FULLSCREEN: How do I change to fullscreen? mode: Press alt-enter again. +FULLSCREEN: My fullscreen is too large. + This is can be a problem on Windows 10, if you have display scaling + set to a value above 100%. Windows in that case will resize the screen + on top of dosbox resizing the screen, which can happen for the output: + ddraw, opengl, openglnb, overlay. You can disable this Windows behaviour + by enabling a specific compatibility setting: + + - Right-click the DOSBox icon and select "Properties". + - Go to the "Compatibility" tab. + - Click on "Change high DPI settings". + - Tick "Override high DPI scaling behaviour" and set it to "Application". + - Apply the changes by clicking on "OK". + + Unfortunately, this compatibility option causes some side effects in + windowed mode, and in this case you will need to change the resolution + in the config/Options file for windowresolution (e.g. 1024x768). + + Alternatively, you can disable the display scaling and or use a lower + fullresolution value. + CD-ROM: My CD-ROM doesn't work. To mount your CD-ROM in DOSBox you have to specify some additional options when mounting the CD-ROM. @@ -370,7 +392,7 @@ KEYBOARD: Right Shift and "\" doesn't work in DOSBox. (Windows only) dosbox.exe check whether keyboard started to work properly. As windib is slower it is best to use one of the two solutions provided here: - http://vogons.zetafleet.com/viewtopic.php?t=24072 + https://www.vogons.org/viewtopic.php?t=24072 KEYBOARD: The keyboard lags. @@ -422,9 +444,9 @@ OPTIONS: I would like to change DOSBox's options. HELP: Great Manual, but I still don't get it. For more questions read the rest of this Manual. You may also look at: - guides located at http://vogons.zetafleet.com/viewforum.php?f=39 - the wiki of DOSBox http://www.dosbox.com/wiki/ - the site/forum: http://www.dosbox.com + guides located at https://www.vogons.org/viewforum.php?f=53 + the wiki of DOSBox https://www.dosbox.com/wiki/ + the site/forum: https://www.dosbox.com @@ -1132,7 +1154,7 @@ KEYB [keyboardlayoutcode [codepage [codepagefile]]] examples are PL214 (Polish typists) or PL457 (Polish programmers). It specifies the keyboard layout to be used. The list of all layouts built into DOSBox is here: - http://vogons.zetafleet.com/viewtopic.php?t=21824 + https://www.vogons.org/viewtopic.php?t=21824 [codepage] is the number of the codepage to be used. The keyboard layout has to provide support for the specified codepage, otherwise the layout @@ -1211,7 +1233,7 @@ if the key doesn't work i.e. fn-cmd-ctrl-F1, but some keys may still need remapping (in Linux too). Saved/recorded files can be found in: - (Windows) "Start/WinLogo Menu"->"All Programs"->DOSBox-0.74->Extras + (Windows) "Start/WinLogo Menu"->"All Programs"->"DOSBox-0.74-2"->Extras (Linux) ~/.dosbox/capture (Mac OS X) "~/Library/Preferences/capture" This can be changed in the DOSBox configuration file. @@ -1366,7 +1388,7 @@ Layout switching in this case just the layout identifier needs to be specified (like keyboardlayout=PL214 in the DOSBox configuration file, or using "keyb PL214" at the DOSBox command prompt). The list of all layouts built into DOSBox is - here: http://vogons.zetafleet.com/viewtopic.php?t=21824 + here: https://www.vogons.org/viewtopic.php?t=21824 Some keyboard layouts (for example layout GK319 codepage 869 and layout RU441 codepage 808) have support for dual layouts that can be accessed by pressing @@ -1601,9 +1623,9 @@ To display the DOSBox status window: The configuration file is automatically created the first time you run DOSBox. The file can be found in: - (Windows) "Start/WinLogo Menu"->"All Programs"->DOSBox-0.74->Options - (Linux) ~/.dosbox/dosbox-0.74.conf - (Mac OS X) "~/Library/Preferences/DOSBox 0.74 Preferences" + (Windows) "Start/WinLogo Menu"->"All Programs"->"DOSBox-0.74-2"->Options + (Linux) ~/.dosbox/dosbox-0.74-2.conf + (Mac OS X) "~/Library/Preferences/DOSBox 0.74-2 Preferences" The file is divided into several sections. Each section starts with a [section name] line. The settings are the property=value lines where value can be altered to customize DOSBox. @@ -1664,7 +1686,7 @@ See the THANKS file. ============ See the site: -http://www.dosbox.com +https://www.dosbox.com for an email address (The Crew-page). diff --git a/VERSION b/VERSION index 3ea25f5..a6e956f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.74 +0.74-2 diff --git a/acinclude.m4 b/acinclude.m4 index 29474ec..08653d8 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -144,7 +144,7 @@ int main (int argc, char *argv[]) echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], [ echo "*** The test program failed to compile or link. See the file config.log for the" - echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** exact error that occurred. This usually means SDL was incorrectly installed" echo "*** or that you have moved SDL since it was installed. In the latter case, you" echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) CFLAGS="$ac_save_CFLAGS" @@ -305,7 +305,7 @@ AC_SUBST(ALSA_LIBS) AH_TOP([ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -317,9 +317,9 @@ AH_TOP([ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ ]) @@ -370,29 +370,41 @@ typedef double Real64; #if SIZEOF_UNSIGNED_INT == 4 typedef unsigned int Bit32u; typedef signed int Bit32s; +#define sBit32t #elif SIZEOF_UNSIGNED_LONG == 4 typedef unsigned long Bit32u; typedef signed long Bit32s; +#define sBit32t "l" #else # error "can't find sizeof(type) of 4 bytes!" #endif +#define sBit32fs(a) sBit32t #a #if SIZEOF_UNSIGNED_LONG == 8 typedef unsigned long Bit64u; typedef signed long Bit64s; +#define sBit64t "l" #elif SIZEOF_UNSIGNED_LONG_LONG == 8 typedef unsigned long long Bit64u; typedef signed long long Bit64s; +#define sBit64t "ll" #else # error "can't find data type of 8 bytes" #endif +#define sBit64fs(a) sBit64t #a #if SIZEOF_INT_P == 4 - typedef Bit32u Bitu; - typedef Bit32s Bits; -#else - typedef Bit64u Bitu; - typedef Bit64s Bits; + +typedef Bit32u Bitu; +typedef Bit32s Bits; +#define sBitfs sBit32fs + +#else //SIZEOF_INT_P + +typedef Bit64u Bitu; +typedef Bit64s Bits; +#define sBitfs sBit64fs + #endif ]) diff --git a/autogen.sh b/autogen.sh old mode 100644 new mode 100755 diff --git a/configure.in b/configure.ac similarity index 72% rename from configure.in rename to configure.ac index 54b5356..e993b75 100644 --- a/configure.in +++ b/configure.ac @@ -9,7 +9,7 @@ AC_CANONICAL_BUILD dnl Setup for automake AM_INIT_AUTOMAKE -AM_CONFIG_HEADER(config.h) +AC_CONFIG_HEADER(config.h) dnl Checks for programs. AC_PROG_MAKE_SET @@ -24,7 +24,6 @@ dnl perharps join this with the other target depended checks. move them upwards if test x$host = xi386-pc-os2-emx ; then CXXFLAGS="$CXXFLAGS -Zmt" LDFLAGS="$LDFLAGS -Zomf -Zmt" - LIBS="$LIBS -los2me" fi dnl Check for SDL @@ -129,11 +128,17 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ ]])], [AC_MSG_RESULT(yes)], [AC_DEFINE([DB_HAVE_NO_POWF],[1],[libm doesn't include powf])]) LIBS=$LIBS_BACKUP +dnl Look for clock_gettime, a DB_HAVE_CLOCK_GETTIME is set when present +AH_TEMPLATE([DB_HAVE_CLOCK_GETTIME],[Determines if the function clock_gettime is available.]) +AC_SEARCH_LIBS([clock_gettime], [rt] , [found_clock_gettime=yes], [found_clock_gettime=no]) +if test x$found_clock_gettime = xyes; then + AC_DEFINE(DB_HAVE_CLOCK_GETTIME) +fi dnl Checks for libraries. #Check if the compiler support attributes -AH_TEMPLATE([C_HAS_ATTRIBUTE],[Determines if the compilers supports attributes for structures.]) +AH_TEMPLATE([C_HAS_ATTRIBUTE],[Determines if the compiler supports attributes for structures.]) AC_MSG_CHECKING(if compiler allows __attribute__) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ typedef struct { } __attribute__((packed)) junk;]], @@ -144,12 +149,12 @@ typedef struct { } __attribute__((packed)) junk;]], OLDCFLAGS="$CFLAGS" CFLAGS="-Werror" -AH_TEMPLATE([C_ATTRIBUTE_ALWAYS_INLINE],[Determines if the compilers supports always_inline attribute.]) +AH_TEMPLATE([C_ATTRIBUTE_ALWAYS_INLINE],[Determines if the compiler supports always_inline attribute.]) AC_MSG_CHECKING(if compiler allows __attribute__((always_inline)) ) -AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void __attribute__((always_inline)) test(){} +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ inline void __attribute__((always_inline)) test(){} ])],[ AC_MSG_RESULT(yes);AC_DEFINE(C_ATTRIBUTE_ALWAYS_INLINE)],AC_MSG_RESULT(no)) -AH_TEMPLATE([C_ATTRIBUTE_FASTCALL],[Determines if the compilers supports fastcall attribute.]) +AH_TEMPLATE([C_ATTRIBUTE_FASTCALL],[Determines if the compiler supports fastcall attribute.]) AC_MSG_CHECKING(if compiler allows __attribute__((fastcall)) ) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void __attribute__((fastcall)) test(){} ])],[ AC_MSG_RESULT(yes);AC_DEFINE(C_ATTRIBUTE_FASTCALL)],AC_MSG_RESULT(no)) @@ -169,6 +174,48 @@ int x=10;if( __builtin_expect ((x==1),0) ) ; #switch language back AC_LANG_POP(C++) +dnl test if compiler supports -mno-ms-bitfields as it is bugged +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 +BACKUP_CFLAGS="$CFLAGS" +CFLAGS="-mno-ms-bitfields" +AC_MSG_CHECKING(if compiler supports -mno-ms-bitfields) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +void blah(){ +; +} +])],[ +AC_MSG_RESULT([yes]) +CXXFLAGS="$CXXFLAGS -mno-ms-bitfields" +],[AC_MSG_RESULT([no])]) +CFLAGS="$BACKUP_CFLAGS" + +dnl When on macOS, enable support for Apple's Core MIDI and/or Core Audio if our compiler can #include their headers +case "$host" in + *-*-darwin*) + AC_MSG_CHECKING(if compiler supports Apple's Core MIDI headers) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + int blah() { return 0; } + ]])], [AC_MSG_RESULT(yes);LIBS="$LIBS -framework CoreMIDI";AC_DEFINE([C_SUPPORTS_COREMIDI], [], + [Compiler supports Core MIDI headers])], + AC_MSG_RESULT(no);AC_MSG_WARN([Compiler can't compile Apple headers. CoreMIDI functionality disabled. Please use the Apple compiler!])) + + AC_MSG_CHECKING(if compiler supports Apple's Core Audio headers) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + #include + int blah() { return 0; } + ]])], [AC_MSG_RESULT(yes);LIBS="$LIBS -framework AudioUnit -framework AudioToolbox";AC_DEFINE([C_SUPPORTS_COREAUDIO], [], + [Compiler supports Core Audio headers])], + AC_MSG_RESULT(no);AC_MSG_WARN([Compiler can't compile Apple headers. CoreAudio functionality disabled. Please use the Apple compiler!])) + ;; + + *) + AC_MSG_CHECKING(if compiler supports Apple's MIDI headers) + AC_MSG_RESULT([no, not on Apple]) + ;; +esac + dnl enable disable alsa and pass it's cflags to CXXFLAGS AC_ARG_ENABLE(alsa-midi, AC_HELP_STRING([--enable-alsa-midi],[compile with alsa midi support (default yes)]), @@ -196,14 +243,14 @@ AC_ARG_ENABLE(debug,AC_HELP_STRING([--enable-debug],[Enable debug mode]),[ if test x$enable_debug = xno; then AC_MSG_RESULT([Debugger not enabled]) - elif test x$have_curses_lib = xyes -a x$have_curses_h = xyes ; then - LIBS="$LIBS -lcurses" + elif test x$have_ncurses_lib = xyes -a x$have_curses_h = xyes ; then + LIBS="$LIBS -lncurses" AC_DEFINE(C_DEBUG,1) if test x$enable_debug = xheavy ; then AC_DEFINE(C_HEAVY_DEBUG,1) fi - elif test x$have_ncurses_lib = xyes -a x$have_curses_h = xyes ; then - LIBS="$LIBS -lncurses" + elif test x$have_curses_lib = xyes -a x$have_curses_h = xyes ; then + LIBS="$LIBS -lcurses" AC_DEFINE(C_DEBUG,1) if test x$enable_debug = xheavy ; then AC_DEFINE(C_HEAVY_DEBUG,1) @@ -220,13 +267,14 @@ AC_ARG_ENABLE(debug,AC_HELP_STRING([--enable-debug],[Enable debug mode]),[ ],) AH_TEMPLATE(C_CORE_INLINE,[Define to 1 to use inlined memory functions in cpu core]) -AC_ARG_ENABLE(core-inline,AC_HELP_STRING([--enable-core-inline],[Enable inlined memory handling in CPU Core]),[ - if test x$enable_core_inline = xyes ; then - AC_MSG_RESULT([enabling inlined memory handling in CPU Core]) +AC_ARG_ENABLE(core-inline,AC_HELP_STRING([--disable-core-inline],[Disable inlined memory handling in CPU Core]),,enable_core_inline=yes) +AC_MSG_CHECKING(whether memory handling in the CPU Core will be inlined) +if test x$enable_core_inline = xyes ; then + AC_MSG_RESULT(yes) AC_DEFINE(C_CORE_INLINE,1) - fi -],) - +else + AC_MSG_RESULT(no) +fi dnl The target cpu checks for dynamic cores AH_TEMPLATE(C_TARGETCPU,[The type of cpu this target has]) @@ -256,6 +304,24 @@ case "$host_cpu" in c_targetcpu="m68k" c_unalignedmemory=yes ;; + armv7l) + AC_DEFINE(C_TARGETCPU,ARMV7LE) + AC_MSG_RESULT(ARMv7 Little Endian) + c_targetcpu="arm" + c_unalignedmemory=yes + ;; + armv6l) + AC_DEFINE(C_TARGETCPU,ARMV4LE) + AC_MSG_RESULT(ARMv6 Little Endian) + c_targetcpu="arm" + dnl c_unalignedmemory=yes + ;; + aarch64) + AC_DEFINE(C_TARGETCPU,ARMV8LE) + AC_MSG_RESULT(ARMv8 Little Endian 64-bit) + c_targetcpu="arm" + c_unalignedmemory=yes + ;; *) AC_DEFINE(C_TARGETCPU,UNKNOWN) AC_MSG_RESULT(unknown) @@ -263,15 +329,45 @@ case "$host_cpu" in ;; esac +dnl check for size of pointer being 4 +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +#if SIZEOF_INT_P != 4 +#error size intp is not 4, this not something to worry about +#endif +void blah() { +; +} +])],[c_sizep=4],[c_sizep=0]) + + +dnl automake 1.14 and upwards rewrite the host to have always 64 bit unless i386 as host is passed +dnl this can make building a 32 bit executable a bit tricky, as dosbox relies on the host to select the +dnl dynamic/dynrec core +AC_MSG_CHECKING([whether Apple user wants to override the build process to produce a 32 bit binary]) +case "$host" in + *-*-darwin*) + if test x$c_targetcpu = xx86_64 -a x$c_sizep = x4 ; then + AC_MSG_RESULT(yes) + AC_DEFINE(C_TARGETCPU,X86) + c_targetcpu="x86" + else + AC_MSG_RESULT(no) + fi + ;; + *) + AC_MSG_RESULT([no, not on Apple]) + ;; +esac + AC_ARG_ENABLE(dynamic-core,AC_HELP_STRING([--disable-dynamic-core],[Disable all dynamic cores]),,enable_dynamic_core=yes) -AH_TEMPLATE(C_DYNAMIC_X86,[Define to 1 to use x86 dynamic cpu core]) -AC_ARG_ENABLE(dynamic-x86,AC_HELP_STRING([--disable-dynamic-x86],[Disable x86 dynamic cpu core]),,enable_dynamic_x86=yes) +AH_TEMPLATE(C_DYNAMIC_X86,[Define to 1 to use x86/x64 dynamic cpu core]) +AC_ARG_ENABLE(dynamic-x86,AC_HELP_STRING([--disable-dynamic-x86],[Disable x86/x64 dynamic cpu core]),,enable_dynamic_x86=yes) AC_MSG_CHECKING(whether x86 dynamic cpu core will be enabled) if test x$enable_dynamic_x86 = xno -o x$enable_dynamic_core = xno; then AC_MSG_RESULT(no) else - if test x$c_targetcpu = xx86 ; then + if test x$c_targetcpu = xx86 -o x$c_targetcpu = xx86_64; then AC_DEFINE(C_DYNAMIC_X86,1) AC_MSG_RESULT(yes) else @@ -286,7 +382,7 @@ if test x$enable_dynrec = xno -o x$enable_dynamic_core = xno; then AC_MSG_RESULT(no) else dnl x86 only enable it if dynamic-x86 is disabled. - if test x$c_targetcpu = xx86 ; then + if test x$c_targetcpu = xx86 -o x$c_targetcpu = xx86_64; then if test x$enable_dynamic_x86 = xno ; then AC_DEFINE(C_DYNREC,1) AC_MSG_RESULT(yes) @@ -294,10 +390,10 @@ dnl x86 only enable it if dynamic-x86 is disabled. AC_MSG_RESULT([no, using dynamic-x86]) fi else - if test x$c_targetcpu = xx86_64 ; then - AC_DEFINE(C_DYNREC,1) - AC_MSG_RESULT(yes) - else + if test x$c_targetcpu = xarm ; then + AC_DEFINE(C_DYNREC,1) + AC_MSG_RESULT(yes) + else AC_MSG_RESULT(no) fi fi @@ -313,14 +409,15 @@ else AC_MSG_RESULT(no) fi -AH_TEMPLATE(C_FPU_X86,[Define to 1 to use a x86 assembly fpu core]) +AH_TEMPLATE(C_FPU_X86,[Define to 1 to use a x86/x64 assembly fpu core]) AC_ARG_ENABLE(fpu-x86,AC_HELP_STRING([--disable-fpu-x86],[Disable x86 assembly fpu core]),,enable_fpu_x86=yes) -AC_MSG_CHECKING(whether x86 assembly fpu core will be enabled) -if test x$enable_fpu_x86 = xno ; then +AC_ARG_ENABLE(fpu-x64,AC_HELP_STRING([--disable-fpu-x64],[Disable x64 assembly fpu core]),,enable_fpu_x64=yes) +AC_MSG_CHECKING(whether the x86/x64 assembly fpu core will be enabled) +if test x$enable_fpu_x86 = xno -o x$enable_fpu_x64 = xno; then AC_MSG_RESULT(no) else if test x$enable_fpu = xyes; then - if test x$c_targetcpu = xx86 ; then + if test x$c_targetcpu = xx86 -o x$c_targetcpu = xx86_64; then AC_DEFINE(C_FPU_X86,1) AC_MSG_RESULT(yes) else @@ -342,13 +439,20 @@ else fi AH_TEMPLATE(C_SSHOT,[Define to 1 to enable screenshots, requires libpng]) +AC_ARG_ENABLE(screenshots,AC_HELP_STRING([--disable-screenshots],[Disable screenshots and movie recording]),,enable_screenshots=yes) AC_CHECK_HEADER(png.h,have_png_h=yes,) AC_CHECK_LIB(png, png_get_io_ptr, have_png_lib=yes, ,-lz) -if test x$have_png_lib = xyes -a x$have_png_h = xyes ; then - LIBS="$LIBS -lpng -lz" - AC_DEFINE(C_SSHOT,1) +AC_MSG_CHECKING([whether screenshots will be enabled]) +if test x$enable_screenshots = xyes; then + if test x$have_png_lib = xyes -a x$have_png_h = xyes ; then + LIBS="$LIBS -lpng -lz" + AC_DEFINE(C_SSHOT,1) + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no, can't find libpng.]) + fi else - AC_MSG_WARN([Can't find libpng, screenshot support disabled]) + AC_MSG_RESULT([no]) fi AH_TEMPLATE(C_MODEM,[Define to 1 to enable internal modem support, requires SDL_net]) @@ -413,6 +517,28 @@ case "$host" in fi ;; esac +else + AC_MSG_RESULT(no) +fi + +AH_TEMPLATE(C_DDRAW,[Define to 1 to enable output=ddraw (Win32)]) +AC_ARG_ENABLE(ddraw,AC_HELP_STRING([--disable-ddraw],[Disable ddraw support]),,enable_ddraw=yes) +AC_CHECK_HEADER(ddraw.h, have_ddraw_h=yes , have_ddraw_h=no , ) +AC_MSG_CHECKING(whether ddraw display output will be enabled) +if test x$enable_ddraw = xyes; then +case "$host" in + *-*-cygwin* | *-*-mingw32*) + if test x$have_ddraw_h = xyes ; then + AC_MSG_RESULT(yes) + AC_DEFINE(C_DDRAW,1) + else + AC_MSG_RESULT(no) + fi + ;; + *) + AC_MSG_RESULT(no) + ;; +esac fi AH_TEMPLATE(C_SDL_SOUND,[Define to 1 to enable SDL_sound support]) @@ -436,6 +562,9 @@ AC_CHECK_HEADER([sys/mman.h], [ AC_CHECK_FUNC([mprotect],[AC_DEFINE(C_HAVE_MPROTECT,1)]) ]) +dnl Check for realpath. Used on Linux +AC_CHECK_FUNCS([realpath]) + dnl Setpriority AH_TEMPLATE(C_SET_PRIORITY,[Define to 1 if you have setpriority support]) AC_MSG_CHECKING(for setpriority support) @@ -451,7 +580,6 @@ dnl Some target detection and actions for them case "$host" in *-*-cygwin* | *-*-mingw32*) LIBS="$LIBS -lwinmm" - AC_CHECK_HEADERS(ddraw.h) AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2 only).]) if test x$have_sdl_net_lib = xyes -a x$have_sdl_net_h = xyes ; then LIBS="$LIBS -lws2_32" @@ -463,7 +591,6 @@ case "$host" in dnl to do more to distinguish them. dnl For now I am lazy and do not add proper detection code. AC_DEFINE(MACOSX, 1, [Compiling on Mac OS X]) - LIBS="$LIBS -framework CoreMIDI -framework AudioUnit -framework AudioToolbox" AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2).]) ;; *-*-linux*) @@ -512,6 +639,7 @@ src/dos/Makefile src/fpu/Makefile src/gui/Makefile src/hardware/Makefile +src/hardware/mame/Makefile src/hardware/serialport/Makefile src/ints/Makefile src/libs/Makefile diff --git a/docs/README.video b/docs/README.video index 1d15b4f..2034808 100644 --- a/docs/README.video +++ b/docs/README.video @@ -32,4 +32,4 @@ A: 1. Start DOSBox like this: dosbox -startmapper selected. Q: The colours are wrong and I'm using 64 bit windows -A: Look here: http://vogons.zetafleet.com/viewtopic.php?t=12133 +A: Look here: https://www.vogons.org/viewtopic.php?t=12133 diff --git a/docs/dosbox.1 b/docs/dosbox.1 index 73ce2e7..72c2459 100644 --- a/docs/dosbox.1 +++ b/docs/dosbox.1 @@ -1,5 +1,5 @@ .\" Hey, EMACS: -*- nroff -*- -.TH DOSBOX 1 "Feb 26, 2010" +.TH DOSBOX 1 "Sep 08, 2015" .\" Please adjust this date whenever revising the manpage. .SH NAME dosbox \- an x86/DOS emulator with sound/graphics @@ -14,10 +14,11 @@ dosbox \- an x86/DOS emulator with sound/graphics .BI "[\-forcescaler " scaler ] .BI "[\-conf " configfile ] .BI "[\-lang " langfile ] -.B [file] +.BI "[\-machine " machinetype ] +.BI "[\-socket " socketnumber ] .BI "[\-c " command ] .B [\-exit] -.BI "[\-machine " machinetype ] +.B [file] .LP .B dosbox \-version .LP @@ -76,6 +77,25 @@ file for the available scalers .RB "Similar to the " \-scaler " parameter, but tries to force usage of" the specified scaler even if it might not fit. .TP +.BI \-conf " configfile" +.RB "Start " dosbox " with the options specified in " +.IR configfile ". This file has a section in which you can put commands you " +wish to execute on startup. Multiple +.IR configfiles " can be present at the commandline." +.TP +.BI \-lang " langfile" +.RB "Start " dosbox " with the language specified in " +.IR langfile . +.TP +.BI \-machine " machinetype" +.RB "Setup " dosbox " to emulate a specific type of machine." +.RI "Valid choices are: " "hercules, cga, tandy, pcjr, ega, vgaonly, svga_s3(default), svga_et3000, svga_et4000, svga_paradise, vesa_nolfb, vesa_oldvbe". +The machinetype has influence on both the videocard and the available +soundcards. +.TP +.BI \-socket " socketnumber" +.RI "Passes the socket number " socketnumber " to the nullmodem emulation. See README for details." +.TP .BI \-c " command" .RI "Runs the specified " command " before running " .BR file . @@ -83,25 +103,9 @@ the specified scaler even if it might not fit. .BR \-c " though. A command can be:" an Internal Program, a DOS command or an executable on a mounted drive. .TP -.BI \-conf " configfile -.RB "Start " dosbox " with the options specified in " -.IR configfile ". This file has a section in which you can put commands you " -wish to execute on startup. Multiple -.IR configfiles " can be present at the commandline." -.TP -.BI \-lang " langfile -.RB "Start " dosbox " with the language specified in " -.IR langfile . -.TP .B "\-exit " .BR "dosbox" " will close itself when the DOS program specified by "file " ends." .TP -.BI \-machine " machinetype -.RB "Setup " dosbox " to emulate a specific type of machine." -.RI "Valid choices are: " "hercules, cga, tandy, pcjr, ega, vgaonly, svga_s3(default), svga_et3000, svga_et4000, svga_paradise, vesa_nolfb, vesa_oldvbe". -The machinetype has influence on both the videocard and the available -soundcards. -.TP .B \-version Output version information and exit. Useful for frontends. .TP diff --git a/include/Makefile.am b/include/Makefile.am index 2e7808f..570d78c 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -20,8 +20,8 @@ keyboard.h \ logging.h \ mapper.h \ mem.h \ +midi.h \ mixer.h \ -modules.h \ mouse.h \ paging.h \ pci_bus.h \ diff --git a/include/bios.h b/include/bios.h index d03eef5..2a7787a 100644 --- a/include/bios.h +++ b/include/bios.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_BIOS_H @@ -61,6 +61,7 @@ #define BIOS_VDU_CONTROL 0x465 #define BIOS_VDU_COLOR_REGISTER 0x466 /* 0x467-0x468 is reserved */ +#define BIOS_LAST_UNEXPECTED_IRQ 0x46b #define BIOS_TIMER 0x46c #define BIOS_24_HOURS_FLAG 0x470 #define BIOS_KEYBOARD_FLAGS 0x471 @@ -103,6 +104,7 @@ #define BIOS_DEFAULT_HANDLER_LOCATION (RealMake(0xf000,0xff53)) +#define BIOS_DEFAULT_INT5_LOCATION (RealMake(0xf000,0xff54)) #define BIOS_DEFAULT_IRQ0_LOCATION (RealMake(0xf000,0xfea5)) #define BIOS_DEFAULT_IRQ1_LOCATION (RealMake(0xf000,0xe987)) #define BIOS_DEFAULT_IRQ2_LOCATION (RealMake(0xf000,0xff55)) diff --git a/include/bios_disk.h b/include/bios_disk.h index ef4ad36..93b8345 100644 --- a/include/bios_disk.h +++ b/include/bios_disk.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_BIOS_DISK_H @@ -54,18 +54,20 @@ public: void Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize); Bit8u GetBiosType(void); Bit32u getSectSize(void); - imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk); + imageDisk(FILE *imgFile, const char *imgName, Bit32u imgSizeK, bool isHardDisk); ~imageDisk() { if(diskimg != NULL) { fclose(diskimg); } }; bool hardDrive; bool active; FILE *diskimg; - Bit8u diskname[512]; + char diskname[512]; Bit8u floppytype; Bit32u sector_size; Bit32u heads,cylinders,sectors; +private: Bit32u current_fpos; + enum { NONE,READ,WRITE } last_action; }; void updateDPT(void); @@ -73,9 +75,11 @@ void incrementFDD(void); #define MAX_HDD_IMAGES 2 -extern imageDisk *imageDiskList[2 + MAX_HDD_IMAGES]; -extern imageDisk *diskSwap[20]; -extern Bits swapPosition; +#define MAX_DISK_IMAGES (2 + MAX_HDD_IMAGES) + +extern imageDisk *imageDiskList[MAX_DISK_IMAGES]; +extern imageDisk *diskSwap[MAX_SWAPPABLE_DISKS]; +extern Bit32s swapPosition; extern Bit16u imgDTASeg; /* Real memory location of temporary DTA pointer for fat image disk access */ extern RealPt imgDTAPtr; /* Real memory location of temporary DTA pointer for fat image disk access */ extern DOS_DTA *imgDTA; diff --git a/include/callback.h b/include/callback.h index ff7ebfa..03008e4 100644 --- a/include/callback.h +++ b/include/callback.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -27,10 +27,11 @@ typedef Bitu (*CallBack_Handler)(void); extern CallBack_Handler CallBack_Handlers[]; -enum { CB_RETN,CB_RETF,CB_RETF8,CB_IRET,CB_IRETD,CB_IRET_STI,CB_IRET_EOI_PIC1, +enum { CB_RETN,CB_RETF,CB_RETF8,CB_RETF_STI,CB_RETF_CLI, + CB_IRET,CB_IRETD,CB_IRET_STI,CB_IRET_EOI_PIC1, CB_IRQ0,CB_IRQ1,CB_IRQ9,CB_IRQ12,CB_IRQ12_RET,CB_IRQ6_PCJR,CB_MOUSE, CB_INT29,CB_INT16,CB_HOOKABLE,CB_TDE_IRET,CB_IPXESR,CB_IPXESR_RET, - CB_INT21,CB_INT13 }; + CB_INT21,CB_INT13,CB_VESA_WAIT,CB_VESA_PM }; #define CB_MAX 128 #define CB_SIZE 32 diff --git a/include/control.h b/include/control.h index e500db2..6d1dffb 100644 --- a/include/control.h +++ b/include/control.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/include/cpu.h b/include/cpu.h index 0903ffb..ecbabd7 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -37,7 +37,7 @@ #define CPU_AUTODETERMINE_SHIFT 0x02 #define CPU_AUTODETERMINE_MASK 0x03 -#define CPU_CYCLES_LOWER_LIMIT 100 +#define CPU_CYCLES_LOWER_LIMIT 200 #define CPU_ARCHTYPE_MIXED 0xff @@ -71,6 +71,7 @@ extern CPU_Decoder * cpudecoder; Bits CPU_Core_Normal_Run(void); Bits CPU_Core_Normal_Trap_Run(void); Bits CPU_Core_Simple_Run(void); +Bits CPU_Core_Simple_Trap_Run(void); Bits CPU_Core_Full_Run(void); Bits CPU_Core_Dyn_X86_Run(void); Bits CPU_Core_Dyn_X86_Trap_Run(void); diff --git a/include/cross.h b/include/cross.h index 73f954e..90b6531 100644 --- a/include/cross.h +++ b/include/cross.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -105,4 +105,5 @@ bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_dire bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory); void close_directory(dir_information* dirp); +FILE *fopen_wrap(const char *path, const char *mode); #endif diff --git a/include/debug.h b/include/debug.h index 031e683..7871d63 100644 --- a/include/debug.h +++ b/include/debug.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ void DEBUG_SetupConsole(void); diff --git a/include/dma.h b/include/dma.h index 71a40b8..ffae266 100644 --- a/include/dma.h +++ b/include/dma.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -24,7 +24,7 @@ enum DMAEvent { DMA_REACHED_TC, DMA_MASKED, DMA_UNMASKED, - DMA_TRANSFEREND +// DMA_TRANSFEREND, this shouldn't really be a ignal }; class DmaChannel; @@ -86,8 +86,8 @@ private: bool flipflop; DmaChannel *DmaChannels[4]; public: - IO_ReadHandleObject DMA_ReadHandler[0x11]; - IO_WriteHandleObject DMA_WriteHandler[0x11]; + IO_ReadHandleObject DMA_ReadHandler[0x12]; + IO_WriteHandleObject DMA_WriteHandler[0x12]; DmaController(Bit8u num) { flipflop = false; ctrlnum = num; /* first or second DMA controller */ @@ -115,6 +115,4 @@ bool SecondDMAControllerAvailable(void); void DMA_SetWrapping(Bitu wrap); -static Bit32u dma_wrapping = 0xffff; - #endif diff --git a/include/dos_inc.h b/include/dos_inc.h index 3091c19..67f8357 100644 --- a/include/dos_inc.h +++ b/include/dos_inc.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -110,19 +110,19 @@ enum { HAND_NONE=0,HAND_FILE,HAND_DEVICE}; /* Routines for File Class */ void DOS_SetupFiles (void); -bool DOS_ReadFile(Bit16u handle,Bit8u * data,Bit16u * amount); -bool DOS_WriteFile(Bit16u handle,Bit8u * data,Bit16u * amount); -bool DOS_SeekFile(Bit16u handle,Bit32u * pos,Bit32u type); -bool DOS_CloseFile(Bit16u handle); +bool DOS_ReadFile(Bit16u handle,Bit8u * data,Bit16u * amount, bool fcb = false); +bool DOS_WriteFile(Bit16u handle,Bit8u * data,Bit16u * amount,bool fcb = false); +bool DOS_SeekFile(Bit16u handle,Bit32u * pos,Bit32u type,bool fcb = false); +bool DOS_CloseFile(Bit16u handle,bool fcb = false); bool DOS_FlushFile(Bit16u handle); bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry); bool DOS_ForceDuplicateEntry(Bit16u entry,Bit16u newentry); bool DOS_GetFileDate(Bit16u entry, Bit16u* otime, Bit16u* odate); /* Routines for Drive Class */ -bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry); +bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry,bool fcb = false); bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bit16u action, Bit16u *entry, Bit16u* status); -bool DOS_CreateFile(char const * name,Bit16u attribute,Bit16u * entry); +bool DOS_CreateFile(char const * name,Bit16u attribute,Bit16u * entry, bool fcb = false); bool DOS_UnlinkFile(char const * const name); bool DOS_FindFirst(char *search,Bit16u attr,bool fcb_findfirst=false); bool DOS_FindNext(void); @@ -386,6 +386,7 @@ public: void SetDiskBufferHeadPt(Bit32u _dbheadpt); void SetStartOfUMBChain(Bit16u _umbstartseg); void SetUMBChainState(Bit8u _umbchaining); + void SetBlockDevices(Bit8u _count); Bit16u GetStartOfUMBChain(void); Bit8u GetUMBChainState(void); RealPt GetPointer(void); @@ -501,14 +502,16 @@ public: void GetRecord(Bit16u & _cur_block,Bit8u & _cur_rec); void SetRecord(Bit16u _cur_block,Bit8u _cur_rec); void GetSeqData(Bit8u & _fhandle,Bit16u & _rec_size); + void SetSeqData(Bit8u _fhandle,Bit16u _rec_size); void GetRandom(Bit32u & _random); void SetRandom(Bit32u _random); Bit8u GetDrive(void); bool Extended(void); void GetAttr(Bit8u & attr); void SetAttr(Bit8u attr); - void SetResultAttr(Bit8u attr); + void SetResult(Bit32u size,Bit16u date,Bit16u time,Bit8u attr); bool Valid(void); + void ClearBlockRecsize(void); private: bool extended; PhysPt real_pt; @@ -528,6 +531,8 @@ private: Bit8u sft_entries; Bit8u share_attributes; Bit8u extra_info; + /* Maybe swap file_handle and sft_entries now that fcbs + * aren't stored in the psp filetable anymore */ Bit8u file_handle; Bit8u reserved[4]; /* end */ @@ -622,6 +627,8 @@ struct DOS_Block { bool verify; bool breakcheck; bool echo; // if set to true dev_con::read will echo input + bool direct_output; + bool internal_output; struct { RealPt mediaid; RealPt tempdta; @@ -638,7 +645,7 @@ struct DOS_Block { extern DOS_Block dos; -static Bit8u RealHandle(Bit16u handle) { +static INLINE Bit8u RealHandle(Bit16u handle) { DOS_PSP psp(dos.psp()); return psp.GetFileHandle(handle); } diff --git a/include/dos_system.h b/include/dos_system.h index d59a7d7..9222708 100644 --- a/include/dos_system.h +++ b/include/dos_system.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -62,7 +62,7 @@ class DOS_DTA; class DOS_File { public: - DOS_File():flags(0) { name=0; refCtr = 0; hdrive=0xff; }; + DOS_File():flags(0),time(0),date(0),attr(0),refCtr(0),open(false),name(0),hdrive(0xff) { }; DOS_File(const DOS_File& orig); DOS_File & operator= (const DOS_File & orig); virtual ~DOS_File(){if(name) delete [] name;}; @@ -117,6 +117,23 @@ private: Bitu devnum; }; +class localFile : public DOS_File { +public: + localFile(const char* name, FILE * handle); + bool Read(Bit8u * data,Bit16u * size); + bool Write(Bit8u * data,Bit16u * size); + bool Seek(Bit32u * pos,Bit32u type); + bool Close(); + Bit16u GetInformation(void); + bool UpdateDateTimeFromHost(void); + void FlagReadOnlyMedium(void); + void Flush(void); + FILE * fhandle; //todo handle this properly +private: + bool read_only_medium; + enum { NONE,READ,WRITE } last_action; +}; + /* The following variable can be lowered to free up some memory. * The negative side effect: The stored searches will be turned over faster. * Should not have impact on systems with few directory entries. */ @@ -145,6 +162,8 @@ public: void CacheOut (const char* path, bool ignoreLastDir = false); void AddEntry (const char* path, bool checkExist = false); + void AddEntryDirOverlay (const char* path, bool checkExist = false); + void DeleteEntry (const char* path, bool ignoreLastDir = false); void EmptyCache (void); @@ -155,7 +174,7 @@ public: public: CFileInfo(void) { orgname[0] = shortname[0] = 0; - isDir = false; + isOverlayDir = isDir = false; id = MAX_OPENDIRS; nextEntry = shortNr = 0; } @@ -166,6 +185,7 @@ public: }; char orgname [CROSS_LEN]; char shortname [DOS_NAMELENGTH_ASCII]; + bool isOverlayDir; bool isDir; Bit16u id; Bitu nextEntry; @@ -189,7 +209,7 @@ private: CFileInfo* FindDirInfo (const char* path, char* expandedPath); bool RemoveSpaces (char* str); bool OpenDir (CFileInfo* dir, const char* path, Bit16u& id); - void CreateEntry (CFileInfo* dir, const char* name, bool query_directory); + void CreateEntry (CFileInfo* dir, const char* name, bool is_directory); void CopyEntry (CFileInfo* dir, CFileInfo* from); Bit16u GetFreeID (CFileInfo* dir); void Clear (void); diff --git a/include/dosbox.h b/include/dosbox.h index a72d954..b5e091b 100644 --- a/include/dosbox.h +++ b/include/dosbox.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/include/fpu.h b/include/fpu.h index 8b89934..44acd31 100644 --- a/include/fpu.h +++ b/include/fpu.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,14 +11,19 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_FPU_H #define DOSBOX_FPU_H +#ifndef DOSBOX_DOSBOX_H +//So the right config.h gets included for C_DEBUG +#include "dosbox.h" +#endif + #ifndef DOSBOX_MEM_H #include "mem.h" #endif @@ -86,7 +91,7 @@ typedef struct { FPU_Tag tags[9]; Bit16u cw,cw_mask_all; Bit16u sw; - Bitu top; + Bit32u top; FPU_Round round; } FPU_rec; @@ -150,5 +155,22 @@ static INLINE void FPU_SET_C3(Bitu C){ if(C) fpu.sw |= 0x4000; } +#define DB_FPU_STACK_CHECK_NONE 0 +#define DB_FPU_STACK_CHECK_LOG 1 +#define DB_FPU_STACK_CHECK_EXIT 2 +//NONE is 0.74 behavior: not care about stack overflow/underflow +//Overflow is always logged/exited on. +//Underflow can be controlled with by this. +//LOG is giving a message when encountered +//EXIT is to hard exit. +//Currently pop is ignored in release mode and overflow is exit. +//in debug mode: pop will log and overflow is exit. +#if C_DEBUG +#define DB_FPU_STACK_CHECK_POP DB_FPU_STACK_CHECK_LOG +#define DB_FPU_STACK_CHECK_PUSH DB_FPU_STACK_CHECK_EXIT +#else +#define DB_FPU_STACK_CHECK_POP DB_FPU_STACK_CHECK_NONE +#define DB_FPU_STACK_CHECK_PUSH DB_FPU_STACK_CHECK_EXIT +#endif #endif diff --git a/include/hardware.h b/include/hardware.h index 4b925ab..3a96086 100644 --- a/include/hardware.h +++ b/include/hardware.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -24,7 +24,7 @@ class Section; enum OPL_Mode { - OPL_none,OPL_cms,OPL_opl2,OPL_dualopl2,OPL_opl3 + OPL_none,OPL_cms,OPL_opl2,OPL_dualopl2,OPL_opl3,OPL_opl3gold }; #define CAPTURE_WAVE 0x01 #define CAPTURE_OPL 0x02 diff --git a/include/inout.h b/include/inout.h index a49f639..3cd1b56 100644 --- a/include/inout.h +++ b/include/inout.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/include/ipx.h b/include/ipx.h index 24a1b42..fb88c0d 100644 --- a/include/ipx.h +++ b/include/ipx.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/include/ipxserver.h b/include/ipxserver.h index c10fc4a..b4fa9fe 100644 --- a/include/ipxserver.h +++ b/include/ipxserver.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_IPXSERVER_H_ diff --git a/include/joystick.h b/include/joystick.h index 02f00a4..c3782c9 100644 --- a/include/joystick.h +++ b/include/joystick.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_JOYSTICK_H diff --git a/include/keyboard.h b/include/keyboard.h index af754c5..125c9ca 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_KEYBOARD_H diff --git a/include/logging.h b/include/logging.h index 7faf84c..0e9c40e 100644 --- a/include/logging.h +++ b/include/logging.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_LOGGING_H diff --git a/include/mapper.h b/include/mapper.h index ba56515..0fd953e 100644 --- a/include/mapper.h +++ b/include/mapper.h @@ -1,5 +1,5 @@ - /* - * Copyright (C) 2002-2011 The DOSBox Team +/* + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_MAPPER_H diff --git a/include/mem.h b/include/mem.h index 1b40c55..99b082e 100644 --- a/include/mem.h +++ b/include/mem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_MEM_H diff --git a/include/midi.h b/include/midi.h new file mode 100644 index 0000000..773b6e6 --- /dev/null +++ b/include/midi.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2002-2019 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef DOSBOX_MIDI_H +#define DOSBOX_MIDI_H + +#ifndef DOSBOX_DOSBOX_H +#include "dosbox.h" +#endif + +#ifndef DOSBOX_PROGRAMS_H +#include "programs.h" +#endif + +class MidiHandler { +public: + MidiHandler(); + virtual bool Open(const char * /*conf*/) { + LOG_MSG("No working midi device found/selected! Please check your settings and/or compilation environment."); + return true; + }; + virtual void Close(void) {}; + virtual void PlayMsg(Bit8u * /*msg*/) {}; + virtual void PlaySysex(Bit8u * /*sysex*/,Bitu /*len*/) {}; + virtual const char * GetName(void) { return "none"; }; + virtual void ListAll(Program * base) {}; + virtual ~MidiHandler() { }; + MidiHandler * next; +}; + + +#define SYSEX_SIZE 8192 +struct DB_Midi { + Bitu status; + Bitu cmd_len; + Bitu cmd_pos; + Bit8u cmd_buf[8]; + Bit8u rt_buf[8]; + struct { + Bit8u buf[SYSEX_SIZE]; + Bitu used; + Bitu delay; + Bit32u start; + } sysex; + bool available; + MidiHandler * handler; +}; + +extern DB_Midi midi; + +#endif diff --git a/include/mixer.h b/include/mixer.h index ed9669a..3c81753 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -72,18 +72,27 @@ public: void AddSamples_s16u_nonnative(Bitu len, const Bit16u * data); void AddSamples_m32_nonnative(Bitu len, const Bit32s * data); void AddSamples_s32_nonnative(Bitu len, const Bit32s * data); - + void AddStretched(Bitu len,Bit16s * data); //Strech block up into needed data + void FillUp(void); void Enable(bool _yesno); MIXER_Handler handler; float volmain[2]; float scale; Bit32s volmul[2]; - Bitu freq_add,freq_index; - Bitu done,needed; - Bits last[2]; + + //This gets added the frequency counter each mixer step + Bitu freq_add; + //When this flows over a new sample needs to be read from the device + Bitu freq_counter; + //Timing on how many samples have been done and were needed by th emixer + Bitu done, needed; + //Previous and next samples + Bits prevSample[2]; + Bits nextSample[2]; const char * name; + bool interpolate; bool enabled; MixerChannel * next; }; diff --git a/include/modules.h b/include/modules.h deleted file mode 100644 index 6bbeffc..0000000 --- a/include/modules.h +++ /dev/null @@ -1,180 +0,0 @@ -/* Standard data types used */ - -typedef unsigned char Bit8u; -typedef signed char Bit8s; -typedef unsigned short Bit16u; -typedef signed short Bit16s; -typedef unsigned long Bit32u; -typedef signed long Bit32s; -#if defined(_MSC_VER) -typedef unsigned __int64 Bit64u; -typedef signed __int64 Bit64s; -#else -typedef unsigned long long int Bit64u; -typedef signed long long int Bit64s; -#endif - - - -/* Setting up pointers to all subfunctions */ -#ifdef MODULE_WANT_IO_READ -typedef Bit8u (* IO_ReadHandler)(Bit32u port); -static void (* IO_RegisterReadHandler)(Bit32u port,IO_ReadHandler handler,char * name); -static void (* IO_FreeReadHandler)(Bit32u port); -#endif - -#ifdef MODULE_WANT_IO_WRITE -typedef void (* IO_WriteHandler)(Bit32u port,Bit8u value); -static void (* IO_RegisterWriteHandler)(Bit32u port,IO_WriteHandler handler,char * name); -static void (* IO_FreeWriteHandler)(Bit32u port); -#endif - -#ifdef MODULE_WANT_IRQ_EOI -typedef void (* IRQ_EOIHandler)(void); -static void (* IRQ_RegisterEOIHandler)(Bit32u irq,IRQ_EOIHandler handler,char * name); -static void (* IRQ_FreeEOIHandler)(Bit32u irq); -#endif - -#ifdef MODULE_WANT_IRQ -static void (* IRQ_Activate)(Bit32u irq); -static void (* IRQ_Deactivate)(Bit32u irq); -#endif - -#ifdef MODULE_WANT_TIMER -typedef void (* TIMER_MicroHandler)(void); -static void (* TIMER_RegisterMicroHandler)(TIMER_MicroHandler handler,Bit32u micro); -#endif - -#ifdef MODULE_WANT_TIMER_TICK -typedef void (* TIMER_TickHandler)(Bit32u ticks); -static void (* TIMER_RegisterTickHandler)(TIMER_TickHandler handler); -#endif - -/* - 4 8-bit and 4 16-bit channels you can read data from - 16-bit reads are word sized -*/ - -#ifdef MODULE_WANT_DMA_READ -static void (* DMA_8_Read)(Bit32u chan,Bit8u * data,Bit16u size); -static void (* DMA_16_Read)(Bit32u chan,Bit8u * data,Bit16u size); -#endif - -/* - 4 8-bit and 4 16-bit channels you can write data from - 16-bit writes are word sized -*/ - -#ifdef MODULE_WANT_DMA_READ -static void (* DMA_8_Write)(Bit32u chan,Bit8u * data,Bit16u size); -static void (* DMA_16_Write)(Bit32u chan,Bit8u * data,Bit16u size); -#endif - - -#ifdef MODULE_WANT_MIXER -/* The len here means the amount of samples needed not the buffersize it needed to fill */ -typedef void (* MIXER_MixHandler)(Bit8u * sampdate,Bit32u len); - -/* Different types if modes a mixer channel can work in */ -#define MIXER_8MONO 0 -#define MIXER_8STEREO 1 -#define MIXER_16MONO 2 -#define MIXER_16STEREO 3 -struct MIXER_Channel; - -#define MAX_AUDIO ((1<<(16-1))-1) -#define MIN_AUDIO -(1<<(16-1)) - -MIXER_Channel *(* MIXER_AddChannel)(MIXER_MixHandler handler,Bit32u freq,char * name); -void (* MIXER_SetVolume)(MIXER_Channel * chan,Bit8u vol); -void (* MIXER_SetFreq)(MIXER_Channel * chan,Bit32u freq); -void (* MIXER_SetMode)(MIXER_Channel * chan,Bit8u mode); -void (* MIXER_Enable)(MIXER_Channel * chan,bool enable); -#endif - -typedef bool (* MODULE_FindHandler)(char * name,void * * function); -typedef char *(* MODULE_StartHandler)(MODULE_FindHandler find_handler); - -#define MODULE_START_PROC "ModuleStart" - -#ifdef MODULE_START_FUNCTION -#include - -#define GET_FUNCTION(a) \ - if (!find_handler(#a ,(void * *) &a)) { \ - return "Can't find requested function"; \ - }; - - -#if defined (WIN32) -#include -BOOL APIENTRY DllMain( HANDLE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - return TRUE; -} - -extern "C" { -__declspec(dllexport) -#endif -char * ModuleStart (MODULE_FindHandler find_handler) { - -#ifdef MODULE_WANT_IRQ_EOI -GET_FUNCTION(IRQ_RegisterEOIHandler); -GET_FUNCTION(IRQ_FreeEOIHandler); -#endif - -#ifdef MODULE_WANT_IRQ -GET_FUNCTION(IRQ_Activate); -GET_FUNCTION(IRQ_Deactivate); -#endif - -#ifdef MODULE_WANT_IO_READ -GET_FUNCTION(IO_RegisterReadHandler); -GET_FUNCTION(IO_FreeReadHandler); -#endif - -#ifdef MODULE_WANT_IO_WRITE -GET_FUNCTION(IO_RegisterWriteHandler); -GET_FUNCTION(IO_FreeWriteHandler); -#endif - -#ifdef MODULE_WANT_TIMER -GET_FUNCTION(TIMER_RegisterMicroHandler); -#endif - -#ifdef MODULE_WANT_TIMER_TICKS -GET_FUNCTION(TIMER_RegisterTickHandler); -#endif - -#ifdef MODULE_WANT_DMA_READ -GET_FUNCTION(DMA_8_Read); -GET_FUNCTION(DMA_16_Read); -#endif - -#ifdef MODULE_WANT_DMA_WRITE -GET_FUNCTION(DMA_8_Write); -GET_FUNCTION(DMA_16_Write); -#endif - -#ifdef MODULE_WANT_MIXER -GET_FUNCTION(MIXER_AddChannel); -GET_FUNCTION(MIXER_SetVolume); -GET_FUNCTION(MIXER_SetFreq); -GET_FUNCTION(MIXER_SetMode); -GET_FUNCTION(MIXER_Enable); -#endif - -return MODULE_START_FUNCTION; - -} -#if defined (WIN32) -} -#endif - - - -#endif - diff --git a/include/mouse.h b/include/mouse.h index 3a318ea..f161463 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -36,6 +36,7 @@ void Mouse_ButtonPressed(Bit8u button); void Mouse_ButtonReleased(Bit8u button); void Mouse_AutoLock(bool enable); -void Mouse_NewVideoMode(void); +void Mouse_BeforeNewVideoMode(bool setmode); +void Mouse_AfterNewVideoMode(bool setmode); #endif diff --git a/include/paging.h b/include/paging.h index e6913f1..098b6cc 100644 --- a/include/paging.h +++ b/include/paging.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -50,9 +50,11 @@ class PageDirectory; #define PFLAG_READABLE 0x1 #define PFLAG_WRITEABLE 0x2 #define PFLAG_HASROM 0x4 -#define PFLAG_HASCODE 0x8 //Page contains dynamic code +#define PFLAG_HASCODE32 0x8 //Page contains 32-bit dynamic code #define PFLAG_NOCODE 0x10 //No dynamic code can be generated here #define PFLAG_INIT 0x20 //No dynamic code can be generated here +#define PFLAG_HASCODE16 0x40 //Page contains 16-bit dynamic code +#define PFLAG_HASCODE (PFLAG_HASCODE32|PFLAG_HASCODE16) #define LINK_START ((1024+64)/4) //Start right after the HMA @@ -226,7 +228,7 @@ void PAGING_InitTLBBank(tlb_entry **bank); static INLINE tlb_entry *get_tlb_entry(PhysPt address) { Bitu index=(address>>12); - if (TLB_BANKS && (index > TLB_SIZE)) { + if (TLB_BANKS && (index >= TLB_SIZE)) { Bitu bank=(address>>BANK_SHIFT) - 1; if (!paging.tlbh_banks[bank]) PAGING_InitTLBBank(&paging.tlbh_banks[bank]); diff --git a/include/pci_bus.h b/include/pci_bus.h index 2dadfa0..3b8e332 100644 --- a/include/pci_bus.h +++ b/include/pci_bus.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_PCI_H diff --git a/include/pic.h b/include/pic.h index 1c8a0a3..5022c7c 100644 --- a/include/pic.h +++ b/include/pic.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_PIC_H @@ -29,11 +29,7 @@ typedef void (PIC_EOIHandler) (void); typedef void (* PIC_EventHandler)(Bitu val); -#define PIC_MAXIRQ 15 -#define PIC_NOIRQ 0xFF - extern Bitu PIC_IRQCheck; -extern Bitu PIC_IRQActive; extern Bitu PIC_Ticks; static INLINE float PIC_TickIndex(void) { diff --git a/include/programs.h b/include/programs.h index 8815fa0..ac48bd8 100644 --- a/include/programs.h +++ b/include/programs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/include/regs.h b/include/regs.h index 07a11a6..a25ff80 100644 --- a/include/regs.h +++ b/include/regs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_REGS_H @@ -59,7 +59,7 @@ struct Segment { enum SegNames { es=0,cs,ss,ds,fs,gs}; struct Segments { - Bitu val[8]; + Bit16u val[8]; PhysPt phys[8]; }; diff --git a/include/render.h b/include/render.h index c63ef12..83f559a 100644 --- a/include/render.h +++ b/include/render.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_RENDER_H @@ -58,8 +58,8 @@ typedef struct { float fps; } src; struct { - Bitu count; - Bitu max; + int count; + int max; Bitu index; Bit8u hadSkip[RENDER_SKIP_CACHE]; } frameskip; diff --git a/include/serialport.h b/include/serialport.h index 9ec7577..e9bbc54 100644 --- a/include/serialport.h +++ b/include/serialport.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/include/setup.h b/include/setup.h index d8b1623..c418ed1 100644 --- a/include/setup.h +++ b/include/setup.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -46,7 +46,7 @@ #ifndef CH_CSTDIO #define CH_CSTDIO -#include +#include #endif @@ -58,7 +58,6 @@ public: Hex():_hex(0) { }; bool operator==(Hex const& other) {return _hex == other._hex;} operator int () const { return _hex; } - }; class Value { @@ -78,40 +77,40 @@ private: public: class WrongType { }; // Conversion error class enum Etype { V_NONE, V_HEX, V_BOOL, V_INT, V_STRING, V_DOUBLE,V_CURRENT} type; - + /* Constructors */ - Value() :_string(0), type(V_NONE) { }; - Value(Hex in) :_hex(in), type(V_HEX) { }; - Value(int in) :_int(in), type(V_INT) { }; - Value(bool in) :_bool(in), type(V_BOOL) { }; - Value(double in) :_double(in), type(V_DOUBLE) { }; - Value(std::string const& in) :_string(new std::string(in)),type(V_STRING) { }; - Value(char const * const in) :_string(new std::string(in)),type(V_STRING) { }; + Value() :_hex(0), _bool(false),_int(0), _string(0), _double(0), type(V_NONE) { }; + Value(Hex in) :_hex(in),_bool(false),_int(0), _string(0), _double(0), type(V_HEX) { }; + Value(int in) :_hex(0), _bool(false),_int(in),_string(0), _double(0), type(V_INT) { }; + Value(bool in) :_hex(0), _bool(in) ,_int(0), _string(0), _double(0), type(V_BOOL) { }; + Value(double in) :_hex(0), _bool(false),_int(0), _string(0), _double(in),type(V_DOUBLE) { }; + Value(std::string const& in) :_hex(0), _bool(false),_int(0), _string(new std::string(in)),_double(0), type(V_STRING) { }; + Value(char const * const in) :_hex(0), _bool(false),_int(0), _string(new std::string(in)),_double(0), type(V_STRING) { }; Value(Value const& in):_string(0) {plaincopy(in);} ~Value() { destroy();}; - Value(std::string const& in,Etype _t) :_string(0),type(V_NONE) {SetValue(in,_t);} - + Value(std::string const& in,Etype _t) :_hex(0),_bool(false),_int(0),_string(0),_double(0),type(V_NONE) {SetValue(in,_t);} + /* Assigment operators */ - Value& operator= (Hex in) throw(WrongType) { return copy(Value(in));} - Value& operator= (int in) throw(WrongType) { return copy(Value(in));} - Value& operator= (bool in) throw(WrongType) { return copy(Value(in));} - Value& operator= (double in) throw(WrongType) { return copy(Value(in));} - Value& operator= (std::string const& in) throw(WrongType) { return copy(Value(in));} - Value& operator= (char const * const in) throw(WrongType) { return copy(Value(in));} - Value& operator= (Value const& in) throw(WrongType) { return copy(Value(in));} + Value& operator= (Hex in) { return copy(Value(in));} + Value& operator= (int in) { return copy(Value(in));} + Value& operator= (bool in) { return copy(Value(in));} + Value& operator= (double in) { return copy(Value(in));} + Value& operator= (std::string const& in) { return copy(Value(in));} + Value& operator= (char const * const in) { return copy(Value(in));} + Value& operator= (Value const& in) { return copy(Value(in));} bool operator== (Value const & other); - operator bool () const throw(WrongType); - operator Hex () const throw(WrongType); - operator int () const throw(WrongType); - operator double () const throw(WrongType); - operator char const* () const throw(WrongType); - bool SetValue(std::string const& in,Etype _type = V_CURRENT) throw(WrongType); + operator bool () const; + operator Hex () const; + operator int () const; + operator double () const; + operator char const* () const; + bool SetValue(std::string const& in,Etype _type = V_CURRENT); std::string ToString() const; private: void destroy() throw(); - Value& copy(Value const& in) throw(WrongType); + Value& copy(Value const& in); void plaincopy(Value const& in) throw(); bool set_hex(std::string const& in); bool set_int(std::string const&in); @@ -132,19 +131,26 @@ public: virtual bool SetValue(std::string const& str)=0; Value const& GetValue() const { return value;} Value const& Get_Default_Value() const { return default_value; } - //CheckValue returns true if value is in suggested_values; + //CheckValue returns true, if value is in suggested_values; //Type specific properties are encouraged to override this and check for type //specific features. virtual bool CheckValue(Value const& in, bool warn); - //Set interval value to in or default if in is invalid. force always sets the value. - bool SetVal(Value const& in, bool forced,bool warn=true) { - if(forced || CheckValue(in,warn)) {value = in; return true;} else { value = default_value; return false;}} - virtual ~Property(){ } +public: + virtual ~Property(){ } virtual const std::vector& GetValues() const; Value::Etype Get_type(){return default_value.type;} Changeable::Value getChange() {return change;} protected: + //Set interval value to in or default if in is invalid. force always sets the value. + //Can be overriden to set a different value if invalid. + virtual bool SetVal(Value const& in, bool forced,bool warn=true) { + if(forced || CheckValue(in,warn)) { + value = in; return true; + } else { + value = default_value; return false; + } + } Value value; std::vector suggested_values; typedef std::vector::iterator iter; @@ -155,12 +161,12 @@ protected: class Prop_int:public Property { public: Prop_int(std::string const& _propname,Changeable::Value when, int _value) - :Property(_propname,when) { + :Property(_propname,when) { default_value = value = _value; min = max = -1; } Prop_int(std::string const& _propname,Changeable::Value when, int _min,int _max,int _value) - :Property(_propname,when) { + :Property(_propname,when) { default_value = value = _value; min = _min; max = _max; @@ -171,6 +177,9 @@ public: bool SetValue(std::string const& in); ~Prop_int(){ } virtual bool CheckValue(Value const& in, bool warn); + // Override SetVal, so it takes min,max in account when there are no suggested values + virtual bool SetVal(Value const& in, bool forced,bool warn=true); + private: Value min,max; }; @@ -188,7 +197,7 @@ public: class Prop_bool:public Property { public: Prop_bool(std::string const& _propname, Changeable::Value when, bool _value) - :Property(_propname,when) { + :Property(_propname,when) { default_value = value = _value; } bool SetValue(std::string const& in); @@ -198,7 +207,7 @@ public: class Prop_string:public Property{ public: Prop_string(std::string const& _propname, Changeable::Value when, char const * const _value) - :Property(_propname,when) { + :Property(_propname,when) { default_value = value = _value; } bool SetValue(std::string const& in); @@ -209,7 +218,7 @@ class Prop_path:public Prop_string{ public: std::string realpath; Prop_path(std::string const& _propname, Changeable::Value when, char const * const _value) - :Prop_string(_propname,when,_value) { + :Prop_string(_propname,when,_value) { default_value = value = _value; realpath = _value; } @@ -220,7 +229,7 @@ public: class Prop_hex:public Property { public: Prop_hex(std::string const& _propname, Changeable::Value when, Hex _value) - :Property(_propname,when) { + :Property(_propname,when) { default_value = value = _value; } bool SetValue(std::string const& in); @@ -274,7 +283,7 @@ public: Prop_path* Add_path(std::string const& _propname, Property::Changeable::Value when, char const * const _value=NULL); Prop_bool* Add_bool(std::string const& _propname, Property::Changeable::Value when, bool _value=false); Prop_hex* Add_hex(std::string const& _propname, Property::Changeable::Value when, Hex _value=0); -// void Add_double(char const * const _propname, double _value=0.0); +// void Add_double(char const * const _propname, double _value=0.0); Prop_multival *Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep); Prop_multival_remain *Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep); @@ -297,10 +306,10 @@ public: class Prop_multival:public Property{ protected: Section_prop* section; - std::string seperator; + std::string separator; void make_default_value(); public: - Prop_multival(std::string const& _propname, Changeable::Value when,std::string const& sep):Property(_propname,when), section(new Section_prop("")),seperator(sep) { + Prop_multival(std::string const& _propname, Changeable::Value when,std::string const& sep):Property(_propname,when), section(new Section_prop("")),separator(sep) { default_value = value = ""; } Section_prop *GetSection() { return section; } @@ -317,7 +326,7 @@ public: virtual bool SetValue(std::string const& input); }; - + class Section_line: public Section{ public: Section_line(std::string const& _sectionname):Section(_sectionname){} diff --git a/include/shell.h b/include/shell.h index ec7d7d0..fc47285 100644 --- a/include/shell.h +++ b/include/shell.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -35,11 +35,12 @@ #define CMD_MAXCMDS 20 #define CMD_OLDSIZE 4096 extern Bitu call_shellstop; +class DOS_Shell; + /* first_shell is used to add and delete stuff from the shell env * by "external" programs. (config) */ -extern Program * first_shell; +extern DOS_Shell * first_shell; -class DOS_Shell; class BatchFile { public: diff --git a/include/support.h b/include/support.h index 93c94ea..bd21e65 100644 --- a/include/support.h +++ b/include/support.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -53,6 +53,7 @@ bool IsHexWord(char * word); Bits ConvDecWord(char * word); Bits ConvHexWord(char * word); +void trim(std::string& str); void upcase(std::string &str); void lowcase(std::string &str); diff --git a/include/timer.h b/include/timer.h index f4fb0c5..d723e01 100644 --- a/include/timer.h +++ b/include/timer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_TIMER_H diff --git a/include/vga.h b/include/vga.h index 6b19e6e..e204105 100644 --- a/include/vga.h +++ b/include/vga.h @@ -1,5 +1,5 @@ - /* - * Copyright (C) 2002-2011 The DOSBox Team +/* + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -112,7 +112,7 @@ typedef struct { typedef enum { PART, - LINE, + DRAWLINE, EGALINE } Drawmode; @@ -427,6 +427,10 @@ void VGA_DAC_CombineColor(Bit8u attr,Bit8u pal); void VGA_DAC_SetEntry(Bitu entry,Bit8u red,Bit8u green,Bit8u blue); void VGA_ATTR_SetPalette(Bit8u index,Bit8u val); +typedef enum {CGA, EGA, MONO} EGAMonitorMode; + +void VGA_ATTR_SetEGAMonitorPalette(EGAMonitorMode m); + /* The VGA Subfunction startups */ void VGA_SetupAttr(void); void VGA_SetupMemory(Section* sec); diff --git a/include/video.h b/include/video.h index b706295..3696ced 100644 --- a/include/video.h +++ b/include/video.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/scripts/dosbox-installer.nsi b/scripts/dosbox-installer.nsi index 470c678..debbb36 100644 --- a/scripts/dosbox-installer.nsi +++ b/scripts/dosbox-installer.nsi @@ -2,7 +2,7 @@ !define VER_MINOR 74 !define APP_NAME "DOSBox ${VER_MAYOR}.${VER_MINOR} Installer" !define COMP_NAME "DOSBox Team" -!define COPYRIGHT "Copyright © 2002-2011 DOSBox Team" +!define COPYRIGHT "Copyright © 2002-2019 DOSBox Team" !define DESCRIPTION "DOSBox Installer" VIProductVersion "${VER_MAYOR}.${VER_MINOR}.0.0" diff --git a/src/Makefile.am b/src/Makefile.am index 184469e..a4029e8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,7 +13,8 @@ endif dosbox_SOURCES = dosbox.cpp $(ico_stuff) dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a \ - ints/libints.a misc/libmisc.a shell/libshell.a hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a + ints/libints.a misc/libmisc.a shell/libshell.a hardware/mame/libmame.a \ + hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a EXTRA_DIST = winres.rc dosbox.ico diff --git a/src/cpu/callback.cpp b/src/cpu/callback.cpp index de64bf5..b1a18e0 100644 --- a/src/cpu/callback.cpp +++ b/src/cpu/callback.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -33,7 +33,7 @@ CallBack_Handler CallBack_Handlers[CB_MAX]; char* CallBack_Description[CB_MAX]; -static Bitu call_stop,call_idle,call_default,call_default2; +static Bitu call_stop,call_idle,call_default; Bitu call_priv_io; static Bitu illegal_handler(void) { @@ -173,6 +173,26 @@ Bitu CALLBACK_SetupExtra(Bitu callback, Bitu type, PhysPt physAddress, bool use_ phys_writeb(physAddress+0x00,(Bit8u)0xCA); //A RETF 8 Instruction phys_writew(physAddress+0x01,(Bit16u)0x0008); return (use_cb?7:3); + case CB_RETF_STI: + phys_writeb(physAddress+0x00,(Bit8u)0xFB); //STI + if (use_cb) { + phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4 + phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction + phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word + physAddress+=4; + } + phys_writeb(physAddress+0x01,(Bit8u)0xCB); //A RETF Instruction + return (use_cb?6:2); + case CB_RETF_CLI: + phys_writeb(physAddress+0x00,(Bit8u)0xFA); //CLI + if (use_cb) { + phys_writeb(physAddress+0x01,(Bit8u)0xFE); //GRP 4 + phys_writeb(physAddress+0x02,(Bit8u)0x38); //Extra Callback instruction + phys_writew(physAddress+0x03,(Bit16u)callback); //The immediate word + physAddress+=4; + } + phys_writeb(physAddress+0x01,(Bit8u)0xCB); //A RETF Instruction + return (use_cb?6:2); case CB_IRET: if (use_cb) { phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4 @@ -256,7 +276,15 @@ Bitu CALLBACK_SetupExtra(Bitu callback, Bitu type, PhysPt physAddress, bool use_ phys_writew(physAddress+0x0b,(Bit16u)0x20e6); // out 0x20, al phys_writeb(physAddress+0x0d,(Bit8u)0x58); // pop ax phys_writeb(physAddress+0x0e,(Bit8u)0xcf); //An IRET Instruction - return (use_cb?0x15:0x0f); + phys_writeb(physAddress+0x0f,(Bit8u)0xfa); // cli + phys_writew(physAddress+0x10,(Bit16u)0x20b0); // mov al, 0x20 + phys_writew(physAddress+0x12,(Bit16u)0x20e6); // out 0x20, al + phys_writeb(physAddress+0x14,(Bit8u)0x55); // push bp + phys_writew(physAddress+0x15,(Bit16u)0x05cd); // int 5 + phys_writeb(physAddress+0x17,(Bit8u)0x5d); // pop bp + phys_writeb(physAddress+0x18,(Bit8u)0x58); // pop ax + phys_writeb(physAddress+0x19,(Bit8u)0xcf); //An IRET Instruction + return (use_cb?0x20:0x1a); case CB_IRQ9: // pic cascade interrupt if (use_cb) { phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4 @@ -274,26 +302,32 @@ Bitu CALLBACK_SetupExtra(Bitu callback, Bitu type, PhysPt physAddress, bool use_ return (use_cb?0x0e:0x0a); case CB_IRQ12: // ps2 mouse int74 if (!use_cb) E_Exit("int74 callback must implement a callback handler!"); - phys_writeb(physAddress+0x00,(Bit8u)0x1e); // push ds - phys_writeb(physAddress+0x01,(Bit8u)0x06); // push es - phys_writew(physAddress+0x02,(Bit16u)0x6066); // pushad - phys_writeb(physAddress+0x04,(Bit8u)0xfc); // cld - phys_writeb(physAddress+0x05,(Bit8u)0xfb); // sti - phys_writeb(physAddress+0x06,(Bit8u)0xFE); //GRP 4 - phys_writeb(physAddress+0x07,(Bit8u)0x38); //Extra Callback instruction - phys_writew(physAddress+0x08,(Bit16u)callback); //The immediate word - return 0x0a; + phys_writeb(physAddress+0x00,(Bit8u)0xfb); // sti + phys_writeb(physAddress+0x01,(Bit8u)0x1e); // push ds + phys_writeb(physAddress+0x02,(Bit8u)0x06); // push es + phys_writew(physAddress+0x03,(Bit16u)0x6066); // pushad + phys_writeb(physAddress+0x05,(Bit8u)0xFE); //GRP 4 + phys_writeb(physAddress+0x06,(Bit8u)0x38); //Extra Callback instruction + phys_writew(physAddress+0x07,(Bit16u)callback); //The immediate word + phys_writeb(physAddress+0x09,(Bit8u)0x50); // push ax + phys_writew(physAddress+0x0a,(Bit16u)0x20b0); // mov al, 0x20 + phys_writew(physAddress+0x0c,(Bit16u)0xa0e6); // out 0xa0, al + phys_writew(physAddress+0x0e,(Bit16u)0x20e6); // out 0x20, al + phys_writeb(physAddress+0x10,(Bit8u)0x58); // pop ax + phys_writeb(physAddress+0x11,(Bit8u)0xfc); // cld + phys_writeb(physAddress+0x12,(Bit8u)0xCB); //A RETF Instruction + return 0x13; case CB_IRQ12_RET: // ps2 mouse int74 return - if (use_cb) { - phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4 - phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction - phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word - physAddress+=4; - } phys_writeb(physAddress+0x00,(Bit8u)0xfa); // cli phys_writew(physAddress+0x01,(Bit16u)0x20b0); // mov al, 0x20 phys_writew(physAddress+0x03,(Bit16u)0xa0e6); // out 0xa0, al phys_writew(physAddress+0x05,(Bit16u)0x20e6); // out 0x20, al + if (use_cb) { + phys_writeb(physAddress+0x07,(Bit8u)0xFE); //GRP 4 + phys_writeb(physAddress+0x08,(Bit8u)0x38); //Extra Callback instruction + phys_writew(physAddress+0x09,(Bit16u)callback); //The immediate word + physAddress+=4; + } phys_writew(physAddress+0x07,(Bit16u)0x6166); // popad phys_writeb(physAddress+0x09,(Bit8u)0x07); // pop es phys_writeb(physAddress+0x0a,(Bit8u)0x1f); // pop ds @@ -304,22 +338,26 @@ Bitu CALLBACK_SetupExtra(Bitu callback, Bitu type, PhysPt physAddress, bool use_ phys_writew(physAddress+0x01,(Bit16u)0x60e4); // in al, 0x60 phys_writew(physAddress+0x03,(Bit16u)0xe03c); // cmp al, 0xe0 if (use_cb) { - phys_writew(physAddress+0x05,(Bit16u)0x0674); // je skip + phys_writew(physAddress+0x05,(Bit16u)0x0b74); // je skip phys_writeb(physAddress+0x07,(Bit8u)0xFE); //GRP 4 phys_writeb(physAddress+0x08,(Bit8u)0x38); //Extra Callback instruction phys_writew(physAddress+0x09,(Bit16u)callback); //The immediate word physAddress+=4; } else { - phys_writew(physAddress+0x05,(Bit16u)0x0274); // je skip + phys_writew(physAddress+0x05,(Bit16u)0x0774); // je skip } - phys_writew(physAddress+0x07,(Bit16u)0x09cd); // int 9 + phys_writeb(physAddress+0x07,(Bit8u)0x1e); // push ds + phys_writew(physAddress+0x08,(Bit16u)0x406a); // push 0x0040 + phys_writeb(physAddress+0x0a,(Bit8u)0x1f); // pop ds + phys_writew(physAddress+0x0b,(Bit16u)0x09cd); // int 9 + phys_writeb(physAddress+0x0d,(Bit8u)0x1f); // pop ds // jump here to (skip): - phys_writeb(physAddress+0x09,(Bit8u)0xfa); // cli - phys_writew(physAddress+0x0a,(Bit16u)0x20b0); // mov al, 0x20 - phys_writew(physAddress+0x0c,(Bit16u)0x20e6); // out 0x20, al - phys_writeb(physAddress+0x0e,(Bit8u)0x58); // pop ax - phys_writeb(physAddress+0x0f,(Bit8u)0xcf); //An IRET Instruction - return (use_cb?0x14:0x10); + phys_writeb(physAddress+0x0e,(Bit8u)0xfa); // cli + phys_writew(physAddress+0x0f,(Bit16u)0x20b0); // mov al, 0x20 + phys_writew(physAddress+0x11,(Bit16u)0x20e6); // out 0x20, al + phys_writeb(physAddress+0x13,(Bit8u)0x58); // pop ax + phys_writeb(physAddress+0x14,(Bit8u)0xcf); //An IRET Instruction + return (use_cb?0x19:0x15); case CB_MOUSE: phys_writew(physAddress+0x00,(Bit16u)0x07eb); // jmp i33hd physAddress+=9; @@ -446,8 +484,51 @@ Bitu CALLBACK_SetupExtra(Bitu callback, Bitu type, PhysPt physAddress, bool use_ phys_writew(physAddress+0x02,(Bit16u)0x0ECD); // int 0e phys_writeb(physAddress+0x04,(Bit8u)0xCF); //An IRET Instruction return (use_cb?9:5); + case CB_VESA_WAIT: + if (use_cb) E_Exit("VESA wait must not implement a callback handler!"); + phys_writeb(physAddress+0x00,(Bit8u)0xFB); // sti + phys_writeb(physAddress+0x01,(Bit8u)0x50); // push ax + phys_writeb(physAddress+0x02,(Bit8u)0x52); // push dx + phys_writeb(physAddress+0x03,(Bit8u)0xBA); // mov dx, + phys_writew(physAddress+0x04,(Bit16u)0x03DA); // 0x3da + phys_writeb(physAddress+0x06,(Bit8u)0xEC); // in al,dx + phys_writew(physAddress+0x07,(Bit16u)0x08A8); // test al,8 + phys_writew(physAddress+0x09,(Bit16u)0xFB75); // jne $-5 + phys_writeb(physAddress+0x0B,(Bit8u)0xEC); // in al,dx + phys_writew(physAddress+0x0C,(Bit16u)0x08A8); // test al,8 + phys_writew(physAddress+0x0E,(Bit16u)0xFB74); // je $-5 + phys_writeb(physAddress+0x10,(Bit8u)0x5A); // pop dx + phys_writeb(physAddress+0x11,(Bit8u)0x58); // pop ax + phys_writeb(physAddress+0x12,(Bit8u)0xCB); //A RETF Instruction + return 19; + case CB_VESA_PM: + if (use_cb) { + phys_writeb(physAddress+0x00,(Bit8u)0xFE); //GRP 4 + phys_writeb(physAddress+0x01,(Bit8u)0x38); //Extra Callback instruction + phys_writew(physAddress+0x02,(Bit16u)callback); //The immediate word + physAddress+=4; + } + phys_writew(physAddress+0x00,(Bit16u)0xC3F6); // test bl, + phys_writeb(physAddress+0x02,(Bit8u)0x80); // 0x80 + phys_writew(physAddress+0x03,(Bit16u)0x1674); // je $+22 + phys_writew(physAddress+0x05,(Bit16u)0x5066); // push ax + phys_writew(physAddress+0x07,(Bit16u)0x5266); // push dx + phys_writew(physAddress+0x09,(Bit16u)0xBA66); // mov dx, + phys_writew(physAddress+0x0B,(Bit16u)0x03DA); // 0x3da + phys_writeb(physAddress+0x0D,(Bit8u)0xEC); // in al,dx + phys_writew(physAddress+0x0E,(Bit16u)0x08A8); // test al,8 + phys_writew(physAddress+0x10,(Bit16u)0xFB75); // jne $-5 + phys_writeb(physAddress+0x12,(Bit8u)0xEC); // in al,dx + phys_writew(physAddress+0x13,(Bit16u)0x08A8); // test al,8 + phys_writew(physAddress+0x15,(Bit16u)0xFB74); // je $-5 + phys_writew(physAddress+0x17,(Bit16u)0x5A66); // pop dx + phys_writew(physAddress+0x19,(Bit16u)0x5866); // pop ax + if (use_cb) + phys_writeb(physAddress+0x1B,(Bit8u)0xC3); //A RETN Instruction + return (use_cb?32:27); default: - E_Exit("CALLBACK:Setup:Illegal type %d",type); + E_Exit("CALLBACK:Setup:Illegal type %" sBitfs(u),type); + //E_Exit("CALLBACK:Setup:Illegal type %d",type); } return 0; } @@ -563,8 +644,6 @@ void CALLBACK_Init(Section* /*sec*/) { /* Default handlers for unhandled interrupts that have to be non-null */ call_default=CALLBACK_Allocate(); CALLBACK_Setup(call_default,&default_handler,CB_IRET,"default"); - call_default2=CALLBACK_Allocate(); - CALLBACK_Setup(call_default2,&default_handler,CB_IRET,"default"); /* Only setup default handler for first part of interrupt table */ for (Bit16u ct=0;ct<0x60;ct++) { @@ -585,10 +664,9 @@ void CALLBACK_Init(Section* /*sec*/) { } // setup a few interrupt handlers that point to bios IRETs by default - real_writed(0,0x0e*4,CALLBACK_RealPointer(call_default2)); //design your own railroad real_writed(0,0x66*4,CALLBACK_RealPointer(call_default)); //war2d real_writed(0,0x67*4,CALLBACK_RealPointer(call_default)); - real_writed(0,0x68*4,CALLBACK_RealPointer(call_default)); + if (machine==MCH_CGA) real_writed(0,0x68*4,0); //Popcorn real_writed(0,0x5c*4,CALLBACK_RealPointer(call_default)); //Network stuff //real_writed(0,0xf*4,0); some games don't like it diff --git a/src/cpu/core_dyn_x86.cpp b/src/cpu/core_dyn_x86.cpp index f63d915..be57252 100644 --- a/src/cpu/core_dyn_x86.cpp +++ b/src/cpu/core_dyn_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -65,6 +65,7 @@ #if C_FPU #define CPU_FPU 1 //Enable FPU escape instructions +#define X86_DYNFPU_DH_ENABLED #endif enum { @@ -154,12 +155,10 @@ static DynReg DynRegs[G_MAX]; #define DREG(_WHICH_) &DynRegs[G_ ## _WHICH_ ] static struct { - Bitu ea,tmpb,tmpd,stack,shift,newesp; + Bit32u ea,tmpb,tmpd,stack,shift,newesp; } extra_regs; -static void IllegalOption(const char* msg) { - E_Exit("DynCore: illegal option in %s",msg); -} +#define IllegalOption(msg) E_Exit("DYNX86: illegal option in " msg) #include "core_dyn_x86/cache.h" @@ -168,17 +167,37 @@ static struct { Bit32u readdata; } core_dyn; -static struct { - Bit32u state[32]; +#if defined(X86_DYNFPU_DH_ENABLED) +static struct dyn_dh_fpu { + Bit16u cw,host_cw; + bool state_used; + // some fields expanded here for alignment purposes + struct { + Bit32u cw; + Bit32u sw; + Bit32u tag; + Bit32u ip; + Bit32u cs; + Bit32u ea; + Bit32u ds; + Bit8u st_reg[8][10]; + } state; FPU_P_Reg temp,temp2; Bit32u dh_fpu_enabled; - Bit32u state_used; - Bit32u cw,host_cw; Bit8u temp_state[128]; } dyn_dh_fpu; +#endif +#define X86 0x01 +#define X86_64 0x02 +#if C_TARGETCPU == X86_64 +#include "core_dyn_x86/risc_x64.h" +#elif C_TARGETCPU == X86 #include "core_dyn_x86/risc_x86.h" +#else +#error DYN_X86 core not supported for this CPU target. +#endif struct DynState { DynReg regs[G_MAX]; @@ -233,44 +252,33 @@ static void dyn_restoreregister(DynReg * src_reg, DynReg * dst_reg) { #include "core_dyn_x86/decoder.h" -#if defined (_MSC_VER) -#define DH_FPU_SAVE_REINIT \ -{ \ - __asm { \ - __asm fnsave dyn_dh_fpu.state[0] \ - } \ - dyn_dh_fpu.state_used=false; \ - dyn_dh_fpu.state[0]|=0x3f; \ -} -#else -#define DH_FPU_SAVE_REINIT \ -{ \ - __asm__ volatile ( \ - "fnsave %0 \n" \ - : "=m" (dyn_dh_fpu.state[0]) \ - : \ - : "memory" \ - ); \ - dyn_dh_fpu.state_used=false; \ - dyn_dh_fpu.state[0]|=0x3f; \ -} -#endif - - Bits CPU_Core_Dyn_X86_Run(void) { + // helper class to auto-save DH_FPU state on function exit + class auto_dh_fpu { + public: + ~auto_dh_fpu(void) { +#if defined(X86_DYNFPU_DH_ENABLED) + if (dyn_dh_fpu.state_used) + gen_dh_fpu_save(); +#endif + }; + }; + auto_dh_fpu fpu_saver; + /* Determine the linear address of CS:EIP */ restart_core: PhysPt ip_point=SegPhys(cs)+reg_eip; - #if C_HEAVY_DEBUG +#if C_DEBUG +#if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; - #endif +#endif +#endif CodePageHandler * chandler=0; if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) { CPU_Exception(cpu.exception.which,cpu.exception.error); goto restart_core; } if (!chandler) { - if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CPU_Core_Normal_Run(); } /* Find correct Dynamic Block to run */ @@ -279,10 +287,11 @@ restart_core: if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) { block=CreateCacheBlock(chandler,ip_point,32); } else { - Bitu old_cycles=CPU_Cycles; + Bit32s old_cycles=CPU_Cycles; CPU_Cycles=1; + // manually save + fpu_saver = auto_dh_fpu(); Bits nc_retcode=CPU_Core_Normal_Run(); - if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT if (!nc_retcode) { CPU_Cycles=old_cycles-1; goto restart_core; @@ -294,41 +303,42 @@ restart_core: run_block: cache.block.running=0; BlockReturn ret=gen_runcode(block->cache.start); +#if C_DEBUG + cycle_count += 32; +#endif switch (ret) { case BR_Iret: +#if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) { - if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return debugCallback; } +#endif #endif if (!GETFLAG(TF)) { if (GETFLAG(IF) && PIC_IRQCheck) { - if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CBRET_NONE; } goto restart_core; } cpudecoder=CPU_Core_Dyn_X86_Trap_Run; - if (!dyn_dh_fpu.state_used) return CBRET_NONE; - DH_FPU_SAVE_REINIT return CBRET_NONE; case BR_Normal: /* Maybe check if we staying in the same page? */ +#if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif #endif goto restart_core; case BR_Cycles: +#if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif - if (!dyn_dh_fpu.state_used) return CBRET_NONE; - DH_FPU_SAVE_REINIT +#endif return CBRET_NONE; case BR_CallBack: - if (!dyn_dh_fpu.state_used) return core_dyn.callback; - DH_FPU_SAVE_REINIT return core_dyn.callback; case BR_SMCBlock: // LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip); @@ -337,21 +347,19 @@ run_block: case BR_Opcode: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; - if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CPU_Core_Normal_Run(); #if (C_DEBUG) case BR_OpcodeFull: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; - if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CPU_Core_Full_Run(); #endif case BR_Link1: case BR_Link2: { - Bitu temp_ip=SegPhys(cs)+reg_eip; + Bit32u temp_ip=SegPhys(cs)+reg_eip; CodePageHandler * temp_handler=(CodePageHandler *)get_tlb_readhandler(temp_ip); - if (temp_handler->flags & PFLAG_HASCODE) { + if (temp_handler->flags & (cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16)) { block=temp_handler->FindCacheBlock(temp_ip & 4095); if (!block) goto restart_core; cache.block.running->LinkTo(ret==BR_Link2,block); @@ -360,12 +368,11 @@ run_block: } goto restart_core; } - if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CBRET_NONE; } Bits CPU_Core_Dyn_X86_Trap_Run(void) { - Bits oldCycles = CPU_Cycles; + Bit32s oldCycles = CPU_Cycles; CPU_Cycles = 1; cpu.trap_skip = false; @@ -438,25 +445,15 @@ void CPU_Core_Dyn_X86_Init(void) { /* Init the generator */ gen_init(); +#if defined(X86_DYNFPU_DH_ENABLED) /* Init the fpu state */ dyn_dh_fpu.dh_fpu_enabled=true; dyn_dh_fpu.state_used=false; dyn_dh_fpu.cw=0x37f; -#if defined (_MSC_VER) - __asm { - __asm finit - __asm fsave dyn_dh_fpu.state[0] - __asm fstcw dyn_dh_fpu.host_cw - } -#else - __asm__ volatile ( - "finit \n" - "fsave %0 \n" - "fstcw %1 \n" - : "=m" (dyn_dh_fpu.state[0]), "=m" (dyn_dh_fpu.host_cw) - : - : "memory" - ); + // FINIT + memset(&dyn_dh_fpu.state, 0, sizeof(dyn_dh_fpu.state)); + dyn_dh_fpu.state.cw = 0x37F; + dyn_dh_fpu.state.tag = 0xFFFF; #endif return; @@ -471,62 +468,10 @@ void CPU_Core_Dyn_X86_Cache_Close(void) { cache_close(); } -void CPU_Core_Dyn_X86_Cache_Reset(void) { - cache_reset(); -} - void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu) { +#if defined(X86_DYNFPU_DH_ENABLED) dyn_dh_fpu.dh_fpu_enabled=dh_fpu; -} - -Bit32u fpu_state[32]; - -void CPU_Core_Dyn_X86_SaveDHFPUState(void) { - if (dyn_dh_fpu.dh_fpu_enabled) { - if (dyn_dh_fpu.state_used!=0) { -#if defined (_MSC_VER) - __asm { - __asm fsave fpu_state[0] - __asm finit - } -#else - __asm__ volatile ( - "fsave %0 \n" - "finit \n" - : "=m" (fpu_state[0]) - : - : "memory" - ); #endif - } - } -} - -void CPU_Core_Dyn_X86_RestoreDHFPUState(void) { - if (dyn_dh_fpu.dh_fpu_enabled) { - if (dyn_dh_fpu.state_used!=0) { -#if defined (_MSC_VER) - __asm { - __asm frstor fpu_state[0] - } -#else - __asm__ volatile ( - "frstor %0 \n" - : - : "m" (fpu_state[0]) - : - ); -#endif - } - } -} - -#else - -void CPU_Core_Dyn_X86_SaveDHFPUState(void) { -} - -void CPU_Core_Dyn_X86_RestoreDHFPUState(void) { } #endif diff --git a/src/cpu/core_dyn_x86/Makefile.am b/src/cpu/core_dyn_x86/Makefile.am index 3d9be09..62cb2bb 100644 --- a/src/cpu/core_dyn_x86/Makefile.am +++ b/src/cpu/core_dyn_x86/Makefile.am @@ -1,2 +1,2 @@ -noinst_HEADERS = cache.h helpers.h decoder.h risc_x86.h string.h \ - dyn_fpu.h dyn_fpu_dh.h \ No newline at end of file +noinst_HEADERS = cache.h helpers.h decoder.h risc_x86.h risc_x64.h string.h \ + dyn_fpu.h dyn_fpu_dh.h diff --git a/src/cpu/core_dyn_x86/cache.h b/src/cpu/core_dyn_x86/cache.h index 2bda44c..6cb3875 100644 --- a/src/cpu/core_dyn_x86/cache.h +++ b/src/cpu/core_dyn_x86/cache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -73,7 +73,7 @@ public: void SetupAt(Bitu _phys_page,PageHandler * _old_pagehandler) { phys_page=_phys_page; old_pagehandler=_old_pagehandler; - flags=old_pagehandler->flags|PFLAG_HASCODE; + flags=old_pagehandler->flags|(cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16); flags&=~PFLAG_WRITEABLE; active_blocks=0; active_count=16; @@ -466,6 +466,10 @@ static INLINE void cache_addd(Bit32u val) { cache.pos+=4; } +static INLINE void cache_addq(Bit64u val) { + *(Bit64u*)cache.pos=val; + cache.pos+=8; +} static void gen_return(BlockReturn retcode); @@ -510,14 +514,14 @@ static void cache_init(bool enable) { #endif if(!cache_code_start_ptr) E_Exit("Allocating dynamic core cache memory failed"); - cache_code=(Bit8u*)(((int)cache_code_start_ptr + PAGESIZE_TEMP-1) & ~(PAGESIZE_TEMP-1)); //MEM LEAK. store old pointer if you want to free it. + cache_code=(Bit8u*)(((Bitu)cache_code_start_ptr + PAGESIZE_TEMP-1) & ~(PAGESIZE_TEMP-1)); //Bitu is same size as a pointer. cache_code_link_blocks=cache_code; cache_code+=PAGESIZE_TEMP; #if (C_HAVE_MPROTECT) if(mprotect(cache_code_link_blocks,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP,PROT_WRITE|PROT_READ|PROT_EXEC)) - LOG_MSG("Setting excute permission on the code cache has failed!"); + LOG_MSG("Setting execute permission on the code cache has failed!"); #endif CacheBlock * block=cache_getblock(); cache.block.first=block; @@ -569,75 +573,3 @@ static void cache_close(void) { cache_code_link_blocks = NULL; cache_initialized = false; */ } - -static void cache_reset(void) { - if (cache_initialized) { - for (;;) { - if (cache.used_pages) { - CodePageHandler * cpage=cache.used_pages; - CodePageHandler * npage=cache.used_pages->next; - cpage->ClearRelease(); - delete cpage; - cache.used_pages=npage; - } else break; - } - - if (cache_blocks == NULL) { - cache_blocks=(CacheBlock*)malloc(CACHE_BLOCKS*sizeof(CacheBlock)); - if(!cache_blocks) E_Exit("Allocating cache_blocks has failed"); - } - memset(cache_blocks,0,sizeof(CacheBlock)*CACHE_BLOCKS); - cache.block.free=&cache_blocks[0]; - for (Bits i=0;icache.start=&cache_code[0]; - block->cache.size=CACHE_TOTAL; - block->cache.next=0; //Last block in the list - - /* Setup the default blocks for block linkage returns */ - cache.pos=&cache_code_link_blocks[0]; - link_blocks[0].cache.start=cache.pos; - gen_return(BR_Link1); - cache.pos=&cache_code_link_blocks[32]; - link_blocks[1].cache.start=cache.pos; - gen_return(BR_Link2); - cache.free_pages=0; - cache.last_page=0; - cache.used_pages=0; - /* Setup the code pages */ - for (Bitu i=0;inext=cache.free_pages; - cache.free_pages=newpage; - } - } -} diff --git a/src/cpu/core_dyn_x86/decoder.h b/src/cpu/core_dyn_x86/decoder.h index a9f90d0..11e9aa1 100644 --- a/src/cpu/core_dyn_x86/decoder.h +++ b/src/cpu/core_dyn_x86/decoder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,13 +11,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define X86_DYNFPU_DH_ENABLED #define X86_INLINED_MEMACCESS @@ -53,19 +52,26 @@ static struct DynDecode { static bool MakeCodePage(Bitu lin_addr,CodePageHandler * &cph) { Bit8u rdval; + const Bitu cflag = cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16; //Ensure page contains memory: if (GCC_UNLIKELY(mem_readb_checked(lin_addr,&rdval))) return true; PageHandler * handler=get_tlb_readhandler(lin_addr); if (handler->flags & PFLAG_HASCODE) { cph=( CodePageHandler *)handler; - return false; + if (handler->flags & cflag) return false; + cph->ClearRelease(); + cph=0; + handler=get_tlb_readhandler(lin_addr); } if (handler->flags & PFLAG_NOCODE) { if (PAGING_ForcePageInit(lin_addr)) { handler=get_tlb_readhandler(lin_addr); if (handler->flags & PFLAG_HASCODE) { cph=( CodePageHandler *)handler; - return false; + if (handler->flags & cflag) return false; + cph->ClearRelease(); + cph=0; + handler=get_tlb_readhandler(lin_addr); } } if (handler->flags & PFLAG_NOCODE) { @@ -325,7 +331,7 @@ static BlockReturn DynRunException(Bit32u eip_add,Bit32u cycle_sub,Bit32u dflags } static void dyn_check_bool_exception(DynReg * check) { - gen_dop_byte(DOP_OR,check,0,check,0); + gen_dop_byte(DOP_TEST,check,0,check,0); save_info[used_save_info].branch_pos=gen_create_branch_long(BR_NZ); dyn_savestate(&save_info[used_save_info].state); if (!decode.cycles) decode.cycles++; @@ -337,7 +343,7 @@ static void dyn_check_bool_exception(DynReg * check) { } static void dyn_check_bool_exception_al(void) { - cache_addw(0xc00a); // or al, al + cache_addw(0xC084); // test al,al save_info[used_save_info].branch_pos=gen_create_branch_long(BR_NZ); dyn_savestate(&save_info[used_save_info].state); if (!decode.cycles) decode.cycles++; @@ -352,7 +358,7 @@ static void dyn_check_bool_exception_al(void) { static void dyn_check_irqrequest(void) { gen_load_host(&PIC_IRQCheck,DREG(TMPB),4); - gen_dop_word(DOP_OR,true,DREG(TMPB),DREG(TMPB)); + gen_dop_word(DOP_TEST,true,DREG(TMPB),DREG(TMPB)); save_info[used_save_info].branch_pos=gen_create_branch_long(BR_NZ); gen_releasereg(DREG(TMPB)); dyn_savestate(&save_info[used_save_info].state); @@ -396,21 +402,29 @@ static void dyn_fill_blocks(void) { dyn_save_critical_regs(); gen_return(BR_Cycles); break; +#ifdef X86_DYNFPU_DH_ENABLED case fpu_restore: dyn_loadstate(&save_info[sct].state); - gen_load_host(&dyn_dh_fpu.state_used,DREG(TMPB),4); - gen_sop_word(SOP_INC,true,DREG(TMPB)); - GenReg * gr1=FindDynReg(DREG(TMPB)); +#if C_TARGETCPU == X86 + cache_addb(0xd9); // FNSTCW fpu.host_cw + cache_addb(0x3d); + cache_addd((Bit32u)(&dyn_dh_fpu.host_cw)); cache_addb(0xdd); // FRSTOR fpu.state (fpu_restore) cache_addb(0x25); - cache_addd((Bit32u)(&(dyn_dh_fpu.state[0]))); - cache_addb(0x89); // mov fpu.state_used,1 - cache_addb(0x05|(gr1->index<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.state_used))); - gen_releasereg(DREG(TMPB)); + cache_addd((Bit32u)(&dyn_dh_fpu.state)); + cache_addb(0xC6); // mov byte [fpu.state_used], 1 + cache_addb(0x05); + cache_addd((Bit32u)(&dyn_dh_fpu.state_used)); + cache_addb(1); +#else // X86_64 + opcode(7).setabsaddr(&dyn_dh_fpu.host_cw).Emit8(0xD9); // FNSTCW [&fpu.host_cw] + opcode(4).setabsaddr(&dyn_dh_fpu.state).Emit8(0xDD); // FRSTOR [&fpu.state] + opcode(0).setimm(1,1).setabsaddr(&dyn_dh_fpu.state_used).Emit8(0xC6); // mov byte[], imm8 +#endif dyn_synchstate(&save_info[sct].state); gen_create_jump(save_info[sct].return_pos); break; +#endif } } used_save_info=0; @@ -418,13 +432,13 @@ static void dyn_fill_blocks(void) { #if !defined(X86_INLINED_MEMACCESS) -static void dyn_read_byte(DynReg * addr,DynReg * dst,Bitu high) { +static void dyn_read_byte(DynReg * addr,DynReg * dst,bool high) { gen_protectflags(); - gen_call_function((void *)&mem_readb_checked,"%Dd%Id",addr,&core_dyn.readdata); + gen_call_function((void *)&mem_readb_checked,"%Dd%Ip",addr,&core_dyn.readdata); dyn_check_bool_exception_al(); - gen_mov_host(&core_dyn.readdata,dst,1,high); + gen_mov_host(&core_dyn.readdata,dst,1,high?4:0); } -static void dyn_write_byte(DynReg * addr,DynReg * val,Bitu high) { +static void dyn_write_byte(DynReg * addr,DynReg * val,bool high) { gen_protectflags(); if (high) gen_call_function((void *)&mem_writeb_checked,"%Dd%Dh",addr,val); else gen_call_function((void *)&mem_writeb_checked,"%Dd%Dd",addr,val); @@ -432,8 +446,8 @@ static void dyn_write_byte(DynReg * addr,DynReg * val,Bitu high) { } static void dyn_read_word(DynReg * addr,DynReg * dst,bool dword) { gen_protectflags(); - if (dword) gen_call_function((void *)&mem_readd_checked,"%Dd%Id",addr,&core_dyn.readdata); - else gen_call_function((void *)&mem_readw_checked,"%Dd%Id",addr,&core_dyn.readdata); + if (dword) gen_call_function((void *)&mem_readd_checked,"%Dd%Ip",addr,&core_dyn.readdata); + else gen_call_function((void *)&mem_readw_checked,"%Dd%Ip",addr,&core_dyn.readdata); dyn_check_bool_exception_al(); gen_mov_host(&core_dyn.readdata,dst,dword?4:2); } @@ -443,33 +457,34 @@ static void dyn_write_word(DynReg * addr,DynReg * val,bool dword) { else gen_call_function((void *)&mem_writew_checked,"%Dd%Dd",addr,val); dyn_check_bool_exception_al(); } -static void dyn_read_byte_release(DynReg * addr,DynReg * dst,Bitu high) { +static void dyn_read_byte_release(DynReg * addr,DynReg * dst,bool high) { gen_protectflags(); - gen_call_function((void *)&mem_readb_checked,"%Ddr%Id",addr,&core_dyn.readdata); + gen_call_function((void *)&mem_readb_checked,"%Drd%Ip",addr,&core_dyn.readdata); dyn_check_bool_exception_al(); - gen_mov_host(&core_dyn.readdata,dst,1,high); + gen_mov_host(&core_dyn.readdata,dst,1,high?4:0); } -static void dyn_write_byte_release(DynReg * addr,DynReg * val,Bitu high) { +static void dyn_write_byte_release(DynReg * addr,DynReg * val,bool high) { gen_protectflags(); - if (high) gen_call_function((void *)&mem_writeb_checked,"%Ddr%Dh",addr,val); - else gen_call_function((void *)&mem_writeb_checked,"%Ddr%Dd",addr,val); + if (high) gen_call_function((void *)&mem_writeb_checked,"%Drd%Dh",addr,val); + else gen_call_function((void *)&mem_writeb_checked,"%Drd%Dd",addr,val); dyn_check_bool_exception_al(); } static void dyn_read_word_release(DynReg * addr,DynReg * dst,bool dword) { gen_protectflags(); - if (dword) gen_call_function((void *)&mem_readd_checked,"%Ddr%Id",addr,&core_dyn.readdata); - else gen_call_function((void *)&mem_readw_checked,"%Ddr%Id",addr,&core_dyn.readdata); + if (dword) gen_call_function((void *)&mem_readd_checked,"%Drd%Ip",addr,&core_dyn.readdata); + else gen_call_function((void *)&mem_readw_checked,"%Drd%Ip",addr,&core_dyn.readdata); dyn_check_bool_exception_al(); gen_mov_host(&core_dyn.readdata,dst,dword?4:2); } static void dyn_write_word_release(DynReg * addr,DynReg * val,bool dword) { gen_protectflags(); - if (dword) gen_call_function((void *)&mem_writed_checked,"%Ddr%Dd",addr,val); - else gen_call_function((void *)&mem_writew_checked,"%Ddr%Dd",addr,val); + if (dword) gen_call_function((void *)&mem_writed_checked,"%Drd%Dd",addr,val); + else gen_call_function((void *)&mem_writew_checked,"%Drd%Dd",addr,val); dyn_check_bool_exception_al(); } #else +#if C_TARGETCPU == X86 static void dyn_read_intro(DynReg * addr,bool release_addr=true) { gen_protectflags(); @@ -495,12 +510,12 @@ static void dyn_read_intro(DynReg * addr,bool release_addr=true) { cache_addw(0xc18b); // mov eax,ecx } -bool mem_readb_checked_dcx86(PhysPt address) { +static bool mem_readb_checked_dcx86(PhysPt address) { return get_tlb_readhandler(address)->readb_checked(address, (Bit8u*)(&core_dyn.readdata)); } -static void dyn_read_byte(DynReg * addr,DynReg * dst,Bitu high) { - dyn_read_intro(addr,false); +static void dyn_read_byte(DynReg * addr,DynReg * dst,bool high,bool release=false) { + dyn_read_intro(addr,release); cache_addw(0xe8c1); // shr eax,0x0c cache_addb(0x0c); @@ -537,45 +552,7 @@ static void dyn_read_byte(DynReg * addr,DynReg * dst,Bitu high) { dst->flags|=DYNFLG_CHANGED; } -static void dyn_read_byte_release(DynReg * addr,DynReg * dst,Bitu high) { - dyn_read_intro(addr); - - cache_addw(0xe8c1); // shr eax,0x0c - cache_addb(0x0c); - cache_addw(0x048b); // mov eax,paging.tlb.read[eax*TYPE Bit32u] - cache_addb(0x85); - cache_addd((Bit32u)(&paging.tlb.read[0])); - cache_addw(0xc085); // test eax,eax - Bit8u* je_loc=gen_create_branch(BR_Z); - - - cache_addw(0x048a); // mov al,[eax+ecx] - cache_addb(0x08); - - Bit8u* jmp_loc=gen_create_jump(); - gen_fill_branch(je_loc); - cache_addb(0x51); // push ecx - cache_addb(0xe8); - cache_addd(((Bit32u)&mem_readb_checked_dcx86) - (Bit32u)cache.pos-4); - cache_addw(0xc483); // add esp,4 - cache_addb(0x04); - cache_addw(0x012c); // sub al,1 - - dyn_check_bool_exception_ne(); - - cache_addw(0x058a); //mov al,[] - cache_addd((Bit32u)(&core_dyn.readdata)); - - gen_fill_jump(jmp_loc); - - x86gen.regs[X86_REG_EAX]->notusable=true; - GenReg * genreg=FindDynReg(dst); - x86gen.regs[X86_REG_EAX]->notusable=false; - cache_addw(0xc08a+(genreg->index<<11)+(high?0x2000:0)); - dst->flags|=DYNFLG_CHANGED; -} - -bool mem_readd_checked_dcx86(PhysPt address) { +static bool mem_readd_checked_dcx86(PhysPt address) { if ((address & 0xfff)<0xffd) { HostPt tlb_addr=get_tlb_read(address); if (tlb_addr) { @@ -587,96 +564,70 @@ bool mem_readd_checked_dcx86(PhysPt address) { } else return mem_unalignedreadd_checked(address, &core_dyn.readdata); } -static void dyn_read_word(DynReg * addr,DynReg * dst,bool dword) { - if (dword) { - dyn_read_intro(addr,false); - - cache_addw(0xe8d1); // shr eax,0x1 - Bit8u* jb_loc1=gen_create_branch(BR_B); - cache_addw(0xe8d1); // shr eax,0x1 - Bit8u* jb_loc2=gen_create_branch(BR_B); - cache_addw(0xe8c1); // shr eax,0x0a - cache_addb(0x0a); - cache_addw(0x048b); // mov eax,paging.tlb.read[eax*TYPE Bit32u] - cache_addb(0x85); - cache_addd((Bit32u)(&paging.tlb.read[0])); - cache_addw(0xc085); // test eax,eax - Bit8u* je_loc=gen_create_branch(BR_Z); - - GenReg * genreg=FindDynReg(dst,true); - - cache_addw(0x048b+(genreg->index <<(8+3))); // mov dest,[eax+ecx] - cache_addb(0x08); - - Bit8u* jmp_loc=gen_create_jump(); - gen_fill_branch(jb_loc1); - gen_fill_branch(jb_loc2); - gen_fill_branch(je_loc); - cache_addb(0x51); // push ecx - cache_addb(0xe8); - cache_addd(((Bit32u)&mem_readd_checked_dcx86) - (Bit32u)cache.pos-4); - cache_addw(0xc483); // add esp,4 - cache_addb(0x04); - cache_addw(0x012c); // sub al,1 - - dyn_check_bool_exception_ne(); - - gen_mov_host(&core_dyn.readdata,dst,4); - dst->flags|=DYNFLG_CHANGED; - - gen_fill_jump(jmp_loc); - } else { - gen_protectflags(); - gen_call_function((void *)&mem_readw_checked,"%Dd%Id",addr,&core_dyn.readdata); - dyn_check_bool_exception_al(); - gen_mov_host(&core_dyn.readdata,dst,2); - } +static bool mem_readw_checked_dcx86(PhysPt address) { + if ((address & 0xfff)<0xfff) { + HostPt tlb_addr=get_tlb_read(address); + if (tlb_addr) { + *(Bit16u*)&core_dyn.readdata=host_readw(tlb_addr+address); + return false; + } else { + return get_tlb_readhandler(address)->readw_checked(address, (Bit16u*)&core_dyn.readdata); + } + } else return mem_unalignedreadw_checked(address, (Bit16u*)&core_dyn.readdata); } -static void dyn_read_word_release(DynReg * addr,DynReg * dst,bool dword) { - if (dword) { - dyn_read_intro(addr); +static void dyn_read_word(DynReg * addr,DynReg * dst,bool dword,bool release=false) { + dyn_read_intro(addr,release); - cache_addw(0xe8d1); // shr eax,0x1 - Bit8u* jb_loc1=gen_create_branch(BR_B); - cache_addw(0xe8d1); // shr eax,0x1 - Bit8u* jb_loc2=gen_create_branch(BR_B); - cache_addw(0xe8c1); // shr eax,0x0a - cache_addb(0x0a); - cache_addw(0x048b); // mov eax,paging.tlb.read[eax*TYPE Bit32u] - cache_addb(0x85); - cache_addd((Bit32u)(&paging.tlb.read[0])); - cache_addw(0xc085); // test eax,eax - Bit8u* je_loc=gen_create_branch(BR_Z); - - GenReg * genreg=FindDynReg(dst,true); - - cache_addw(0x048b+(genreg->index <<(8+3))); // mov dest,[eax+ecx] - cache_addb(0x08); - - Bit8u* jmp_loc=gen_create_jump(); - gen_fill_branch(jb_loc1); - gen_fill_branch(jb_loc2); - gen_fill_branch(je_loc); - cache_addb(0x51); // push ecx - cache_addb(0xe8); - cache_addd(((Bit32u)&mem_readd_checked_dcx86) - (Bit32u)cache.pos-4); - cache_addw(0xc483); // add esp,4 - cache_addb(0x04); - cache_addw(0x012c); // sub al,1 - - dyn_check_bool_exception_ne(); - - gen_mov_host(&core_dyn.readdata,dst,4); - dst->flags|=DYNFLG_CHANGED; - - gen_fill_jump(jmp_loc); - } else { - gen_protectflags(); - gen_call_function((void *)&mem_readw_checked,"%Ddr%Id",addr,&core_dyn.readdata); - dyn_check_bool_exception_al(); - gen_mov_host(&core_dyn.readdata,dst,2); + if (!dword) { + x86gen.regs[X86_REG_EAX]->notusable=true; + x86gen.regs[X86_REG_ECX]->notusable=true; } + GenReg * genreg=FindDynReg(dst,dword); + if (!dword) { + x86gen.regs[X86_REG_EAX]->notusable=false; + x86gen.regs[X86_REG_ECX]->notusable=false; + } + + cache_addw(0xc8c1); // ror eax, 12 + cache_addb(0x0c); + cache_addb(0x3d); // cmp eax, 0xFFD00000/0xFFF00000 + cache_addd(dword ? 0xffd00000:0xfff00000); + Bit8u* jb_loc1=gen_create_branch(BR_NB); + cache_addb(0x25); // and eax, 0x000FFFFF + cache_addd(0x000fffff); + cache_addw(0x048b); // mov eax,paging.tlb.read[eax*TYPE Bit32u] + cache_addb(0x85); + cache_addd((Bit32u)(&paging.tlb.read[0])); + cache_addw(0xc085); // test eax,eax + Bit8u* je_loc=gen_create_branch(BR_Z); + + if (!dword) cache_addb(0x66); + cache_addw(0x048b+(genreg->index <<(8+3))); // mov dest,[eax+ecx] + cache_addb(0x08); + + Bit8u* jmp_loc=gen_create_jump(); + gen_fill_branch(jb_loc1); + gen_fill_branch(je_loc); + + if (!dword) { + cache_addw(0x0589+(genreg->index<<11)); // mov [core_dyn.readdata], dst + cache_addd((Bit32u)&core_dyn.readdata); + } + cache_addb(0x51); // push ecx + cache_addb(0xe8); + if (dword) cache_addd(((Bit32u)&mem_readd_checked_dcx86) - (Bit32u)cache.pos-4); + else cache_addd(((Bit32u)&mem_readw_checked_dcx86) - (Bit32u)cache.pos-4); + cache_addw(0xc483); // add esp,4 + cache_addb(0x04); + cache_addw(0x012c); // sub al,1 + + dyn_check_bool_exception_ne(); + + gen_mov_host(&core_dyn.readdata,dst,4); + dst->flags|=DYNFLG_CHANGED; + + gen_fill_jump(jmp_loc); } static void dyn_write_intro(DynReg * addr,bool release_addr=true) { @@ -710,52 +661,13 @@ static void dyn_write_intro(DynReg * addr,bool release_addr=true) { cache_addw(0xc88b); // mov ecx,eax } -static void dyn_write_byte(DynReg * addr,DynReg * val,bool high) { - dyn_write_intro(addr,false); +static void dyn_write_byte(DynReg * addr,DynReg * val,bool high,bool release=false) { + dyn_write_intro(addr,release); GenReg * genreg=FindDynReg(val); cache_addw(0xe9c1); // shr ecx,0x0c cache_addb(0x0c); - cache_addw(0x0c8b); // mov ecx,paging.tlb.read[ecx*TYPE Bit32u] - cache_addb(0x8d); - cache_addd((Bit32u)(&paging.tlb.write[0])); - cache_addw(0xc985); // test ecx,ecx - Bit8u* je_loc=gen_create_branch(BR_Z); - - cache_addw(0x0488+(genreg->index<<11)+(high?0x2000:0)); // mov [eax+ecx],reg - cache_addb(0x08); - - Bit8u* jmp_loc=gen_create_jump(); - gen_fill_branch(je_loc); - - if (GCC_UNLIKELY(high)) cache_addw(0xe086+((genreg->index+(genreg->index<<3))<<8)); - cache_addb(0x52); // push edx - cache_addb(0x50+genreg->index); - cache_addb(0x50); // push eax - if (GCC_UNLIKELY(high)) cache_addw(0xe086+((genreg->index+(genreg->index<<3))<<8)); - cache_addb(0xe8); - cache_addd(((Bit32u)&mem_writeb_checked) - (Bit32u)cache.pos-4); - cache_addw(0xc483); // add esp,8 - cache_addb(0x08); - cache_addw(0x012c); // sub al,1 - cache_addb(0x5a); // pop edx - - // Restore registers to be used again - x86gen.regs[X86_REG_EAX]->notusable=false; - x86gen.regs[X86_REG_ECX]->notusable=false; - - dyn_check_bool_exception_ne(); - - gen_fill_jump(jmp_loc); -} - -static void dyn_write_byte_release(DynReg * addr,DynReg * val,bool high) { - dyn_write_intro(addr); - - GenReg * genreg=FindDynReg(val); - cache_addw(0xe9c1); // shr ecx,0x0c - cache_addb(0x0c); - cache_addw(0x0c8b); // mov ecx,paging.tlb.read[ecx*TYPE Bit32u] + cache_addw(0x0c8b); // mov ecx,paging.tlb.write[ecx*TYPE Bit32u] cache_addb(0x8d); cache_addd((Bit32u)(&paging.tlb.write[0])); cache_addw(0xc985); // test ecx,ecx @@ -788,104 +700,286 @@ static void dyn_write_byte_release(DynReg * addr,DynReg * val,bool high) { gen_fill_jump(jmp_loc); } -static void dyn_write_word(DynReg * addr,DynReg * val,bool dword) { +static void dyn_write_word(DynReg * addr,DynReg * val,bool dword,bool release=false) { + dyn_write_intro(addr,release); + + GenReg * genreg=FindDynReg(val); + cache_addw(0xc9c1); // ror ecx, 12 + cache_addb(0x0c); + cache_addw(0xf981); // cmp ecx, 0xFFD00000/0xFFF00000 + cache_addd(dword ? 0xffd00000:0xfff00000); + Bit8u* jb_loc1=gen_create_branch(BR_NB); + cache_addw(0xe181); // and ecx, 0x000FFFFF + cache_addd(0x000fffff); + cache_addw(0x0c8b); // mov ecx,paging.tlb.write[ecx*TYPE Bit32u] + cache_addb(0x8d); + cache_addd((Bit32u)(&paging.tlb.write[0])); + cache_addw(0xc985); // test ecx,ecx + Bit8u* je_loc=gen_create_branch(BR_Z); + + if (!dword) cache_addb(0x66); + cache_addw(0x0489+(genreg->index <<(8+3))); // mov [eax+ecx],reg + cache_addb(0x08); + + Bit8u* jmp_loc=gen_create_jump(); + gen_fill_branch(jb_loc1); + gen_fill_branch(je_loc); + + cache_addb(0x52); // push edx + cache_addb(0x50+genreg->index); + cache_addb(0x50); // push eax + cache_addb(0xe8); + if (dword) cache_addd(((Bit32u)&mem_writed_checked) - (Bit32u)cache.pos-4); + else cache_addd(((Bit32u)&mem_writew_checked) - (Bit32u)cache.pos-4); + cache_addw(0xc483); // add esp,8 + cache_addb(0x08); + cache_addw(0x012c); // sub al,1 + cache_addb(0x5a); // pop edx + + // Restore registers to be used again + x86gen.regs[X86_REG_EAX]->notusable=false; + x86gen.regs[X86_REG_ECX]->notusable=false; + + dyn_check_bool_exception_ne(); + + gen_fill_jump(jmp_loc); +} + +#else // X86_64 +static bool mem_readd_checked_dcx64(PhysPt address, Bit32u* dst) { + return get_tlb_readhandler(address)->readd_checked(address, dst); +} +static bool mem_readw_checked_dcx64(PhysPt address, Bit16u* dst) { + return get_tlb_readhandler(address)->readw_checked(address, dst); +} +static bool mem_writed_checked_dcx64(PhysPt address, Bitu val) { + return get_tlb_writehandler(address)->writed_checked(address, val); +} +static bool mem_writew_checked_dcx64(PhysPt address, Bitu val) { + return get_tlb_writehandler(address)->writew_checked(address, val); +} +static bool mem_readb_checked_dcx64(PhysPt address, Bit8u* dst) { + return get_tlb_readhandler(address)->readb_checked(address, dst); +} +static bool mem_writeb_checked_dcx64(PhysPt address, Bitu val) { + return get_tlb_writehandler(address)->writeb_checked(address, val); +} + +static void dyn_read_word(DynReg * addr,DynReg * dst,bool dword,bool release=false) { + DynState callstate; + Bit8u tmp; + + gen_protectflags(); + GenReg *gensrc = FindDynReg(addr); + if (dword && release) gen_releasereg(addr); + GenReg *gendst = FindDynReg(dst,dword); + if (!dword && release) gen_releasereg(addr); + gensrc->notusable = true; + x64gen.regs[reg_args[0]]->notusable=true; + x64gen.regs[reg_args[1]]->notusable=true; + tmp = GetNextReg(); + gensrc->notusable = false; + x64gen.regs[reg_args[0]]->notusable=false; + x64gen.regs[reg_args[1]]->notusable=false; + dyn_savestate(&callstate); + + Bit8u *page_brk; + opcode(tmp).set64().setea(gensrc->index,-1,0,dword?3:1).Emit8(0x8D); // lea tmp, [dst+(dword?3:1)] if (dword) { - dyn_write_intro(addr,false); - - GenReg * genreg=FindDynReg(val); - cache_addw(0xe9d1); // shr ecx,0x1 - Bit8u* jb_loc1=gen_create_branch(BR_B); - cache_addw(0xe9d1); // shr ecx,0x1 - Bit8u* jb_loc2=gen_create_branch(BR_B); - cache_addw(0xe9c1); // shr ecx,0x0a - cache_addb(0x0a); - cache_addw(0x0c8b); // mov ecx,paging.tlb.read[ecx*TYPE Bit32u] - cache_addb(0x8d); - cache_addd((Bit32u)(&paging.tlb.write[0])); - cache_addw(0xc985); // test ecx,ecx - Bit8u* je_loc=gen_create_branch(BR_Z); - - cache_addw(0x0489+(genreg->index <<(8+3))); // mov [eax+ecx],reg - cache_addb(0x08); - - Bit8u* jmp_loc=gen_create_jump(); - gen_fill_branch(jb_loc1); - gen_fill_branch(jb_loc2); - gen_fill_branch(je_loc); - - cache_addb(0x52); // push edx - cache_addb(0x50+genreg->index); - cache_addb(0x50); // push eax - cache_addb(0xe8); - cache_addd(((Bit32u)&mem_writed_checked) - (Bit32u)cache.pos-4); - cache_addw(0xc483); // add esp,8 - cache_addb(0x08); - cache_addw(0x012c); // sub al,1 - cache_addb(0x5a); // pop edx - - // Restore registers to be used again - x86gen.regs[X86_REG_EAX]->notusable=false; - x86gen.regs[X86_REG_ECX]->notusable=false; - - dyn_check_bool_exception_ne(); - - gen_fill_jump(jmp_loc); + opcode(4).set64().setimm(~0xFFF,4).setrm(tmp).Emit8(0x81); // and tmp, ~0xFFF + opcode(gensrc->index).set64().setrm(tmp).Emit8(0x39); // cmp tmp,src + page_brk=gen_create_branch(BR_NBE); } else { - gen_protectflags(); - gen_call_function((void *)&mem_writew_checked,"%Dd%Dd",addr,val); - dyn_check_bool_exception_al(); + opcode(0,false).setimm(0xFFF,2).setrm(tmp).Emit8(0xF7); // test tmpw,0xFFF + page_brk=gen_create_branch(BR_Z); } + + opcode(5).setrm(tmp).setimm(12,1).Emit8(0xC1); // shr tmpd,12 + // mov tmp, [8*tmp+paging.tlb.read(rbp)] + opcode(tmp).set64().setea(5,tmp,3,(Bits)paging.tlb.read-(Bits)&cpu_regs).Emit8(0x8B); + opcode(tmp).set64().setrm(tmp).Emit8(0x85); // test tmp,tmp + Bit8u *nomap=gen_create_branch(BR_Z); + //mov dst, [tmp+src] + opcode(gendst->index,dword).setea(tmp,gensrc->index).Emit8(0x8B); + Bit8u* jmp_loc = gen_create_short_jump(); + + gen_fill_branch(page_brk); + gen_load_imm(tmp, (Bitu)(dword?(void*)mem_unalignedreadd_checked:(void*)mem_unalignedreadw_checked)); + Bit8u* page_jmp = gen_create_short_jump(); + + gen_fill_branch(nomap); + gen_load_imm(tmp, (Bitu)(dword?(void*)mem_readd_checked_dcx64:(void*)mem_readw_checked_dcx64)); + gen_fill_short_jump(page_jmp); + + if (gensrc->index != ARG0_REG) { + x64gen.regs[reg_args[0]]->Clear(); + opcode(ARG0_REG).setrm(gensrc->index).Emit8(0x8B); + } + gendst->Clear(); + x64gen.regs[reg_args[1]]->Clear(); + gen_load_imm(ARG1_REG, (Bitu)dst->data); + gen_call_ptr(NULL, tmp); + dyn_check_bool_exception_al(); + + dyn_synchstate(&callstate); + dst->flags |= DYNFLG_CHANGED; + gen_fill_short_jump(jmp_loc); } +static void dyn_read_byte(DynReg * addr,DynReg * dst,bool high,bool release=false) { + DynState callstate; + Bit8u tmp; + + gen_protectflags(); + GenReg *gensrc = FindDynReg(addr); + GenReg *gendst = FindDynReg(dst); + tmp = GetNextReg(high); + if (release) gen_releasereg(addr); + dyn_savestate(&callstate); + + if (gendst->index>3) IllegalOption("dyn_read_byte"); + + opcode(tmp).setrm(gensrc->index).Emit8(0x8B); // mov tmp, src + opcode(5).setrm(tmp).setimm(12,1).Emit8(0xC1); // shr tmp,12 + // mov tmp, [8*tmp+paging.tlb.read(rbp)] + opcode(tmp).set64().setea(5,tmp,3,(Bits)paging.tlb.read-(Bits)&cpu_regs).Emit8(0x8B); + opcode(tmp).set64().setrm(tmp).Emit8(0x85); // test tmp,tmp + Bit8u *nomap=gen_create_branch(BR_Z); + + int src = gensrc->index; + if (high && src>=8) { // can't use REX prefix with high-byte reg + opcode(tmp).set64().setrm(src).Emit8(0x03); // add tmp, src + src = -1; + } + // mov dst, byte [tmp+src] + opcode(gendst->index,true,high?4:0).setea(tmp,src).Emit8(0x8A); + Bit8u* jmp_loc=gen_create_short_jump(); + + gen_fill_branch(nomap); + if (gensrc->index != ARG0_REG) { + x64gen.regs[reg_args[0]]->Clear(); + opcode(ARG0_REG).setrm(gensrc->index).Emit8(0x8B); // mov ARG0,src + } + x64gen.regs[reg_args[1]]->Clear(); + gen_load_imm(ARG1_REG, (Bitu)(high?((Bit8u*)dst->data)+1:dst->data)); + gendst->Clear(); + gen_call_ptr((void*)mem_readb_checked_dcx64); + dyn_check_bool_exception_al(); + + dyn_synchstate(&callstate); + dst->flags |= DYNFLG_CHANGED; + gen_fill_short_jump(jmp_loc); +} + +static void dyn_write_word(DynReg * addr,DynReg * val,bool dword,bool release=false) { + DynState callstate; + Bit8u tmp; + + gen_protectflags(); + GenReg *gendst = FindDynReg(addr); + GenReg *genval = FindDynReg(val); + x64gen.regs[reg_args[0]]->notusable=true; + x64gen.regs[reg_args[1]]->notusable=true; + tmp = GetNextReg(); + x64gen.regs[reg_args[0]]->notusable=false; + x64gen.regs[reg_args[1]]->notusable=false; + if (release) gen_releasereg(addr); + dyn_savestate(&callstate); + + Bit8u *page_brk; + opcode(tmp).set64().setea(gendst->index,-1,0,dword?3:1).Emit8(0x8D); // lea tmp, [dst+(dword?3:1)] + if (dword) { + opcode(4).set64().setimm(~0xFFF,4).setrm(tmp).Emit8(0x81); // and tmp, ~0xFFF + opcode(gendst->index).set64().setrm(tmp).Emit8(0x39); // cmp tmp,dst + page_brk=gen_create_branch(BR_NBE); + } else { + opcode(0,false).setimm(0xFFF,2).setrm(tmp).Emit8(0xF7); // test tmpw,0xFFF + page_brk=gen_create_branch(BR_Z); + } + + opcode(5).setrm(tmp).setimm(12,1).Emit8(0xC1); // shr tmpd,12 + // mov tmp, [8*tmp+paging.tlb.write(rbp)] + opcode(tmp).set64().setea(5,tmp,3,(Bits)paging.tlb.write-(Bits)&cpu_regs).Emit8(0x8B); + opcode(tmp).set64().setrm(tmp).Emit8(0x85); // test tmp,tmp + Bit8u *nomap=gen_create_branch(BR_Z); + //mov [tmp+src], dst + opcode(genval->index,dword).setea(tmp,gendst->index).Emit8(0x89); + Bit8u* jmp_loc = gen_create_short_jump(); + + gen_fill_branch(page_brk); + gen_load_imm(tmp, (Bitu)(dword?(void*)mem_unalignedwrited_checked:(void*)mem_unalignedwritew_checked)); + Bit8u* page_jmp = gen_create_short_jump(); + gen_fill_branch(nomap); + gen_load_imm(tmp, (Bitu)(dword?(void*)mem_writed_checked_dcx64:(void*)mem_writew_checked_dcx64)); + gen_fill_short_jump(page_jmp); + + if (gendst->index != ARG0_REG) { + x64gen.regs[reg_args[0]]->Clear(); + opcode(ARG0_REG).setrm(gendst->index).Emit8(0x8B); + } + gen_load_arg_reg(1, val, dword ? "d":"w"); + gen_call_ptr(NULL, tmp); + dyn_check_bool_exception_al(); + dyn_synchstate(&callstate); + gen_fill_short_jump(jmp_loc); +} + +static void dyn_write_byte(DynReg * addr,DynReg * val,bool high,bool release=false) { + DynState callstate; + Bit8u tmp; + + gen_protectflags(); + GenReg *gendst = FindDynReg(addr); + GenReg *genval = FindDynReg(val); + tmp = GetNextReg(high); + if (release) gen_releasereg(addr); + dyn_savestate(&callstate); + + if (genval->index>3) IllegalOption("dyn_write_byte"); + + opcode(tmp).setrm(gendst->index).Emit8(0x8B); // mov tmpd, dst + opcode(5).setrm(tmp).setimm(12,1).Emit8(0xC1); // shr tmpd,12 + // mov tmp, [8*tmp+paging.tlb.write(rbp)] + opcode(tmp).set64().setea(5,tmp,3,(Bits)paging.tlb.write-(Bits)&cpu_regs).Emit8(0x8B); + opcode(tmp).set64().setrm(tmp).Emit8(0x85); // test tmp,tmp + Bit8u *nomap=gen_create_branch(BR_Z); + + int dst = gendst->index; + if (high && dst>=8) { // can't use REX prefix with high-byte reg + opcode(tmp).set64().setrm(dst).Emit8(0x03); // add tmp, dst + dst = -1; + } + // mov byte [tmp+src], val + opcode(genval->index,true,high?4:0).setea(tmp,dst).Emit8(0x88); + + Bit8u* jmp_loc=gen_create_short_jump(); + gen_fill_branch(nomap); + + if (gendst->index != ARG0_REG) { + x64gen.regs[reg_args[0]]->Clear(); + opcode(ARG0_REG).setrm(gendst->index).Emit8(0x8B); // mov ARG0,dst + } + gen_load_arg_reg(1, val, high ? "h":"l"); + gen_call_ptr((void*)mem_writeb_checked_dcx64); + dyn_check_bool_exception_al(); + + dyn_synchstate(&callstate); + gen_fill_short_jump(jmp_loc); +} + +#endif +static void dyn_read_word_release(DynReg * addr,DynReg * dst,bool dword) { + dyn_read_word(addr,dst,dword,addr!=dst); +} +static void dyn_read_byte_release(DynReg * addr,DynReg * dst,bool high) { + dyn_read_byte(addr,dst,high,addr!=dst); +} static void dyn_write_word_release(DynReg * addr,DynReg * val,bool dword) { - if (dword) { - dyn_write_intro(addr); - - GenReg * genreg=FindDynReg(val); - cache_addw(0xe9d1); // shr ecx,0x1 - Bit8u* jb_loc1=gen_create_branch(BR_B); - cache_addw(0xe9d1); // shr ecx,0x1 - Bit8u* jb_loc2=gen_create_branch(BR_B); - cache_addw(0xe9c1); // shr ecx,0x0a - cache_addb(0x0a); - cache_addw(0x0c8b); // mov ecx,paging.tlb.read[ecx*TYPE Bit32u] - cache_addb(0x8d); - cache_addd((Bit32u)(&paging.tlb.write[0])); - cache_addw(0xc985); // test ecx,ecx - Bit8u* je_loc=gen_create_branch(BR_Z); - - cache_addw(0x0489+(genreg->index <<(8+3))); // mov [eax+ecx],reg - cache_addb(0x08); - - Bit8u* jmp_loc=gen_create_jump(); - gen_fill_branch(jb_loc1); - gen_fill_branch(jb_loc2); - gen_fill_branch(je_loc); - - cache_addb(0x52); // push edx - cache_addb(0x50+genreg->index); - cache_addb(0x50); // push eax - cache_addb(0xe8); - cache_addd(((Bit32u)&mem_writed_checked) - (Bit32u)cache.pos-4); - cache_addw(0xc483); // add esp,8 - cache_addb(0x08); - cache_addw(0x012c); // sub al,1 - cache_addb(0x5a); // pop edx - - // Restore registers to be used again - x86gen.regs[X86_REG_EAX]->notusable=false; - x86gen.regs[X86_REG_ECX]->notusable=false; - - dyn_check_bool_exception_ne(); - - gen_fill_jump(jmp_loc); - } else { - gen_protectflags(); - gen_call_function((void *)&mem_writew_checked,"%Ddr%Dd",addr,val); - dyn_check_bool_exception_al(); - } + dyn_write_word(addr,val,dword,true); +} +static void dyn_write_byte_release(DynReg * addr,DynReg * src,bool high) { + dyn_write_byte(addr,src,high,true); } - #endif @@ -931,9 +1025,9 @@ static void dyn_pop(DynReg * dynreg,bool checked=true) { gen_dop_word(DOP_ADD,true,DREG(STACK),DREG(SS)); if (checked) { if (decode.big_op) { - gen_call_function((void *)&mem_readd_checked,"%Drd%Id",DREG(STACK),&core_dyn.readdata); + gen_call_function((void *)&mem_readd_checked,"%Drd%Ip",DREG(STACK),&core_dyn.readdata); } else { - gen_call_function((void *)&mem_readw_checked,"%Drd%Id",DREG(STACK),&core_dyn.readdata); + gen_call_function((void *)&mem_readw_checked,"%Drd%Ip",DREG(STACK),&core_dyn.readdata); } dyn_check_bool_exception_al(); gen_mov_host(&core_dyn.readdata,dynreg,decode.big_op?4:2); @@ -1048,7 +1142,7 @@ skip_extend_word: segbase=DREG(DS); Bitu val; if (decode_fetchd_imm(val)) { - gen_mov_host((void*)val,DREG(EA),4); + gen_dop_word_imm_mem(DOP_MOV,true,DREG(EA),(void*)val); if (!addseg) { gen_lea(reg_ea,DREG(EA),scaled,scale,0); } else { @@ -1081,7 +1175,7 @@ skip_extend_word: case 2: { Bitu val; if (decode_fetchd_imm(val)) { - gen_mov_host((void*)val,DREG(EA),4); + gen_dop_word_imm_mem(DOP_MOV,true,DREG(EA),(void*)val); if (!addseg) { gen_lea(DREG(EA),DREG(EA),scaled,scale,0); gen_lea(reg_ea,DREG(EA),base,0,0); @@ -1212,7 +1306,7 @@ static void dyn_mov_ebgb(void) { DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];Bitu rm_regi=decode.modrm.reg&4; if (decode.modrm.mod<3) { dyn_fill_ea(); - dyn_write_byte_release(DREG(EA),rm_reg,rm_regi==4); + dyn_write_byte_release(DREG(EA),rm_reg,rm_regi!=0); } else { gen_dop_byte(DOP_MOV,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,rm_reg,rm_regi); } @@ -1223,7 +1317,7 @@ static void dyn_mov_gbeb(void) { DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];Bitu rm_regi=decode.modrm.reg&4; if (decode.modrm.mod<3) { dyn_fill_ea(); - dyn_read_byte_release(DREG(EA),rm_reg,rm_regi); + dyn_read_byte_release(DREG(EA),rm_reg,rm_regi!=0); } else { gen_dop_byte(DOP_MOV,rm_reg,rm_regi,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4); } @@ -1553,7 +1647,7 @@ static void dyn_grp2_ev(grp2_types type) { if (decode_fetchb_imm(val)) { if (decode.modrm.reg < 4) gen_needflags(); else gen_discardflags(); - gen_load_host((void*)val,DREG(TMPB),1); + gen_dop_byte_imm_mem(DOP_MOV,DREG(TMPB),0,(void*)val); gen_shift_word_cl(decode.modrm.reg,decode.big_op,src,DREG(TMPB)); gen_releasereg(DREG(TMPB)); break; @@ -1612,7 +1706,7 @@ static void dyn_grp3_eb(void) { gen_dop_byte(DOP_MOV,DREG(TMPB),0,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4); gen_releasereg(DREG(EAX)); gen_call_function((decode.modrm.reg==6) ? (void *)&dyn_helper_divb : (void *)&dyn_helper_idivb, - "%Rd%Dd",DREG(TMPB),DREG(TMPB)); + "%Rd%Drd",DREG(TMPB),DREG(TMPB)); dyn_check_bool_exception(DREG(TMPB)); goto skipsave; } @@ -1654,7 +1748,7 @@ static void dyn_grp3_ev(void) { void * func=(decode.modrm.reg==6) ? (decode.big_op ? (void *)&dyn_helper_divd : (void *)&dyn_helper_divw) : (decode.big_op ? (void *)&dyn_helper_idivd : (void *)&dyn_helper_idivw); - gen_call_function(func,"%Rd%Dd",DREG(TMPB),DREG(TMPW)); + gen_call_function(func,"%Rd%Drd",DREG(TMPB),DREG(TMPW)); dyn_check_bool_exception(DREG(TMPB)); gen_releasereg(DREG(TMPB)); goto skipsave; @@ -1732,8 +1826,8 @@ static void dyn_pop_ev(void) { if (decode.modrm.mod<3) { dyn_fill_ea(); // dyn_write_word_release(DREG(EA),DREG(TMPW),decode.big_op); - if (decode.big_op) gen_call_function((void *)&mem_writed_inline,"%Ddr%Dd",DREG(EA),DREG(TMPW)); - else gen_call_function((void *)&mem_writew_inline,"%Ddr%Dd",DREG(EA),DREG(TMPW)); + if (decode.big_op) gen_call_function((void *)&mem_writed_inline,"%Drd%Dd",DREG(EA),DREG(TMPW)); + else gen_call_function((void *)&mem_writew_inline,"%Drd%Dd",DREG(EA),DREG(TMPW)); } else { gen_dop_word(DOP_MOV,decode.big_op,&DynRegs[decode.modrm.rm],DREG(TMPW)); } @@ -1854,7 +1948,7 @@ static void dyn_loop(LoopTypes type) { branch2=gen_create_branch(BR_Z); break; case LOOP_JCXZ: - gen_dop_word(DOP_OR,decode.big_addr,DREG(ECX),DREG(ECX)); + gen_dop_word(DOP_TEST,decode.big_addr,DREG(ECX),DREG(ECX)); gen_releasereg(DREG(ECX)); branch2=gen_create_branch(BR_NZ); break; @@ -1975,8 +2069,8 @@ static void dyn_add_iocheck_var(Bit8u accessed_port,Bitu access_size) { #define dh_fpu_startup() { \ fpu_used=true; \ gen_protectflags(); \ - gen_load_host(&dyn_dh_fpu.state_used,DREG(TMPB),4); \ - gen_dop_word_imm(DOP_CMP,true,DREG(TMPB),0); \ + gen_load_host(&dyn_dh_fpu.state_used,DREG(TMPB),1); \ + gen_dop_byte(DOP_TEST,DREG(TMPB),0,DREG(TMPB),0); \ gen_releasereg(DREG(TMPB)); \ save_info[used_save_info].branch_pos=gen_create_branch_long(BR_Z); \ dyn_savestate(&save_info[used_save_info].state); \ @@ -2002,15 +2096,15 @@ static CacheBlock * CreateCacheBlock(CodePageHandler * codepage,PhysPt start,Bit decode.block->page.start=decode.page.index; codepage->AddCacheBlock(decode.block); - gen_save_host_direct(&cache.block.running,(Bit32u)decode.block); for (i=0;i> 3) & 7; switch (group){ case 0x00: /* FADD ST,STi */ - gen_call_function((void*)&FPU_FADD_EA,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_FADD_EA,"%Drd",DREG(TMPB)); break; case 0x01: /* FMUL ST,STi */ - gen_call_function((void*)&FPU_FMUL_EA,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_FMUL_EA,"%Drd",DREG(TMPB)); break; case 0x02: /* FCOM STi */ - gen_call_function((void*)&FPU_FCOM_EA,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_FCOM_EA,"%Drd",DREG(TMPB)); break; case 0x03: /* FCOMP STi */ - gen_call_function((void*)&FPU_FCOM_EA,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_FCOM_EA,"%Drd",DREG(TMPB)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: /* FSUB ST,STi */ - gen_call_function((void*)&FPU_FSUB_EA,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_FSUB_EA,"%Drd",DREG(TMPB)); break; case 0x05: /* FSUBR ST,STi */ - gen_call_function((void*)&FPU_FSUBR_EA,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_FSUBR_EA,"%Drd",DREG(TMPB)); break; case 0x06: /* FDIV ST,STi */ - gen_call_function((void*)&FPU_FDIV_EA,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_FDIV_EA,"%Drd",DREG(TMPB)); break; case 0x07: /* FDIVR ST,STi */ - gen_call_function((void*)&FPU_FDIVR_EA,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_FDIVR_EA,"%Drd",DREG(TMPB)); break; default: break; @@ -98,39 +98,39 @@ static void dyn_fpu_esc0(){ if (decode.modrm.val >= 0xc0) { dyn_fpu_top(); Bitu group=(decode.modrm.val >> 3) & 7; - Bitu sub=(decode.modrm.val & 7); + //Bitu sub=(decode.modrm.val & 7); switch (group){ case 0x00: //FADD ST,STi / - gen_call_function((void*)&FPU_FADD,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FADD,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x01: // FMUL ST,STi / - gen_call_function((void*)&FPU_FMUL,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FMUL,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x02: // FCOM STi / - gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x03: // FCOMP STi / - gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: // FSUB ST,STi / - gen_call_function((void*)&FPU_FSUB,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FSUB,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x05: // FSUBR ST,STi / - gen_call_function((void*)&FPU_FSUBR,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FSUBR,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x06: // FDIV ST,STi / - gen_call_function((void*)&FPU_FDIV,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FDIV,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x07: // FDIVR ST,STi / - gen_call_function((void*)&FPU_FDIVR,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FDIVR,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; default: break; } } else { dyn_fill_ea(); - gen_call_function((void*)&FPU_FLD_F32_EA,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FLD_F32_EA,"%Drd",DREG(EA)); gen_load_host(&TOP,DREG(TMPB),4); dyn_eatree(); } @@ -149,18 +149,18 @@ static void dyn_fpu_esc1(){ gen_dop_word_imm(DOP_AND,true,DREG(EA),7); gen_call_function((void*)&FPU_PREP_PUSH,""); gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FST,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x01: /* FXCH STi */ dyn_fpu_top(); - gen_call_function((void*)&FPU_FXCH,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FXCH,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x02: /* FNOP */ gen_call_function((void*)&FPU_FNOP,""); break; case 0x03: /* FSTP STi */ dyn_fpu_top(); - gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FST,"%Drd%Drd",DREG(TMPB),DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: @@ -290,29 +290,29 @@ static void dyn_fpu_esc1(){ gen_protectflags(); gen_call_function((void*)&FPU_PREP_PUSH,""); gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void*)&FPU_FLD_F32,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FLD_F32,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x01: /* UNKNOWN */ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub); break; case 0x02: /* FST float*/ - gen_call_function((void*)&FPU_FST_F32,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_F32,"%Drd",DREG(EA)); break; case 0x03: /* FSTP float*/ - gen_call_function((void*)&FPU_FST_F32,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_F32,"%Drd",DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: /* FLDENV */ - gen_call_function((void*)&FPU_FLDENV,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FLDENV,"%Drd",DREG(EA)); break; case 0x05: /* FLDCW */ - gen_call_function((void *)&FPU_FLDCW,"%Ddr",DREG(EA)); + gen_call_function((void *)&FPU_FLDCW,"%Drd",DREG(EA)); break; case 0x06: /* FSTENV */ - gen_call_function((void *)&FPU_FSTENV,"%Ddr",DREG(EA)); + gen_call_function((void *)&FPU_FSTENV,"%Drd",DREG(EA)); break; case 0x07: /* FNSTCW*/ - gen_call_function((void *)&FPU_FNSTCW,"%Ddr",DREG(EA)); + gen_call_function((void *)&FPU_FNSTCW,"%Drd",DREG(EA)); break; default: LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub); @@ -335,7 +335,7 @@ static void dyn_fpu_esc2(){ gen_dop_word_imm(DOP_ADD,true,DREG(EA),1); gen_dop_word_imm(DOP_AND,true,DREG(EA),7); gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void *)&FPU_FUCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void *)&FPU_FUCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); gen_call_function((void *)&FPU_FPOP,""); gen_call_function((void *)&FPU_FPOP,""); break; @@ -350,7 +350,7 @@ static void dyn_fpu_esc2(){ } } else { dyn_fill_ea(); - gen_call_function((void*)&FPU_FLD_I32_EA,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FLD_I32_EA,"%Drd",DREG(EA)); gen_load_host(&TOP,DREG(TMPB),4); dyn_eatree(); } @@ -395,24 +395,24 @@ static void dyn_fpu_esc3(){ gen_call_function((void*)&FPU_PREP_PUSH,""); gen_protectflags(); gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void*)&FPU_FLD_I32,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FLD_I32,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x01: /* FISTTP */ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub); break; case 0x02: /* FIST */ - gen_call_function((void*)&FPU_FST_I32,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_I32,"%Drd",DREG(EA)); break; case 0x03: /* FISTP */ - gen_call_function((void*)&FPU_FST_I32,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_I32,"%Drd",DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x05: /* FLD 80 Bits Real */ gen_call_function((void*)&FPU_PREP_PUSH,""); - gen_call_function((void*)&FPU_FLD_F80,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FLD_F80,"%Drd",DREG(EA)); break; case 0x07: /* FSTP 80 Bits Real */ - gen_call_function((void*)&FPU_FST_F80,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_F80,"%Drd",DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; default: @@ -424,41 +424,41 @@ static void dyn_fpu_esc3(){ static void dyn_fpu_esc4(){ dyn_get_modrm(); Bitu group=(decode.modrm.val >> 3) & 7; - Bitu sub=(decode.modrm.val & 7); + //Bitu sub=(decode.modrm.val & 7); if (decode.modrm.val >= 0xc0) { dyn_fpu_top(); switch(group){ case 0x00: /* FADD STi,ST*/ - gen_call_function((void*)&FPU_FADD,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FADD,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x01: /* FMUL STi,ST*/ - gen_call_function((void*)&FPU_FMUL,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FMUL,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x02: /* FCOM*/ - gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x03: /* FCOMP*/ - gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: /* FSUBR STi,ST*/ - gen_call_function((void*)&FPU_FSUBR,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FSUBR,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x05: /* FSUB STi,ST*/ - gen_call_function((void*)&FPU_FSUB,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FSUB,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x06: /* FDIVR STi,ST*/ - gen_call_function((void*)&FPU_FDIVR,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FDIVR,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x07: /* FDIV STi,ST*/ - gen_call_function((void*)&FPU_FDIV,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FDIV,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; default: break; } } else { dyn_fill_ea(); - gen_call_function((void*)&FPU_FLD_F64_EA,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FLD_F64_EA,"%Drd",DREG(EA)); gen_load_host(&TOP,DREG(TMPB),4); dyn_eatree(); } @@ -472,23 +472,23 @@ static void dyn_fpu_esc5(){ dyn_fpu_top(); switch(group){ case 0x00: /* FFREE STi */ - gen_call_function((void*)&FPU_FFREE,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FFREE,"%Drd",DREG(EA)); break; case 0x01: /* FXCH STi*/ - gen_call_function((void*)&FPU_FXCH,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FXCH,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x02: /* FST STi */ - gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FST,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x03: /* FSTP STi*/ - gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FST,"%Drd%Drd",DREG(TMPB),DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: /* FUCOM STi */ - gen_call_function((void*)&FPU_FUCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FUCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x05: /*FUCOMP STi */ - gen_call_function((void*)&FPU_FUCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FUCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; default: @@ -504,30 +504,30 @@ static void dyn_fpu_esc5(){ gen_call_function((void*)&FPU_PREP_PUSH,""); gen_protectflags(); gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void*)&FPU_FLD_F64,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FLD_F64,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x01: /* FISTTP longint*/ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub); break; case 0x02: /* FST double real*/ - gen_call_function((void*)&FPU_FST_F64,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_F64,"%Drd",DREG(EA)); break; case 0x03: /* FSTP double real*/ - gen_call_function((void*)&FPU_FST_F64,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_F64,"%Drd",DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: /* FRSTOR */ - gen_call_function((void*)&FPU_FRSTOR,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FRSTOR,"%Drd",DREG(EA)); break; case 0x06: /* FSAVE */ - gen_call_function((void*)&FPU_FSAVE,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FSAVE,"%Drd",DREG(EA)); break; case 0x07: /*FNSTSW */ gen_protectflags(); gen_load_host(&TOP,DREG(TMPB),4); gen_call_function((void*)&FPU_SET_TOP,"%Dd",DREG(TMPB)); gen_load_host(&fpu.sw,DREG(TMPB),4); - gen_call_function((void*)&mem_writew,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&mem_writew,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; default: LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub); @@ -543,13 +543,13 @@ static void dyn_fpu_esc6(){ dyn_fpu_top(); switch(group){ case 0x00: /*FADDP STi,ST*/ - gen_call_function((void*)&FPU_FADD,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FADD,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x01: /* FMULP STi,ST*/ - gen_call_function((void*)&FPU_FMUL,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FMUL,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x02: /* FCOMP5*/ - gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; /* TODO IS THIS ALLRIGHT ????????? */ case 0x03: /*FCOMPP*/ if(sub != 1) { @@ -559,20 +559,20 @@ static void dyn_fpu_esc6(){ gen_load_host(&TOP,DREG(EA),4); gen_dop_word_imm(DOP_ADD,true,DREG(EA),1); gen_dop_word_imm(DOP_AND,true,DREG(EA),7); - gen_call_function((void*)&FPU_FCOM,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FCOM,"%Drd%Drd",DREG(TMPB),DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); /* extra pop at the bottom*/ break; case 0x04: /* FSUBRP STi,ST*/ - gen_call_function((void*)&FPU_FSUBR,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FSUBR,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x05: /* FSUBP STi,ST*/ - gen_call_function((void*)&FPU_FSUB,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FSUB,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x06: /* FDIVRP STi,ST*/ - gen_call_function((void*)&FPU_FDIVR,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FDIVR,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x07: /* FDIVP STi,ST*/ - gen_call_function((void*)&FPU_FDIV,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FDIV,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; default: break; @@ -580,7 +580,7 @@ static void dyn_fpu_esc6(){ gen_call_function((void*)&FPU_FPOP,""); } else { dyn_fill_ea(); - gen_call_function((void*)&FPU_FLD_I16_EA,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FLD_I16_EA,"%Drd",DREG(EA)); gen_load_host(&TOP,DREG(TMPB),4); dyn_eatree(); } @@ -594,24 +594,24 @@ static void dyn_fpu_esc7(){ switch (group){ case 0x00: /* FFREEP STi*/ dyn_fpu_top(); - gen_call_function((void*)&FPU_FFREE,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FFREE,"%Drd",DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x01: /* FXCH STi*/ dyn_fpu_top(); - gen_call_function((void*)&FPU_FXCH,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FXCH,"%Drd%Drd",DREG(TMPB),DREG(EA)); break; case 0x02: /* FSTP STi*/ case 0x03: /* FSTP STi*/ dyn_fpu_top(); - gen_call_function((void*)&FPU_FST,"%Ddr%Ddr",DREG(TMPB),DREG(EA)); + gen_call_function((void*)&FPU_FST,"%Drd%Drd",DREG(TMPB),DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: switch(sub){ case 0x00: /* FNSTSW AX*/ gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void*)&FPU_SET_TOP,"%Ddr",DREG(TMPB)); + gen_call_function((void*)&FPU_SET_TOP,"%Drd",DREG(TMPB)); gen_mov_host(&fpu.sw,DREG(EAX),2); break; default: @@ -629,34 +629,34 @@ static void dyn_fpu_esc7(){ case 0x00: /* FILD Bit16s */ gen_call_function((void*)&FPU_PREP_PUSH,""); gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void*)&FPU_FLD_I16,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FLD_I16,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x01: LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub); break; case 0x02: /* FIST Bit16s */ - gen_call_function((void*)&FPU_FST_I16,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_I16,"%Drd",DREG(EA)); break; case 0x03: /* FISTP Bit16s */ - gen_call_function((void*)&FPU_FST_I16,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_I16,"%Drd",DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x04: /* FBLD packed BCD */ gen_call_function((void*)&FPU_PREP_PUSH,""); gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void*)&FPU_FBLD,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FBLD,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x05: /* FILD Bit64s */ gen_call_function((void*)&FPU_PREP_PUSH,""); gen_load_host(&TOP,DREG(TMPB),4); - gen_call_function((void*)&FPU_FLD_I64,"%Ddr%Ddr",DREG(EA),DREG(TMPB)); + gen_call_function((void*)&FPU_FLD_I64,"%Drd%Drd",DREG(EA),DREG(TMPB)); break; case 0x06: /* FBSTP packed BCD */ - gen_call_function((void*)&FPU_FBST,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FBST,"%Drd",DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; case 0x07: /* FISTP Bit64s */ - gen_call_function((void*)&FPU_FST_I64,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FST_I64,"%Drd",DREG(EA)); gen_call_function((void*)&FPU_FPOP,""); break; default: diff --git a/src/cpu/core_dyn_x86/dyn_fpu_dh.h b/src/cpu/core_dyn_x86/dyn_fpu_dh.h index 47dc757..b995cb0 100644 --- a/src/cpu/core_dyn_x86/dyn_fpu_dh.h +++ b/src/cpu/core_dyn_x86/dyn_fpu_dh.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -148,6 +148,16 @@ static void FPU_FRSTOR_DH(PhysPt addr){ } } +static void dh_fpu_mem(Bit8u inst, Bitu reg=decode.modrm.reg, void* mem=&dyn_dh_fpu.temp.m1) { +#if C_TARGETCPU == X86 + cache_addb(inst); + cache_addb(0x05|(reg<<3)); + cache_addd((Bit32u)(mem)); +#else // X86_64 + opcode(reg).setabsaddr(mem).Emit8(inst); +#endif +} + static void dh_fpu_esc0(){ dyn_get_modrm(); if (decode.modrm.val >= 0xc0) { @@ -155,10 +165,8 @@ static void dh_fpu_esc0(){ cache_addb(decode.modrm.val); } else { dyn_fill_ea(); - gen_call_function((void*)&FPU_FLD_32,"%Ddr",DREG(EA)); - cache_addb(0xd8); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_32,"%Drd",DREG(EA)); + dh_fpu_mem(0xd8); } } @@ -173,46 +181,34 @@ static void dh_fpu_esc1(){ dyn_fill_ea(); switch(group){ case 0x00: /* FLD float*/ - gen_call_function((void*)&FPU_FLD_32,"%Ddr",DREG(EA)); - cache_addb(0xd9); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_32,"%Drd",DREG(EA)); + dh_fpu_mem(0xd9); break; case 0x01: /* UNKNOWN */ LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub); break; case 0x02: /* FST float*/ - cache_addb(0xd9); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_32,"%Ddr",DREG(EA)); + dh_fpu_mem(0xd9); + gen_call_function((void*)&FPU_FST_32,"%Drd",DREG(EA)); break; case 0x03: /* FSTP float*/ - cache_addb(0xd9); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_32,"%Ddr",DREG(EA)); + dh_fpu_mem(0xd9); + gen_call_function((void*)&FPU_FST_32,"%Drd",DREG(EA)); break; case 0x04: /* FLDENV */ - gen_call_function((void*)&FPU_FLDENV_DH,"%Ddr",DREG(EA)); - cache_addb(0xd9); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLDENV_DH,"%Drd",DREG(EA)); + dh_fpu_mem(0xd9); break; case 0x05: /* FLDCW */ - gen_call_function((void *)&FPU_FLDCW_DH,"%Ddr",DREG(EA)); - cache_addb(0xd9); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void *)&FPU_FLDCW_DH,"%Drd",DREG(EA)); + dh_fpu_mem(0xd9); break; case 0x06: /* FSTENV */ - cache_addb(0xd9); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FSTENV_DH,"%Ddr",DREG(EA)); + dh_fpu_mem(0xd9); + gen_call_function((void*)&FPU_FSTENV_DH,"%Drd",DREG(EA)); break; case 0x07: /* FNSTCW*/ - gen_call_function((void*)&FPU_FNSTCW_DH,"%Ddr",DREG(EA)); + gen_call_function((void*)&FPU_FNSTCW_DH,"%Drd",DREG(EA)); break; default: LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",group,sub); @@ -228,10 +224,8 @@ static void dh_fpu_esc2(){ cache_addb(decode.modrm.val); } else { dyn_fill_ea(); - gen_call_function((void*)&FPU_FLD_32,"%Ddr",DREG(EA)); - cache_addb(0xda); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_32,"%Drd",DREG(EA)); + dh_fpu_mem(0xda); } } @@ -274,37 +268,27 @@ static void dh_fpu_esc3(){ dyn_fill_ea(); switch(group){ case 0x00: /* FILD */ - gen_call_function((void*)&FPU_FLD_32,"%Ddr",DREG(EA)); - cache_addb(0xdb); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_32,"%Drd",DREG(EA)); + dh_fpu_mem(0xdb); break; case 0x01: /* FISTTP */ LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub); break; case 0x02: /* FIST */ - cache_addb(0xdb); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_32,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdb); + gen_call_function((void*)&FPU_FST_32,"%Drd",DREG(EA)); break; case 0x03: /* FISTP */ - cache_addb(0xdb); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_32,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdb); + gen_call_function((void*)&FPU_FST_32,"%Drd",DREG(EA)); break; case 0x05: /* FLD 80 Bits Real */ - gen_call_function((void*)&FPU_FLD_80,"%Ddr",DREG(EA)); - cache_addb(0xdb); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_80,"%Drd",DREG(EA)); + dh_fpu_mem(0xdb); break; case 0x07: /* FSTP 80 Bits Real */ - cache_addb(0xdb); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_80,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdb); + gen_call_function((void*)&FPU_FST_80,"%Drd",DREG(EA)); break; default: LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",group,sub); @@ -314,17 +298,13 @@ static void dh_fpu_esc3(){ static void dh_fpu_esc4(){ dyn_get_modrm(); - Bitu group=(decode.modrm.val >> 3) & 7; - Bitu sub=(decode.modrm.val & 7); if (decode.modrm.val >= 0xc0) { cache_addb(0xdc); cache_addb(decode.modrm.val); } else { dyn_fill_ea(); - gen_call_function((void*)&FPU_FLD_64,"%Ddr",DREG(EA)); - cache_addb(0xdc); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_64,"%Drd",DREG(EA)); + dh_fpu_mem(0xdc); } } @@ -339,45 +319,32 @@ static void dh_fpu_esc5(){ Bitu sub=(decode.modrm.val & 7); switch(group){ case 0x00: /* FLD double real*/ - gen_call_function((void*)&FPU_FLD_64,"%Ddr",DREG(EA)); - cache_addb(0xdd); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_64,"%Drd",DREG(EA)); + dh_fpu_mem(0xdd); break; case 0x01: /* FISTTP longint*/ LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub); break; case 0x02: /* FST double real*/ - cache_addb(0xdd); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_64,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdd); + gen_call_function((void*)&FPU_FST_64,"%Drd",DREG(EA)); break; case 0x03: /* FSTP double real*/ - cache_addb(0xdd); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_64,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdd); + gen_call_function((void*)&FPU_FST_64,"%Drd",DREG(EA)); break; case 0x04: /* FRSTOR */ - gen_call_function((void*)&FPU_FRSTOR_DH,"%Ddr",DREG(EA)); - cache_addb(0xdd); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp_state[0]))); + gen_call_function((void*)&FPU_FRSTOR_DH,"%Drd",DREG(EA)); + dh_fpu_mem(0xdd, decode.modrm.reg, &(dyn_dh_fpu.temp_state[0])); break; case 0x06: /* FSAVE */ - cache_addb(0xdd); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp_state[0]))); - gen_call_function((void*)&FPU_FSAVE_DH,"%Ddr",DREG(EA)); - cache_addb(0xdb); - cache_addb(0xe3); + dh_fpu_mem(0xdd, decode.modrm.reg, &(dyn_dh_fpu.temp_state[0])); + gen_call_function((void*)&FPU_FSAVE_DH,"%Drd",DREG(EA)); + cache_addw(0xE3DB); break; case 0x07: /* FNSTSW */ - cache_addb(0xdd); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_16,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdd); + gen_call_function((void*)&FPU_FST_16,"%Drd",DREG(EA)); break; default: LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",group,sub); @@ -387,17 +354,13 @@ static void dh_fpu_esc5(){ static void dh_fpu_esc6(){ dyn_get_modrm(); - Bitu group=(decode.modrm.val >> 3) & 7; - Bitu sub=(decode.modrm.val & 7); if (decode.modrm.val >= 0xc0) { cache_addb(0xde); cache_addb(decode.modrm.val); } else { dyn_fill_ea(); - gen_call_function((void*)&FPU_FLD_16,"%Ddr",DREG(EA)); - cache_addb(0xde); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_16,"%Drd",DREG(EA)); + dh_fpu_mem(0xde); } } @@ -423,9 +386,7 @@ static void dh_fpu_esc7(){ case 0x04: switch(sub){ case 0x00: /* FNSTSW AX*/ - cache_addb(0xdd); - cache_addb(0x05|(0x07<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + dh_fpu_mem(0xdd, 7); gen_load_host(&(dyn_dh_fpu.temp.m1),DREG(TMPB),4); gen_dop_word(DOP_MOV,false,DREG(EAX),DREG(TMPB)); gen_releasereg(DREG(TMPB)); @@ -443,49 +404,35 @@ static void dh_fpu_esc7(){ dyn_fill_ea(); switch(group){ case 0x00: /* FILD Bit16s */ - gen_call_function((void*)&FPU_FLD_16,"%Ddr",DREG(EA)); - cache_addb(0xdf); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_16,"%Drd",DREG(EA)); + dh_fpu_mem(0xdf); break; case 0x01: LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub); break; case 0x02: /* FIST Bit16s */ - cache_addb(0xdf); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_16,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdf); + gen_call_function((void*)&FPU_FST_16,"%Drd",DREG(EA)); break; case 0x03: /* FISTP Bit16s */ - cache_addb(0xdf); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_16,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdf); + gen_call_function((void*)&FPU_FST_16,"%Drd",DREG(EA)); break; case 0x04: /* FBLD packed BCD */ - gen_call_function((void*)&FPU_FLD_80,"%Ddr",DREG(EA)); - cache_addb(0xdf); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_80,"%Drd",DREG(EA)); + dh_fpu_mem(0xdf); break; case 0x05: /* FILD Bit64s */ - gen_call_function((void*)&FPU_FLD_64,"%Ddr",DREG(EA)); - cache_addb(0xdf); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); + gen_call_function((void*)&FPU_FLD_64,"%Drd",DREG(EA)); + dh_fpu_mem(0xdf); break; case 0x06: /* FBSTP packed BCD */ - cache_addb(0xdf); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_80,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdf); + gen_call_function((void*)&FPU_FST_80,"%Drd",DREG(EA)); break; case 0x07: /* FISTP Bit64s */ - cache_addb(0xdf); - cache_addb(0x05|(decode.modrm.reg<<3)); - cache_addd((Bit32u)(&(dyn_dh_fpu.temp.m1))); - gen_call_function((void*)&FPU_FST_64,"%Ddr",DREG(EA)); + dh_fpu_mem(0xdf); + gen_call_function((void*)&FPU_FST_64,"%Drd",DREG(EA)); break; default: LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",group,sub); diff --git a/src/cpu/core_dyn_x86/helpers.h b/src/cpu/core_dyn_x86/helpers.h index 727a973..0bba981 100644 --- a/src/cpu/core_dyn_x86/helpers.h +++ b/src/cpu/core_dyn_x86/helpers.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ static bool dyn_helper_divb(Bit8u val) { @@ -40,8 +40,8 @@ static bool dyn_helper_idivb(Bit8s val) { static bool dyn_helper_divw(Bit16u val) { if (!val) return CPU_PrepareException(0,0); - Bitu num=(reg_dx<<16)|reg_ax; - Bitu quo=num/val; + Bit32u num=(((Bit32u)reg_dx)<<16)|reg_ax; + Bit32u quo=num/val; Bit16u rem=(Bit16u)(num % val); Bit16u quo16=(Bit16u)(quo&0xffff); if (quo!=(Bit32u)quo16) return CPU_PrepareException(0,0); @@ -52,8 +52,8 @@ static bool dyn_helper_divw(Bit16u val) { static bool dyn_helper_idivw(Bit16s val) { if (!val) return CPU_PrepareException(0,0); - Bits num=(reg_dx<<16)|reg_ax; - Bits quo=num/val; + Bit32s num=(((Bit32u)reg_dx)<<16)|reg_ax; + Bit32s quo=num/val; Bit16s rem=(Bit16s)(num % val); Bit16s quo16s=(Bit16s)quo; if (quo!=(Bit32s)quo16s) return CPU_PrepareException(0,0); diff --git a/src/cpu/core_dyn_x86/risc_x64.h b/src/cpu/core_dyn_x86/risc_x64.h new file mode 100644 index 0000000..384cdb4 --- /dev/null +++ b/src/cpu/core_dyn_x86/risc_x64.h @@ -0,0 +1,1278 @@ +/* + * Copyright (C) 2002-2019 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#if defined(_WIN64) +enum { + X64_REG_RBX, + X64_REG_RDX, + X64_REG_RCX, + X64_REG_RAX, + // volatiles + X64_REG_R8, + X64_REG_R9, + X64_REG_R10, + X64_REG_R11, + // non-volatiles + X64_REG_R12, + X64_REG_R13, + X64_REG_R14, + X64_REG_R15, + X64_REG_RSI, + X64_REG_RDI, + X64_REGS +}; +static const int reg_args[4] = {X64_REG_RCX, X64_REG_RDX, X64_REG_R8, X64_REG_R9}; +#define ARG0_REG 1 +#define ARG1_REG 2 +#define CALLSTACK 40 +#else +enum { + // (high)byte-accessible + X64_REG_RBX, + X64_REG_RCX, + X64_REG_RDX, + X64_REG_RAX, + // volatiles + X64_REG_RSI, + X64_REG_RDI, + X64_REG_R8, + X64_REG_R9, + X64_REG_R10, + X64_REG_R11, + // non-volatiles + X64_REG_R12, + X64_REG_R13, + X64_REG_R14, + X64_REG_R15, + // delimiter + X64_REGS +}; +static const int reg_args[4] = {X64_REG_RDI, X64_REG_RSI, X64_REG_RDX, X64_REG_RCX}; +#define ARG0_REG 7 +#define ARG1_REG 6 +#define CALLSTACK 8 +#endif + +static struct { + bool flagsactive; + Bitu last_used; + GenReg * regs[X64_REGS]; +} x64gen; + +class opcode { +public: + opcode(void) : is_word(false), imm_size(0), rex(0) {} + opcode(int reg,bool dword=true,Bitu acc=1) : is_word(!dword), imm_size(0), rex(0) { + setreg(reg, acc); + } + + opcode& setword() {is_word=true; return *this;} + opcode& set64(void) {rex|=0x48;return *this;} + opcode& setimm(Bit64u _imm, int size) {imm=_imm;imm_size=size;return *this;} + + opcode& setreg(int r, Bitu acc=1); // acc: 0=low byte, 1=word/dword, 4=high byte + opcode& setrm(int r, Bitu acc=1); + opcode& setabsaddr(void* addr); + opcode& setea(int rbase, int rscale=-1, Bitu scale=0, Bits off=0); + + void Emit8Reg(Bit8u op); + void Emit8(Bit8u op); + void Emit16(Bit16u op); + +private: + bool is_word; + int reg; + Bit64u imm; + int imm_size; + + Bit8u rex, modrm, sib; + Bits offset; + + void EmitImm(void) { + switch(imm_size) { + case 1: cache_addb((Bit8u)imm);break; + case 2: cache_addw((Bit16u)imm);break; + case 4: cache_addd((Bit32u)imm);break; + case 8: cache_addq(imm);break; + } + } + + void EmitSibOffImm(void) { + if (modrm<0xC0) { + if ((modrm&7)==4) cache_addb(sib); + switch (modrm>>6) { + case 0: + if ((modrm&7)==5) { + // update offset to be RIP relative + Bits diff = offset - (Bits)cache.pos - 4 - imm_size; + if ((Bit32s)diff == diff) offset = diff; + else { // try 32-bit absolute address + if ((Bit32s)offset != offset) IllegalOption("opcode::Emit: bad RIP address"); + // change emitted modrm base from 5 to 4 (use sib) + cache.pos[-1] -= 1; + cache_addb(0x25); // sib: [none+1*none+simm32] + } + } else if ((modrm&7)!=4 || (sib&7)!=5) + break; + case 2: cache_addd((Bit32u)offset); break; + case 1: cache_addb((Bit8u)offset); break; + } + } + EmitImm(); + } +}; + +void opcode::Emit8Reg(Bit8u op) { + if (is_word) cache_addb(0x66); + if (reg>=8) rex |= 0x41; + if (rex) cache_addb(rex); + cache_addb(op|(reg&7)); + EmitImm(); +} + +void opcode::Emit8(Bit8u op) { + if (is_word) cache_addb(0x66); + if (rex) cache_addb(rex); + cache_addw(op+(modrm<<8)); + EmitSibOffImm(); +} + +void opcode::Emit16(Bit16u op) { + if (is_word) cache_addb(0x66); + if (rex) cache_addb(rex); + cache_addw(op); + cache_addb(modrm); + EmitSibOffImm(); +} + +opcode& opcode::setreg(int r, Bitu acc) { + if (acc==4) { + if (r>3 || rex) IllegalOption("opcode::setreg: cannot encode high byte"); + r += 4; + } + else if (acc==0 && r>3) rex |= 0x40; + reg = r; + return *this; +} + +opcode& opcode::setrm(int r, Bitu acc) { + if (reg>=8) rex |= 0x44; + if (r>=8) rex |= 0x41; + if (acc==4) { + if (r>3 || rex) IllegalOption("opcode::setrm: cannot encode high byte"); + r += 4; + } + else if (acc==0 && r>3) rex |= 0x40; + modrm = 0xC0+((reg&7)<<3)+(r&7); + return *this; +} + +opcode& opcode::setabsaddr(void* addr) { + /* address must be in one of three ranges (in order of preference: + * &cpu_regs +/- 2GB (RBP relative) enc: modrm+1 or 4 bytes + * cache.pos +/- 2GB (RIP relative) enc: modrm+4 bytes + * < 0x80000000 or >= 0xFFFFFFFF80000000 (signed 32-bit absolute) enc: modrm+sib+4 bytes + */ + if (reg>=8) rex |= 0x44; + modrm = (reg&7)<<3; + offset = (Bits)addr - (Bits)&cpu_regs; + if ((Bit32s)offset == offset) { // [RBP+(Bit8s/Bit32s)] + if ((Bit8s)offset == offset) modrm += 0x45; + else modrm += 0x85; + } else { + offset = (Bits)addr; + modrm += 5; // [RIP+Bit32s] or [abs Bit32s] + } + + return *this; +} + +opcode& opcode::setea(int rbase, int rscale, Bitu scale, Bits off) { + if (reg>=8) rex |= 0x44; + if (rbase>=8) rex |= 0x41, rbase &= 7; + if (rscale>=8) rex |= 0x42, rscale &= 7; + modrm = (reg&7)<<3; + offset = off; + + if (rbase<0 || rscale>=0 || rbase==4) { // sib required + modrm += 4; + if (rscale>=0) sib = (Bit8u)((scale<<6)+(rscale<<3)); + else sib = 4<<3; + if (rbase>=0) sib += rbase; + else sib += 5; + } else modrm += rbase; + + if (rbase==5 || (off && rbase>=0)) { + if ((Bit8s)off == off) modrm += 0x40; + else modrm += 0x80; + } + + return *this; +} + + +class GenReg { +public: + GenReg(Bit8u _index) : index(_index) { + notusable=false;dynreg=0; + } + DynReg * dynreg; + Bitu last_used; //Keeps track of last assigned regs + const Bit8u index; + bool notusable; + void Load(DynReg * _dynreg,bool stale=false) { + if (!_dynreg) return; + if (GCC_UNLIKELY((Bitu)dynreg)) Clear(); + dynreg=_dynreg; + last_used=x64gen.last_used; + dynreg->flags&=~DYNFLG_CHANGED; + dynreg->genreg=this; + if ((!stale) && (dynreg->flags & (DYNFLG_LOAD|DYNFLG_ACTIVE))) { + opcode(index).setabsaddr(dynreg->data).Emit8(0x8B); // mov r32, [] + } + dynreg->flags|=DYNFLG_ACTIVE; + } + void Save(void) { + if (GCC_UNLIKELY(!((Bitu)dynreg))) IllegalOption("GenReg->Save"); + dynreg->flags&=~DYNFLG_CHANGED; + opcode(index).setabsaddr(dynreg->data).Emit8(0x89); // mov [], r32 + } + void Release(void) { + if (GCC_UNLIKELY(!((Bitu)dynreg))) return; + if (dynreg->flags&DYNFLG_CHANGED && dynreg->flags&DYNFLG_SAVE) { + Save(); + } + dynreg->flags&=~(DYNFLG_CHANGED|DYNFLG_ACTIVE); + dynreg->genreg=0;dynreg=0; + } + void Clear(void) { + if (!dynreg) return; + if (dynreg->flags&DYNFLG_CHANGED) { + Save(); + } + dynreg->genreg=0;dynreg=0; + } +}; + +static BlockReturn gen_runcodeInit(Bit8u *code); +static BlockReturn (*gen_runcode)(Bit8u *code) = gen_runcodeInit; + +static BlockReturn gen_runcodeInit(Bit8u *code) { + Bit8u* oldpos = cache.pos; + cache.pos = &cache_code_link_blocks[128]; + gen_runcode = (BlockReturn(*)(Bit8u*))cache.pos; + + opcode(5).Emit8Reg(0x50); // push rbp + opcode(15).Emit8Reg(0x50); // push r15 + opcode(14).Emit8Reg(0x50); // push r14 + // mov rbp, &cpu_regs + if ((Bit32u)(Bitu)&cpu_regs == (Bitu)&cpu_regs) opcode(5).setimm((Bitu)&cpu_regs,4).Emit8Reg(0xB8); + else opcode(5).set64().setimm((Bitu)&cpu_regs,8).Emit8Reg(0xB8); + opcode(13).Emit8Reg(0x50); // push r13 + opcode(12).Emit8Reg(0x50); // push r12 + opcode(3).Emit8Reg(0x50); // push rbx + opcode(0).setea(5,-1,0,offsetof(CPU_Regs,flags)).Emit8(0x8B); // mov eax, [reg_flags(rbp)] +#if defined(_WIN64) + opcode(7).Emit8Reg(0x50); // push rdi + opcode(6).Emit8Reg(0x50); // push rsi +#endif + opcode(15).set64().setrm(4).Emit8(0x8B); // mov r15, rsp + opcode(0).setimm(FMASK_TEST,4).Emit8Reg(0x25); // and eax, FMASK_TEST + cache_addb(0x48);cache_addw(0x158D); // lea rdx, [rip+simm32] + Bit8u *diff = cache.pos; + cache_addd(0); + opcode(4).set64().setrm(4).setimm(~15,1).Emit8(0x83); // and rsp, ~15 + opcode(15).Emit8Reg(0x50); // push r15 + opcode(2).Emit8Reg(0x50); // push rdx + opcode(5).set64().setrm(4).setimm(CALLSTACK*2,1).Emit8(0x83); // sub rsp, 16/80 + opcode(0).setea(4,-1,0,CALLSTACK).Emit8(0x89); // mov [rsp+8/40], eax + opcode(4).setrm(ARG0_REG).Emit8(0xFF); // jmp ARG0 + + *(Bit32u*)diff = (Bit32u)(cache.pos - diff - 4); + // eax = return value, ecx = flags + opcode(1).setea(5,-1,0,offsetof(CPU_Regs,flags)).Emit8(0x33); // xor ecx, reg_flags + opcode(4).setrm(1).setimm(FMASK_TEST,4).Emit8(0x81); // and ecx,FMASK_TEST + opcode(1).setea(5,-1,0,offsetof(CPU_Regs,flags)).Emit8(0x31); // xor reg_flags, ecx + + opcode(4).set64().setea(4,-1,0,CALLSTACK).Emit8(0x8B); // mov rsp, [rsp+8/40] +#if defined(_WIN64) + opcode(6).Emit8Reg(0x58); // pop rsi + opcode(7).Emit8Reg(0x58); // pop rdi +#endif + opcode(3).Emit8Reg(0x58); // pop rbx + opcode(12).Emit8Reg(0x58); // pop r12 + opcode(13).Emit8Reg(0x58); // pop r13 + opcode(14).Emit8Reg(0x58); // pop r14 + opcode(15).Emit8Reg(0x58); // pop r15 + opcode(5).Emit8Reg(0x58); // pop rbp + cache_addb(0xc3); // ret + + cache.pos = oldpos; + return gen_runcode(code); +} + +static GenReg * FindDynReg(DynReg * dynreg,bool stale=false) { + x64gen.last_used++; + if (dynreg->genreg) { + dynreg->genreg->last_used=x64gen.last_used; + return dynreg->genreg; + } + /* Find best match for selected global reg */ + Bits i; + Bits first_used,first_index; + first_used=-1; + if (dynreg->flags & DYNFLG_HAS8) { + /* Has to be rax,rbx,rcx,rdx */ + for (i=first_index=0;i<=3;i++) { + GenReg * genreg=x64gen.regs[i]; + if (genreg->notusable) continue; + if (!(genreg->dynreg)) { + genreg->Load(dynreg,stale); + return genreg; + } + if (genreg->last_used<(Bitu)first_used) { + first_used=genreg->last_used; + first_index=i; + } + } + } else { + for (i=first_index=X64_REGS-1;i>=0;i--) { + GenReg * genreg=x64gen.regs[i]; + if (genreg->notusable) continue; + if (!(genreg->dynreg)) { + genreg->Load(dynreg,stale); + return genreg; + } + if (genreg->last_used<(Bitu)first_used) { + first_used=genreg->last_used; + first_index=i; + } + } + } + /* No free register found use earliest assigned one */ + GenReg * newreg=x64gen.regs[first_index]; + newreg->Load(dynreg,stale); + return newreg; +} + +static Bit8u GetNextReg(bool low=false) { + Bitu i; + Bitu first_used,first_index; + first_used=x64gen.last_used+1; + for (i=first_index=0;inotusable) continue; + if (low && genreg->index>=8) continue; + if (!(genreg->dynreg)) { + first_index=i; + break; + } + if (genreg->last_usedlast_used; + first_index = i; + } + } + x64gen.regs[first_index]->Clear(); + return x64gen.regs[first_index]->index; +} + +static void ForceDynReg(GenReg * genreg,DynReg * dynreg) { + genreg->last_used = ++x64gen.last_used; + if (dynreg->genreg) { + if (dynreg->genreg==genreg) return; + if (genreg->dynreg) genreg->Clear(); + // mov dst32, src32 + opcode(genreg->index).setrm(dynreg->genreg->index).Emit8(0x8B); + dynreg->genreg->dynreg=0; + dynreg->genreg=genreg; + genreg->dynreg=dynreg; + } else genreg->Load(dynreg); +} + +static void gen_preloadreg(DynReg * dynreg) { + FindDynReg(dynreg); +} + +static void gen_releasereg(DynReg * dynreg) { + GenReg * genreg=dynreg->genreg; + if (genreg) genreg->Release(); + else dynreg->flags&=~(DYNFLG_ACTIVE|DYNFLG_CHANGED); +} + +static void gen_setupreg(DynReg * dnew,DynReg * dsetup) { + dnew->flags=dsetup->flags; + if (dnew->genreg==dsetup->genreg) return; + /* Not the same genreg must be wrong */ + if (dnew->genreg) { + /* Check if the genreg i'm changing is actually linked to me */ + if (dnew->genreg->dynreg==dnew) dnew->genreg->dynreg=0; + } + dnew->genreg=dsetup->genreg; + if (dnew->genreg) dnew->genreg->dynreg=dnew; +} + +static void gen_synchreg(DynReg * dnew,DynReg * dsynch) { + /* First make sure the registers match */ + if (dnew->genreg!=dsynch->genreg) { + if (dnew->genreg) dnew->genreg->Clear(); + if (dsynch->genreg) { + dsynch->genreg->Load(dnew); + } + } + /* Always use the loadonce flag from either state */ + dnew->flags|=(dsynch->flags & dnew->flags&DYNFLG_ACTIVE); + if ((dnew->flags ^ dsynch->flags) & DYNFLG_CHANGED) { + /* Ensure the changed value gets saved */ + if (dnew->flags & DYNFLG_CHANGED) { + dnew->genreg->Save(); + } else dnew->flags|=DYNFLG_CHANGED; + } +} + +static void gen_needflags(void) { + if (!x64gen.flagsactive) { + x64gen.flagsactive=true; + opcode(0).set64().setrm(4).setimm(CALLSTACK,1).Emit8(0x83); // add rsp,8/40 + cache_addb(0x9d); //POPFQ + } +} + +static void gen_protectflags(void) { + if (x64gen.flagsactive) { + x64gen.flagsactive=false; + cache_addb(0x9c); //PUSHFQ + opcode(4).set64().setea(4,-1,0,-(CALLSTACK)).Emit8(0x8D); // lea rsp, [rsp-8/40] + } +} + +static void gen_discardflags(void) { + if (!x64gen.flagsactive) { + x64gen.flagsactive=true; + opcode(0).set64().setrm(4).setimm(CALLSTACK+8,1).Emit8(0x83); // add rsp,16/48 + } +} + +static void gen_needcarry(void) { + if (!x64gen.flagsactive) { + x64gen.flagsactive=true; + opcode(4).setea(4,-1,0,CALLSTACK).setimm(0,1).Emit16(0xBA0F); // bt [rsp+8/40], 0 + opcode(4).set64().setea(4,-1,0,CALLSTACK+8).Emit8(0x8D); // lea rsp, [rsp+16/48] + } +} + +static void gen_setzeroflag(void) { + if (x64gen.flagsactive) IllegalOption("gen_setzeroflag"); + opcode(1).setea(4,-1,0,CALLSTACK).setimm(0x40,1).Emit8(0x83); // or dword [rsp+8/40],0x40 +} + +static void gen_clearzeroflag(void) { + if (x64gen.flagsactive) IllegalOption("gen_clearzeroflag"); + opcode(4).setea(4,-1,0,CALLSTACK).setimm(~0x40,1).Emit8(0x83); // and dword [rsp+8/40],~0x40 +} + +static bool skip_flags=false; + +static void set_skipflags(bool state) { + if (!state) gen_discardflags(); + skip_flags=state; +} + +static void gen_reinit(void) { + x64gen.last_used=0; + x64gen.flagsactive=false; + for (Bitu i=0;idynreg=0; + } +} + +static void gen_load_host(void * data,DynReg * dr1,Bitu size) { + opcode op = opcode(FindDynReg(dr1,true)->index).setabsaddr(data); + switch (size) { + case 1: // movzx r32, byte[] + op.Emit16(0xB60F); + break; + case 2: // movzx r32, word[] + op.Emit16(0xB70F); + break; + case 4: // mov r32, [] + op.Emit8(0x8B); + break; + default: + IllegalOption("gen_load_host"); + } + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_mov_host(void * data,DynReg * dr1,Bitu size,Bitu di1=0) { + int idx = FindDynReg(dr1,size==4)->index; + opcode op; + Bit8u tmp; + switch (size) { + case 1: + op.setreg(idx,di1); + tmp = 0x8A; // mov r8, [] + break; + case 2: op.setword(); // mov r16, [] + case 4: op.setreg(idx); + tmp = 0x8B; // mov r32, [] + break; + default: + IllegalOption("gen_mov_host"); + } + op.setabsaddr(data).Emit8(tmp); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_load_arg_reg(int argno,DynReg *dr,const char *s) { + GenReg *gen = x64gen.regs[reg_args[argno]]; + GenReg *src = dr->genreg; + opcode op(gen->index); + + if (*s=='r') { + s++; + gen_releasereg(dr); + } + + gen->Clear(); + + switch (*s) { + case 'h': + if (src) { + if (src->index>3 || gen->index>3) { + // shld r32,r32,24 + opcode(src->index).setimm(24,1).setrm(gen->index).Emit16(0xA40F); + op.setrm(gen->index,0); + } else op.setrm(src->index,4); + } else op.setabsaddr(((Bit8u*)dr->data)+1); + op.Emit16(0xB60F); // movzx r32, r/m8 + break; + case 'l': + if (src) op.setrm(src->index,0); + else op.setabsaddr(dr->data); + op.Emit16(0xB60F); // movzx r32, r/m8 + break; + case 'w': + if (src) op.setrm(src->index); + else op.setabsaddr(dr->data); + op.Emit16(0xB70F); // movzx r32, r/m16 + break; + case 'd': + if (src) { + if (src != gen) op.setrm(src->index).Emit8(0x8B); + } else op.setabsaddr(dr->data).Emit8(0x8B); + break; + default: + IllegalOption("gen_load_arg_reg param:DREG"); + } +} + +static void gen_load_imm(int index,Bitu imm) { + if (imm==0) + opcode(index).setrm(index).Emit8(0x33); // xor r32,r32 + else if ((Bit32u)imm==imm) + opcode(index).setimm(imm,4).Emit8Reg(0xB8); // MOV r32, imm32 + else if ((Bit32s)imm==(Bits)imm) + opcode(0).set64().setimm(imm,4).setrm(index).Emit8(0xC7); // mov r64, simm32 + else + opcode(index).set64().setabsaddr((void*)imm).Emit8(0x8D); // lea r64, [imm] +} + +static void gen_dop_byte(DualOps op,DynReg * dr1,Bitu di1,DynReg * dr2,Bitu di2) { + Bit8u tmp; + opcode i(FindDynReg(dr1)->index,true,di1); + i.setrm(FindDynReg(dr2)->index,di2); + + switch (op) { + case DOP_ADD: tmp=0x02; break; + case DOP_ADC: tmp=0x12; break; + case DOP_SUB: tmp=0x2a; break; + case DOP_SBB: tmp=0x1a; break; + case DOP_CMP: tmp=0x3a; goto nochange; + case DOP_XOR: tmp=0x32; break; + case DOP_AND: tmp=0x22; if ((dr1==dr2) && (di1==di2)) goto nochange; break; + case DOP_OR: tmp=0x0a; if ((dr1==dr2) && (di1==di2)) goto nochange; break; + case DOP_TEST: tmp=0x84; goto nochange; + case DOP_MOV: if ((dr1==dr2) && (di1==di2)) return; tmp=0x8a; break; + case DOP_XCHG: tmp=0x86; dr2->flags|=DYNFLG_CHANGED; break; + default: + IllegalOption("gen_dop_byte"); + } + dr1->flags|=DYNFLG_CHANGED; +nochange: + i.Emit8(tmp); +} + +static void gen_dop_byte_imm(DualOps op,DynReg * dr1,Bitu di1,Bitu imm) { + Bit8u tmp=0x80; + int dst = FindDynReg(dr1)->index; + opcode i; + i.setimm(imm,1); + imm &= 0xff; + + switch (op) { + case DOP_ADD: i.setreg(0); if (!imm) goto nochange; break; + case DOP_ADC: i.setreg(2); break; + case DOP_SUB: i.setreg(5); if (!imm) goto nochange; break; + case DOP_SBB: i.setreg(3); break; + case DOP_CMP: i.setreg(7); goto nochange; //Doesn't change + case DOP_XOR: i.setreg(6); if (!imm) goto nochange; break; + case DOP_AND: i.setreg(4); if (imm==255) goto nochange; break; + case DOP_OR: i.setreg(1); if (!imm) goto nochange; break; + case DOP_TEST: i.setreg(0);tmp=0xF6;goto nochange; + case DOP_MOV: i.setreg(dst,di1).Emit8Reg(0xB0); + dr1->flags|=DYNFLG_CHANGED; + return; + default: + IllegalOption("gen_dop_byte_imm"); + } + dr1->flags|=DYNFLG_CHANGED; +nochange: + i.setrm(dst,di1).Emit8(tmp); +} + +static void gen_dop_byte_imm_mem(DualOps op,DynReg * dr1,Bitu di1,void* data) { + opcode i; + Bits addr = (Bits)data; + Bits rbpdiff = addr - (Bits)&cpu_regs; + Bits ripdiff = addr - (Bits)cache.pos; + if (ripdiff<0) ripdiff = ~ripdiff+32; + if ((Bit32s)addr==addr || (Bit32s)rbpdiff==rbpdiff || ripdiff < 0x7FFFFFE0ll) + i = opcode(FindDynReg(dr1)->index,true,di1).setabsaddr(data); + else { + GenReg* dst = FindDynReg(dr1); + dst->notusable=true; + int src = GetNextReg(di1!=0); + dst->notusable=false; + if ((Bit32u)addr == (Bitu)addr) opcode(src).setimm(addr,4).Emit8Reg(0xB8); + else opcode(src).setimm(addr,8).set64().Emit8Reg(0xB8); + i = opcode(dst->index,true,di1).setea(src); + } + + Bit8u tmp; + switch (op) { + case DOP_ADD: tmp=0x02; break; + case DOP_ADC: tmp=0x12; break; + case DOP_SUB: tmp=0x2a; break; + case DOP_SBB: tmp=0x1a; break; + case DOP_CMP: tmp=0x3a; goto nochange; //Doesn't change + case DOP_XOR: tmp=0x32; break; + case DOP_AND: tmp=0x22; break; + case DOP_OR: tmp=0x0a; break; + case DOP_TEST: tmp=0x84; goto nochange; //Doesn't change + case DOP_MOV: tmp=0x8A; break; + default: + IllegalOption("gen_dop_byte_imm_mem"); + } + dr1->flags|=DYNFLG_CHANGED; +nochange: + i.Emit8(tmp); +} + +static void gen_sop_byte(SingleOps op,DynReg * dr1,Bitu di1) { + Bit8u tmp; + int dst = FindDynReg(dr1)->index; + opcode i; + + switch (op) { + case SOP_INC: i.setreg(0);tmp=0xFE; break; + case SOP_DEC: i.setreg(1);tmp=0xFE; break; + case SOP_NOT: i.setreg(2);tmp=0xF6; break; + case SOP_NEG: i.setreg(3);tmp=0xF6; break; + default: + IllegalOption("gen_sop_byte"); + } + i.setrm(dst,di1).Emit8(tmp); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_extend_word(bool sign,DynReg * ddr,DynReg * dsr) { + if (ddr==dsr && dsr->genreg==NULL) + opcode(FindDynReg(ddr,true)->index).setabsaddr(dsr->data).Emit16(sign ? 0xBF0F:0xB70F); + else { + int src = FindDynReg(dsr)->index; + int dst = FindDynReg(ddr,true)->index; + if (sign && (src|dst)==0) cache_addb(0x98); // cwde + else opcode(dst).setrm(src).Emit16(sign ? 0xBF0F:0xB70F); // movsx/movzx dst32, src16 + } + + ddr->flags|=DYNFLG_CHANGED; +} + +static void gen_extend_byte(bool sign,bool dword,DynReg * ddr,DynReg * dsr,Bitu dsi) { + if (ddr==dsr && dword && dsr->genreg==NULL) { + opcode op = opcode(FindDynReg(ddr,true)->index); + if (dsi) op.setabsaddr((void*)(((Bit8u*)dsr->data)+1)); + else op.setabsaddr(dsr->data); + op.Emit16(sign ? 0xBE0F:0xB60F); // movsx/movzx r32,m8 + } else { + int src = FindDynReg(dsr)->index; + int dst = FindDynReg(ddr,dword)->index; + if (dsi && (src>3 || dst>=8)) { // high-byte + REX = extra work required + // high-byte + REX prefix = extra work required: + // move source high-byte to dest low-byte then extend dest + gen_protectflags(); // shld changes flags, movzx/movsx does not + + // shld r16, r16, 8 + opcode(src,false).setimm(8,1).setrm(dst).Emit16(0xA40F); + src = dst; + dsi = 0; + } + if (sign && !dword && (src|dst|dsi)==0) cache_addw(0x9866); // cbw + else opcode(dst,dword).setrm(src,dsi).Emit16(sign ? 0xBE0F:0xB60F); + } + ddr->flags|=DYNFLG_CHANGED; +} + +static void gen_lea(DynReg * ddr,DynReg * dsr1,DynReg * dsr2,Bitu scale,Bits imm) { + if (ddr==dsr1 && dsr2==NULL && !imm) + return; + if (ddr==dsr2 && dsr1==NULL) { + if (!scale && !imm) + return; + else if (scale<2) { + // change [2*reg] to [reg+reg] + // or [0+1*reg] to [reg+0*reg] + // (index with no base requires 32-bit offset) + dsr1 = dsr2; + if (!scale) dsr2 = NULL; + else scale = 0; + } + } + + GenReg * gdr=FindDynReg(ddr,ddr!=dsr1 && ddr!=dsr2); + + int idx1 = dsr1 ? FindDynReg(dsr1)->index : -1; + int idx2 = dsr2 ? FindDynReg(dsr2)->index : -1; + + if (idx1==13 && dsr2 && idx2!=13 && !scale && !imm) { + // use r13 as index instead of base to avoid mandatory offset + int s = idx1; + idx1 = idx2; + idx2 = s; + } + + opcode(gdr->index).setea(idx1, idx2, scale, imm).Emit8(0x8D); + ddr->flags|=DYNFLG_CHANGED; +} + +static void gen_dop_word(DualOps op,bool dword,DynReg * dr1,DynReg * dr2) { + Bit8u tmp; + GenReg *gr2 = FindDynReg(dr2); + GenReg *gr1 = FindDynReg(dr1,dword && op==DOP_MOV); + + switch (op) { + case DOP_ADD: tmp=0x03; break; + case DOP_ADC: tmp=0x13; break; + case DOP_SUB: tmp=0x2b; break; + case DOP_SBB: tmp=0x1b; break; + case DOP_CMP: tmp=0x3b; goto nochange; + case DOP_XOR: tmp=0x33; break; + case DOP_AND: tmp=0x23; if (dr1==dr2) goto nochange; break; + case DOP_OR: tmp=0x0b; if (dr1==dr2) goto nochange; break; + case DOP_TEST: tmp=0x85; goto nochange; + case DOP_MOV: if (dr1==dr2) return; tmp=0x8b; break; + case DOP_XCHG: + dr2->flags|=DYNFLG_CHANGED; + if (dword && !((dr1->flags&DYNFLG_HAS8) ^ (dr2->flags&DYNFLG_HAS8))) { + dr1->genreg=gr2;gr2->dynreg=dr1; + dr2->genreg=gr1;gr1->dynreg=dr2; + dr1->flags|=DYNFLG_CHANGED; + return; + } + tmp=0x87; + break; + default: + IllegalOption("gen_dop_word"); + } + dr1->flags|=DYNFLG_CHANGED; +nochange: + opcode(gr1->index,dword).setrm(gr2->index).Emit8(tmp); +} + +static void gen_dop_word_imm(DualOps op,bool dword,DynReg * dr1,Bits imm) { + Bit8u tmp=0x81; + int dst = FindDynReg(dr1,dword && op==DOP_MOV)->index; + opcode i; + if (!dword) { + i.setword(); + imm = (Bit16s)imm; + } else imm = (Bit32s)imm; + if (op <= DOP_OR && (Bit8s)imm==imm) { + i.setimm(imm, 1); + tmp = 0x83; + } else i.setimm(imm, dword?4:2); + + switch (op) { + case DOP_ADD: i.setreg(0); if (!imm) goto nochange; break; + case DOP_ADC: i.setreg(2); break; + case DOP_SUB: i.setreg(5); if (!imm) goto nochange; break; + case DOP_SBB: i.setreg(3); break; + case DOP_CMP: i.setreg(7); goto nochange; //Doesn't change + case DOP_XOR: i.setreg(6); if (!imm) goto nochange; break; + case DOP_AND: i.setreg(4); if (imm==-1) goto nochange; break; + case DOP_OR: i.setreg(1); if (!imm) goto nochange; break; + case DOP_TEST: i.setreg(0);tmp=0xF7; goto nochange; //Doesn't change + case DOP_MOV: i.setreg(dst).Emit8Reg(0xB8); dr1->flags|=DYNFLG_CHANGED; return; + default: + IllegalOption("gen_dop_word_imm"); + } + dr1->flags|=DYNFLG_CHANGED; +nochange: + i.setrm(dst).Emit8(tmp); +} + +static void gen_dop_word(DualOps op,DynReg *dr1,opcode &i) { + Bit8u tmp; + switch (op) { + case DOP_ADD: tmp=0x03; break; + case DOP_ADC: tmp=0x13; break; + case DOP_SUB: tmp=0x2b; break; + case DOP_SBB: tmp=0x1b; break; + case DOP_CMP: tmp=0x3b; goto nochange; //Doesn't change + case DOP_XOR: tmp=0x33; break; + case DOP_AND: tmp=0x23; break; + case DOP_OR: tmp=0x0b; break; + case DOP_TEST: tmp=0x85; goto nochange; //Doesn't change + case DOP_MOV: tmp=0x8b; break; + case DOP_XCHG: tmp=0x87; break; + default: + IllegalOption("gen_dop_word0"); + } + dr1->flags|=DYNFLG_CHANGED; +nochange: + i.Emit8(tmp); +} + +static void gen_dop_word_var(DualOps op,bool dword,DynReg * dr1,void* drd) { + opcode i = opcode(FindDynReg(dr1,dword && op==DOP_MOV)->index,dword).setabsaddr(drd); + gen_dop_word(op,dr1,i); +} + +static void gen_dop_word_imm_mem(DualOps op,bool dword,DynReg * dr1,void* data) { + opcode i; + Bits addr = (Bits)data; + Bits rbpdiff = addr - (Bits)&cpu_regs; + Bits ripdiff = addr - (Bits)cache.pos; + if (ripdiff<0) ripdiff = ~ripdiff+32; + if ((Bit32s)addr==addr || (Bit32s)rbpdiff==rbpdiff || ripdiff < 0x7FFFFFE0ll) + i = opcode(FindDynReg(dr1,dword && op==DOP_MOV)->index,dword).setabsaddr(data); + else if (dword && op==DOP_MOV) { + if (dr1->genreg) dr1->genreg->dynreg=0; + x64gen.regs[X64_REG_RAX]->Load(dr1,true); + if ((Bit32u)addr == (Bitu)addr) { + cache_addb(0x67); + opcode(0).setimm(addr,4).Emit8Reg(0xA1); + } else opcode(0).setimm(addr,8).Emit8Reg(0xA1); + dr1->flags|=DYNFLG_CHANGED; + return; + } else { + GenReg* dst = FindDynReg(dr1,false); + dst->notusable=true; + int src = GetNextReg(); + dst->notusable=false; + if ((Bit32u)addr == (Bitu)addr) opcode(src).setimm(addr,4).Emit8Reg(0xB8); + else opcode(src).setimm(addr,8).set64().Emit8Reg(0xB8); + i = opcode(dst->index,dword).setea(src); + } + gen_dop_word(op,dr1,i); +} + +static void gen_lea_imm_mem(DynReg * ddr,DynReg * dsr,void* data) { + gen_dop_word_imm_mem(DOP_MOV,true,ddr,data); + gen_lea(ddr, ddr, dsr, 0, 0); +} + +static void gen_imul_word(bool dword,DynReg * dr1,DynReg * dr2) { + // dr1 = dr1*dr2 + opcode(FindDynReg(dr1)->index,dword).setrm(FindDynReg(dr2)->index).Emit16(0xAF0F); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_imul_word_imm(bool dword,DynReg * dr1,DynReg * dr2,Bits imm) { + // dr1 = dr2*imm + opcode op; + if (dr1==dr2 && dword && dr1->genreg==NULL) + op = opcode(FindDynReg(dr1,true)->index).setabsaddr(dr2->data); + else + op = opcode(FindDynReg(dr1,dword&&dr1!=dr2)->index,dword).setrm(FindDynReg(dr2)->index); + + if ((Bit8s)imm==imm) op.setimm(imm,1).Emit8(0x6B); + else op.setimm(imm,dword?4:2).Emit8(0x69); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_sop_word(SingleOps op,bool dword,DynReg * dr1) { + opcode i; + Bit8u tmp; + if (!dword) i.setword(); + switch (op) { + case SOP_INC: i.setreg(0);tmp=0xFF;break; + case SOP_DEC: i.setreg(1);tmp=0xFF;break; + case SOP_NOT: i.setreg(2);tmp=0xF7;break; + case SOP_NEG: i.setreg(3);tmp=0xF7;break; + default: + IllegalOption("gen_sop_word"); + } + i.setrm(FindDynReg(dr1)->index).Emit8(tmp); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_shift_byte_cl(Bitu op,DynReg * dr1,Bitu di1,DynReg * drecx) { + ForceDynReg(x64gen.regs[X64_REG_RCX],drecx); + opcode((int)op).setrm(FindDynReg(dr1)->index,di1).Emit8(0xD2); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_shift_byte_imm(Bitu op,DynReg * dr1,Bitu di1,Bit8u imm) { + opcode inst = opcode((int)op).setrm(FindDynReg(dr1)->index,di1); + if (imm==1) inst.Emit8(0xD0); + else inst.setimm(imm,1).Emit8(0xC0); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_shift_word_cl(Bitu op,bool dword,DynReg * dr1,DynReg * drecx) { + ForceDynReg(x64gen.regs[X64_REG_RCX],drecx); + opcode((int)op,dword).setrm(FindDynReg(dr1)->index).Emit8(0xD3); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_shift_word_imm(Bitu op,bool dword,DynReg * dr1,Bit8u imm) { + opcode inst = opcode((int)op,dword).setrm(FindDynReg(dr1)->index); + if (imm==1) inst.Emit8(0xD1); + else inst.setimm(imm,1).Emit8(0xC1); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_cbw(bool dword,DynReg * dyn_ax) { + if (dword) gen_extend_word(true,dyn_ax,dyn_ax); + else gen_extend_byte(true,false,dyn_ax,dyn_ax,0); +} + +static void gen_cwd(bool dword,DynReg * dyn_ax,DynReg * dyn_dx) { + if (dyn_dx->genreg != x64gen.regs[X64_REG_RDX]) { + if (dword) { + if (dyn_dx->genreg) dyn_dx->genreg->dynreg = NULL; + x64gen.regs[X64_REG_RDX]->Load(dyn_dx,true); + } else ForceDynReg(x64gen.regs[X64_REG_RDX],dyn_dx); + } + ForceDynReg(x64gen.regs[X64_REG_RAX],dyn_ax); + dyn_dx->flags|=DYNFLG_CHANGED; + if (!dword) cache_addw(0x9966); + else cache_addb(0x99); +} + +static void gen_mul_byte(bool imul,DynReg * dyn_ax,DynReg * dr1,Bitu di1) { + ForceDynReg(x64gen.regs[X64_REG_RAX],dyn_ax); + opcode(imul?5:4).setrm(FindDynReg(dr1)->index,di1).Emit8(0xF6); + dyn_ax->flags|=DYNFLG_CHANGED; +} + +static void gen_mul_word(bool imul,DynReg * dyn_ax,DynReg * dyn_dx,bool dword,DynReg * dr1) { + ForceDynReg(x64gen.regs[X64_REG_RAX],dyn_ax); + if (dword && dyn_dx!=dr1) { + // release current genreg + if (dyn_dx->genreg) dyn_dx->genreg->dynreg = NULL; + x64gen.regs[X64_REG_RDX]->Load(dyn_dx,true); + } else ForceDynReg(x64gen.regs[X64_REG_RDX],dyn_dx); + opcode(imul?5:4,dword).setrm(FindDynReg(dr1)->index).Emit8(0xF7); + dyn_ax->flags|=DYNFLG_CHANGED; + dyn_dx->flags|=DYNFLG_CHANGED; +} + +static void gen_dshift_imm(bool dword,bool left,DynReg * dr1,DynReg * dr2,Bitu imm) { + // shld/shrd imm + opcode(FindDynReg(dr2)->index,dword).setimm(imm,1).setrm(FindDynReg(dr1)->index).Emit16(left ? 0xA40F:0xAC0F); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_dshift_cl(bool dword,bool left,DynReg * dr1,DynReg * dr2,DynReg * drecx) { + ForceDynReg(x64gen.regs[X64_REG_RCX],drecx); + // shld/shrd cl + opcode(FindDynReg(dr2)->index,dword).setrm(FindDynReg(dr1)->index).Emit16(left ? 0xA50F:0xAD0F); + dr1->flags|=DYNFLG_CHANGED; +} + +static void gen_call_ptr(void *func=NULL, Bit8u ptr=0) { + x64gen.regs[X64_REG_RAX]->Clear(); + x64gen.regs[X64_REG_RCX]->Clear(); + x64gen.regs[X64_REG_RDX]->Clear(); +#if !defined(_WIN64) + x64gen.regs[X64_REG_RSI]->Clear(); + x64gen.regs[X64_REG_RDI]->Clear(); +#endif + x64gen.regs[X64_REG_R8]->Clear(); + x64gen.regs[X64_REG_R9]->Clear(); + x64gen.regs[X64_REG_R10]->Clear(); + x64gen.regs[X64_REG_R11]->Clear(); + + /* Do the actual call to the procedure */ + if (func!=NULL) { + Bits diff = (Bits)func - (Bits)cache.pos - 5; + if ((Bit32s)diff == diff) { + opcode(0).setimm(diff,4).Emit8Reg(0xE8); // call rel32 + return; + } + gen_load_imm(ptr, (Bitu)func); + } + opcode(2).setrm(ptr).Emit8(0xFF); // call ptr +} + +static void gen_call_function(void * func,const char* ops,...) { + Bitu paramcount=0; + va_list params; + DynReg *dynret=NULL; + char rettype; + + /* Save the flags */ + if (GCC_LIKELY(!skip_flags)) gen_protectflags(); + if (ops==NULL) IllegalOption("gen_call_function NULL format"); + va_start(params,ops); + while (*ops) { + if (*ops++=='%') { + GenReg *gen; + switch (*ops++) { + case 'I': /* immediate value */ + gen = x64gen.regs[reg_args[paramcount++]]; + gen->Clear(); + if (*ops++!='p') gen_load_imm(gen->index,va_arg(params,Bit32u)); + else gen_load_imm(gen->index,va_arg(params,Bitu)); + break; + case 'D': /* Dynamic register */ + gen_load_arg_reg((int)paramcount++, va_arg(params,DynReg*), ops++); + break; + case 'R': /* Dynamic register for returned value */ + dynret = va_arg(params,DynReg*); + rettype = *ops++; + break; + case 'F': /* arg is flags, release */ + gen = x64gen.regs[reg_args[paramcount++]]; + gen->Clear(); + gen_protectflags(); + opcode(gen->index).setea(4,-1,0,CALLSTACK).Emit8(0x8B); // mov reg, [rsp+8/40] + opcode(0).set64().setimm(CALLSTACK+8,1).setrm(4).Emit8(0x83); // add rsp,16/48 + break; + default: + IllegalOption("gen_call_function unknown param"); + } + } + } + va_end(params); + + gen_call_ptr(func); + + /* Save the return value in correct register */ + if (dynret) { + GenReg * genret; + if (rettype == 'd') { + genret=x64gen.regs[X64_REG_RAX]; + if (dynret->genreg) dynret->genreg->dynreg=0; + genret->Load(dynret,true); + } else { + opcode op(0); // src=eax/ax/al/ah + x64gen.regs[X64_REG_RAX]->notusable = true; + genret = FindDynReg(dynret); + x64gen.regs[X64_REG_RAX]->notusable = false; + switch (rettype) { + case 'w': + // mov r16, ax + op.setword().setrm(genret->index).Emit8(0x89); + break; + case 'h': + // mov reg8h, al + op.setrm(genret->index,4).Emit8(0x88); + break; + case 'l': + // mov r/m8, al + op.setrm(genret->index,0).Emit8(0x88); + break; + } + } + dynret->flags|=DYNFLG_CHANGED; + } +} + +static void gen_call_write(DynReg * dr,Bit32u val,Bitu write_size) { + void *func; + gen_protectflags(); + gen_load_arg_reg(0,dr,"rd"); + + switch (write_size) { + case 1: func = (void*)mem_writeb_checked; break; + case 2: func = (void*)mem_writew_checked; break; + case 4: func = (void*)mem_writed_checked; break; + default: IllegalOption("gen_call_write"); + } + + x64gen.regs[reg_args[1]]->Clear(); + opcode(ARG1_REG).setimm(val,4).Emit8Reg(0xB8); // mov ARG2, imm32 + gen_call_ptr(func); +} + +static Bit8u * gen_create_branch(BranchTypes type) { + /* First free all registers */ + cache_addw(0x70+type); + return (cache.pos-1); +} + +static void gen_fill_branch(Bit8u * data,Bit8u * from=cache.pos) { +#if C_DEBUG + Bits len=from-data-1; + if (len<0) len=~len; + if (len>127) + LOG_MSG("Big jump %d",len); +#endif + *data=(Bit8u)(from-data-1); +} + +static Bit8u * gen_create_branch_long(BranchTypes type) { + cache_addw(0x800f+(type<<8)); + cache_addd(0); + return (cache.pos-4); +} + +static void gen_fill_branch_long(Bit8u * data,Bit8u * from=cache.pos) { + *(Bit32u*)data=(Bit32u)(from-data-4); +} + +static Bit8u * gen_create_jump(Bit8u * to=0) { + /* First free all registers */ + cache_addb(0xe9); + cache_addd((Bit32u)(to-(cache.pos+4))); + return (cache.pos-4); +} + +static void gen_fill_jump(Bit8u * data,Bit8u * to=cache.pos) { + *(Bit32u*)data=(Bit32u)(to-data-4); +} + +static Bit8u * gen_create_short_jump(void) { + cache_addw(0x00EB); + return cache.pos-1; +} + +static void gen_fill_short_jump(Bit8u * data, Bit8u * to=cache.pos) { +#if C_DEBUG + Bits len=to-data-1; + if (len<0) len=~len; + if (len>127) + LOG_MSG("Big jump %d",len); +#endif + data[0] = (Bit8u)(to-data-1); +} + +static void gen_jmp_ptr(void * _ptr,Bit32s imm=0) { + Bitu ptr = (Bitu)_ptr; + if ((Bit32u)ptr == ptr) { + cache_addb(0x67); // 32-bit abs address + opcode(0).set64().setimm(ptr,4).Emit8Reg(0xA1); + } else opcode(0).set64().setimm(ptr,8).Emit8Reg(0xA1); + opcode(4).setea(0,-1,0,imm).Emit8(0xFF); // jmp [rax+imm] +} + +static void gen_save_flags(DynReg * dynreg) { + if (GCC_UNLIKELY(x64gen.flagsactive)) IllegalOption("gen_save_flags"); + opcode(FindDynReg(dynreg)->index).setea(4,-1,0,CALLSTACK).Emit8(0x8B); // mov reg32, [rsp+8/40] + dynreg->flags|=DYNFLG_CHANGED; +} + +static void gen_load_flags(DynReg * dynreg) { + if (GCC_UNLIKELY(x64gen.flagsactive)) IllegalOption("gen_load_flags"); + opcode(FindDynReg(dynreg)->index).setea(4,-1,0,CALLSTACK).Emit8(0x89); // mov [rsp+8/40],reg32 +} + +static void gen_save_host_direct(void *data,Bitu imm) { + if ((Bit32s)imm != (Bits)imm) { + opcode(0).setimm(imm,4).setabsaddr(data).Emit8(0xC7); // mov dword[], imm32 (low dword) + opcode(0).setimm(imm>>32,4).setabsaddr((Bit8u*)data+4).Emit8(0xC7); // high dword + } else + opcode(0).set64().setimm(imm,4).setabsaddr(data).Emit8(0xC7); // mov qword[], Bit32s +} + +static void gen_return(BlockReturn retcode) { + gen_protectflags(); + opcode(1).setea(4,-1,0,CALLSTACK).Emit8(0x8B); // mov ecx, [rsp+8/40] + opcode(0).set64().setrm(4).setimm(CALLSTACK+8,1).Emit8(0x83); // add rsp,16/48 + if (retcode==0) cache_addw(0xc033); // xor eax,eax + else { + cache_addb(0xb8); //MOV EAX, retcode + cache_addd(retcode); + } + opcode(4).setea(4,-1,0,CALLSTACK-8).Emit8(0xFF); // jmp [rsp+CALLSTACK-8] +} + +static void gen_return_fast(BlockReturn retcode,bool ret_exception=false) { + if (GCC_UNLIKELY(x64gen.flagsactive)) IllegalOption("gen_return_fast"); + opcode(1).setabsaddr(®_flags).Emit8(0x8B); // mov ECX, [cpu_regs.flags] + if (!ret_exception) { + opcode(0).set64().setrm(4).setimm(CALLSTACK+8,1).Emit8(0x83); // add rsp,16/48 + if (retcode==0) cache_addw(0xc033); // xor eax,eax + else { + cache_addb(0xb8); //MOV EAX, retcode + cache_addd(retcode); + } + } + opcode(4).setea(4,-1,0,CALLSTACK-8).Emit8(0xFF); // jmp [rsp+CALLSTACK] +} + +static void gen_init(void) { + x64gen.regs[X64_REG_RAX]=new GenReg(0); + x64gen.regs[X64_REG_RCX]=new GenReg(1); + x64gen.regs[X64_REG_RDX]=new GenReg(2); + x64gen.regs[X64_REG_RBX]=new GenReg(3); + x64gen.regs[X64_REG_RSI]=new GenReg(6); + x64gen.regs[X64_REG_RDI]=new GenReg(7); + x64gen.regs[X64_REG_R8]=new GenReg(8); + x64gen.regs[X64_REG_R9]=new GenReg(9); + x64gen.regs[X64_REG_R10]=new GenReg(10); + x64gen.regs[X64_REG_R11]=new GenReg(11); + x64gen.regs[X64_REG_R12]=new GenReg(12); + x64gen.regs[X64_REG_R13]=new GenReg(13); + x64gen.regs[X64_REG_R14]=new GenReg(14); + x64gen.regs[X64_REG_R15]=new GenReg(15); +} + +#if defined(X86_DYNFPU_DH_ENABLED) +static void gen_dh_fpu_saveInit(void); +static void (*gen_dh_fpu_save)(void) = gen_dh_fpu_saveInit; + +// DO NOT USE opcode::setabsaddr IN THIS FUNCTION (RBP unavailable at execution time) +static void gen_dh_fpu_saveInit(void) { + Bit8u* oldpos = cache.pos; + cache.pos = &cache_code_link_blocks[64]; + gen_dh_fpu_save = (void(*)(void))cache.pos; + + Bitu addr = (Bitu)&dyn_dh_fpu; + // mov RAX, &dyn_dh_fpu + if ((Bit32u)addr == addr) opcode(0).setimm(addr,4).Emit8Reg(0xB8); + else opcode(0).set64().setimm(addr,8).Emit8Reg(0xB8); + + // fnsave [dyn_dh_fpu.state] + opcode(6).setea(0,-1,0,offsetof(struct dyn_dh_fpu,state)).Emit8(0xdd); + // fldcw [dyn_dh_fpu.host_cw] + opcode(5).setea(0,-1,0,offsetof(struct dyn_dh_fpu,host_cw)).Emit8(0xd9); + // mov byte [dyn_dh_fpu.state_used], 0 + opcode(0).setimm(0,1).setea(0,-1,0,offsetof(struct dyn_dh_fpu,state_used)).Emit8(0xc6); + // or byte [dyn_dh_fpu.state.cw], 0x3F + opcode(1).setimm(0x3F,1).setea(0,-1,0,offsetof(struct dyn_dh_fpu,state.cw)).Emit8(0x80); + cache_addb(0xC3); // RET + + cache.pos = oldpos; + gen_dh_fpu_save(); +} +#endif + diff --git a/src/cpu/core_dyn_x86/risc_x86.h b/src/cpu/core_dyn_x86/risc_x86.h index fdde79b..6b1cc9e 100644 --- a/src/cpu/core_dyn_x86/risc_x86.h +++ b/src/cpu/core_dyn_x86/risc_x86.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -311,16 +311,16 @@ static void gen_load_host(void * data,DynReg * dr1,Bitu size) { dr1->flags|=DYNFLG_CHANGED; } -static void gen_mov_host(void * data,DynReg * dr1,Bitu size,Bit8u di1=0) { +static void gen_mov_host(void * data,DynReg * dr1,Bitu size,Bitu di1=0) { GenReg * gr1=FindDynReg(dr1,(size==4)); switch (size) { case 1:cache_addb(0x8a);break; //mov byte case 2:cache_addb(0x66); //mov word case 4:cache_addb(0x8b);break; //mov default: - IllegalOption("gen_load_host"); + IllegalOption("gen_mov_host"); } - cache_addb(0x5+((gr1->index+(di1?4:0))<<3)); + cache_addb(0x5+((gr1->index+di1)<<3)); cache_addd((Bit32u)data); dr1->flags|=DYNFLG_CHANGED; } @@ -388,7 +388,7 @@ static void gen_dop_byte_imm_mem(DualOps op,DynReg * dr1,Bit8u di1,void* data) { case DOP_AND: tmp=0x0522; break; case DOP_OR: tmp=0x050a; break; case DOP_TEST: tmp=0x0584; goto nochange; //Doesn't change - case DOP_MOV: tmp=0x0585; break; + case DOP_MOV: tmp=0x058A; break; default: IllegalOption("gen_dop_byte_imm_mem"); } @@ -769,6 +769,7 @@ static void gen_call_function(void * func,char const* ops,...) { } ops++; } + va_end(params); #if defined (MACOSX) /* align stack */ @@ -1068,4 +1069,28 @@ static void gen_init(void) { x86gen.regs[X86_REG_EDI]=new GenReg(7); } - +#if defined(X86_DYNFPU_DH_ENABLED) +static void gen_dh_fpu_save(void) +#if defined (_MSC_VER) +{ + __asm { + __asm fnsave dyn_dh_fpu.state + __asm fldcw dyn_dh_fpu.host_cw + } + dyn_dh_fpu.state_used=false; + dyn_dh_fpu.state.cw|=0x3f; +} +#else +{ + __asm__ volatile ( + "fnsave %0 \n" + "fldcw %1 \n" + : "=m" (dyn_dh_fpu.state) + : "m" (dyn_dh_fpu.host_cw) + : "memory" + ); + dyn_dh_fpu.state_used=false; + dyn_dh_fpu.state.cw|=0x3f; +} +#endif +#endif diff --git a/src/cpu/core_dyn_x86/string.h b/src/cpu/core_dyn_x86/string.h index 7881332..8b27672 100644 --- a/src/cpu/core_dyn_x86/string.h +++ b/src/cpu/core_dyn_x86/string.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ enum STRING_OP { @@ -82,7 +82,7 @@ static void dyn_string(STRING_OP op) { Bit8u * rep_ecx_jmp; /* Check if ECX!=zero */ if (decode.rep) { - gen_dop_word(DOP_OR,decode.big_addr,DREG(ECX),DREG(ECX)); + gen_dop_word(DOP_TEST,decode.big_addr,DREG(ECX),DREG(ECX)); rep_ecx_jmp=gen_create_branch_long(BR_Z); } if (usesi) { @@ -99,11 +99,11 @@ static void dyn_string(STRING_OP op) { } switch (op) { case STR_OUTSB: - gen_call_function((void*)&IO_WriteB,"%Id%Dl",DREG(EDX),tmp_reg);break; + gen_call_function((void*)&IO_WriteB,"%Dw%Dl",DREG(EDX),tmp_reg);break; case STR_OUTSW: - gen_call_function((void*)&IO_WriteW,"%Id%Dw",DREG(EDX),tmp_reg);break; + gen_call_function((void*)&IO_WriteW,"%Dw%Dw",DREG(EDX),tmp_reg);break; case STR_OUTSD: - gen_call_function((void*)&IO_WriteD,"%Id%Dd",DREG(EDX),tmp_reg);break; + gen_call_function((void*)&IO_WriteD,"%Dw%Dd",DREG(EDX),tmp_reg);break; } } if (usedi) { diff --git a/src/cpu/core_dynrec.cpp b/src/cpu/core_dynrec.cpp index ed975f5..9e23291 100644 --- a/src/cpu/core_dynrec.cpp +++ b/src/cpu/core_dynrec.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -137,7 +137,8 @@ static struct { #define X86_64 0x02 #define MIPSEL 0x03 #define ARMV4LE 0x04 -#define POWERPC 0x04 +#define ARMV7LE 0x05 +#define ARMV8LE 0x07 #if C_TARGETCPU == X86_64 #include "core_dynrec/risc_x64.h" @@ -145,10 +146,10 @@ static struct { #include "core_dynrec/risc_x86.h" #elif C_TARGETCPU == MIPSEL #include "core_dynrec/risc_mipsel32.h" -#elif C_TARGETCPU == ARMV4LE +#elif (C_TARGETCPU == ARMV4LE) || (C_TARGETCPU == ARMV7LE) #include "core_dynrec/risc_armv4le.h" -#elif C_TARGETCPU == POWERPC -#include "core_dynrec/risc_ppc.h" +#elif C_TARGETCPU == ARMV8LE +#include "core_dynrec/risc_armv8le.h" #endif #include "core_dynrec/decoder.h" @@ -158,16 +159,14 @@ CacheBlockDynRec * LinkBlocks(BlockReturn ret) { // the last instruction was a control flow modifying instruction Bitu temp_ip=SegPhys(cs)+reg_eip; CodePageHandlerDynRec * temp_handler=(CodePageHandlerDynRec *)get_tlb_readhandler(temp_ip); - if (temp_handler->flags & PFLAG_HASCODE) { + if (temp_handler->flags & (cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16)) { // see if the target is an already translated block block=temp_handler->FindCacheBlock(temp_ip & 4095); - if (!block) return NULL; - - // found it, link the current block to - cache.block.running->LinkTo(ret==BR_Link2,block); - return block; + if (block) { // found it, link the current block to + cache.block.running->LinkTo(ret==BR_Link2,block); + } } - return NULL; + return block; } /* @@ -219,7 +218,7 @@ Bits CPU_Core_Dynrec_Run(void) { continue; } CPU_CycleLeft+=old_cycles; - return nc_retcode; + return nc_retcode; } } @@ -231,8 +230,10 @@ run_block: switch (ret) { case BR_Iret: +#if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif #endif if (!GETFLAG(TF)) { if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE; @@ -247,16 +248,20 @@ run_block: // modifying instruction (like ret) or some nontrivial cpu state // changing instruction (for example switch to/from pmode), // or the maximum number of instructions to translate was reached +#if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif #endif break; case BR_Cycles: // cycles went negative, return from the core to handle // external events, schedule the pic... -#if C_HEAVY_DEBUG +#if C_DEBUG +#if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#endif #endif return CBRET_NONE; diff --git a/src/cpu/core_dynrec/Makefile.am b/src/cpu/core_dynrec/Makefile.am index 2bc15bb..f135543 100644 --- a/src/cpu/core_dynrec/Makefile.am +++ b/src/cpu/core_dynrec/Makefile.am @@ -1,5 +1,5 @@ noinst_HEADERS = cache.h decoder.h decoder_basic.h decoder_opcodes.h \ dyn_fpu.h operators.h risc_x64.h risc_x86.h risc_mipsel32.h \ risc_armv4le.h risc_armv4le-common.h \ - risc_armv4le-s3.h risc_armv4le-o3.h risc_armv4le-thumb.h \ - risc_armv4le-thumb-iw.h risc_armv4le-thumb-niw.h + risc_armv4le-o3.h risc_armv4le-thumb.h \ + risc_armv4le-thumb-iw.h risc_armv4le-thumb-niw.h risc_armv8le.h diff --git a/src/cpu/core_dynrec/cache.h b/src/cpu/core_dynrec/cache.h index 5c81436..9ae81eb 100644 --- a/src/cpu/core_dynrec/cache.h +++ b/src/cpu/core_dynrec/cache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -96,7 +96,7 @@ public: old_pagehandler=_old_pagehandler; // adjust flags - flags=old_pagehandler->flags|PFLAG_HASCODE; + flags=old_pagehandler->flags|(cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16); flags&=~PFLAG_WRITEABLE; active_blocks=0; @@ -596,14 +596,14 @@ static void cache_init(bool enable) { if(!cache_code_start_ptr) E_Exit("Allocating dynamic cache failed"); // align the cache at a page boundary - cache_code=(Bit8u*)(((long)cache_code_start_ptr + PAGESIZE_TEMP-1) & ~(PAGESIZE_TEMP-1)); //MEM LEAK. store old pointer if you want to free it. + cache_code=(Bit8u*)(((Bitu)cache_code_start_ptr + PAGESIZE_TEMP-1) & ~(PAGESIZE_TEMP-1));//Bitu is same size as a pointer. cache_code_link_blocks=cache_code; cache_code=cache_code+PAGESIZE_TEMP; #if (C_HAVE_MPROTECT) if(mprotect(cache_code_link_blocks,CACHE_TOTAL+CACHE_MAXSIZE+PAGESIZE_TEMP,PROT_WRITE|PROT_READ|PROT_EXEC)) - LOG_MSG("Setting excute permission on the code cache has failed"); + LOG_MSG("Setting execute permission on the code cache has failed"); #endif CacheBlockDynRec * block=cache_getblock(); cache.block.first=block; diff --git a/src/cpu/core_dynrec/decoder.h b/src/cpu/core_dynrec/decoder.h index 08b5244..b0bd8db 100644 --- a/src/cpu/core_dynrec/decoder.h +++ b/src/cpu/core_dynrec/decoder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -446,7 +446,9 @@ restart_prefix: case 0xcb:dyn_ret_far(0);goto finish_block; // int/iret +#if !(C_DEBUG) case 0xcd:dyn_interrupt(decode_fetchb());goto finish_block; +#endif case 0xcf:dyn_iret();goto finish_block; // case 0xd4: AAM missing diff --git a/src/cpu/core_dynrec/decoder_basic.h b/src/cpu/core_dynrec/decoder_basic.h index d09012c..c8e2a8e 100644 --- a/src/cpu/core_dynrec/decoder_basic.h +++ b/src/cpu/core_dynrec/decoder_basic.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -120,7 +120,7 @@ static struct DynDecode { // modrm state of the current instruction (if used) struct { - Bitu val; +// Bitu val; Bitu mod; Bitu rm; Bitu reg; @@ -130,21 +130,30 @@ static struct DynDecode { static bool MakeCodePage(Bitu lin_addr,CodePageHandlerDynRec * &cph) { Bit8u rdval; + const Bitu cflag = cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16; //Ensure page contains memory: if (GCC_UNLIKELY(mem_readb_checked(lin_addr,&rdval))) return true; PageHandler * handler=get_tlb_readhandler(lin_addr); if (handler->flags & PFLAG_HASCODE) { - // this is a codepage handler, and the one that we're looking for + // this is a codepage handler, make sure it matches current code size cph=(CodePageHandlerDynRec *)handler; - return false; + if (handler->flags & cflag) return false; + // wrong code size/stale dynamic code, drop it + cph->ClearRelease(); + cph=0; + // handler was changed, refresh + handler=get_tlb_readhandler(lin_addr); } if (handler->flags & PFLAG_NOCODE) { if (PAGING_ForcePageInit(lin_addr)) { handler=get_tlb_readhandler(lin_addr); if (handler->flags & PFLAG_HASCODE) { cph=(CodePageHandlerDynRec *)handler; - return false; + if (handler->flags & cflag) return false; + cph->ClearRelease(); + cph=0; + handler=get_tlb_readhandler(lin_addr); } } if (handler->flags & PFLAG_NOCODE) { @@ -372,10 +381,10 @@ static bool decode_fetchd_imm(Bitu & val) { // modrm decoding helper static void INLINE dyn_get_modrm(void) { - decode.modrm.val=decode_fetchb(); - decode.modrm.mod=(decode.modrm.val >> 6) & 3; - decode.modrm.reg=(decode.modrm.val >> 3) & 7; - decode.modrm.rm=(decode.modrm.val & 7); + Bitu val=decode_fetchb(); + decode.modrm.mod=(val >> 6) & 3; + decode.modrm.reg=(val >> 3) & 7; + decode.modrm.rm=(val & 7); } @@ -490,7 +499,8 @@ static INLINE void dyn_set_eip_end(void) { // set reg_eip to the start of the next instruction plus an offset (imm) static INLINE void dyn_set_eip_end(HostReg reg,Bit32u imm=0) { - gen_mov_word_to_reg(reg,®_eip,decode.big_op); + gen_mov_word_to_reg(reg,®_eip,true); //get_extend_word will mask off the upper bits + //gen_mov_word_to_reg(reg,®_eip,decode.big_op); gen_add_imm(reg,(Bit32u)(decode.code-decode.code_start+imm)); if (!decode.big_op) gen_extend_word(false,reg); } diff --git a/src/cpu/core_dynrec/decoder_opcodes.h b/src/cpu/core_dynrec/decoder_opcodes.h index 1a4ca6f..67eaee2 100644 --- a/src/cpu/core_dynrec/decoder_opcodes.h +++ b/src/cpu/core_dynrec/decoder_opcodes.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -444,14 +444,23 @@ static void dyn_push_word_imm(Bitu imm) { static void dyn_pop_ev(void) { dyn_get_modrm(); if (decode.modrm.mod<3) { -/* dyn_fill_ea(FC_ADDR); - gen_protect_addr_reg(); - dyn_read_word(FC_ADDR,FC_OP1,decode.big_op); // dummy read to trigger possible page faults */ + // save original ESP + MOV_REG_WORD32_TO_HOST_REG(FC_OP2,DRC_REG_ESP); + gen_protect_reg(FC_OP2); if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword); else gen_call_function_raw((void*)&dynrec_pop_word); dyn_fill_ea(FC_ADDR); -// gen_restore_addr_reg(); - dyn_write_word(FC_ADDR,FC_RETOP,decode.big_op); + gen_mov_regs(FC_OP2,FC_RETOP); + gen_mov_regs(FC_OP1,FC_ADDR); + if (decode.big_op) gen_call_function_raw((void *)&mem_writed_checked_drc); + else gen_call_function_raw((void *)&mem_writew_checked_drc); + gen_extend_byte(false,FC_RETOP); // bool -> dword + DRC_PTR_SIZE_IM no_fault = gen_create_branch_on_zero(FC_RETOP, true); + // restore original ESP + gen_restore_reg(FC_OP2); + MOV_REG_WORD32_FROM_HOST_REG(FC_OP2,DRC_REG_ESP); + dyn_check_exception(FC_RETOP); + gen_fill_branch(no_fault); } else { if (decode.big_op) gen_call_function_raw((void*)&dynrec_pop_dword); else gen_call_function_raw((void*)&dynrec_pop_word); diff --git a/src/cpu/core_dynrec/dyn_fpu.h b/src/cpu/core_dynrec/dyn_fpu.h index cc22e39..739b4bb 100644 --- a/src/cpu/core_dynrec/dyn_fpu.h +++ b/src/cpu/core_dynrec/dyn_fpu.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -68,7 +68,8 @@ static INLINE void dyn_fpu_top_swapped() { } static void dyn_eatree() { - Bitu group=(decode.modrm.val >> 3) & 7; +// Bitu group = (decode.modrm.val >> 3) & 7; + Bitu group = decode.modrm.reg&7; //It is already that, but compilers. switch (group){ case 0x00: // FADD ST,STi gen_call_function_R((void*)&FPU_FADD_EA,FC_OP1); @@ -102,7 +103,8 @@ static void dyn_eatree() { static void dyn_fpu_esc0(){ dyn_get_modrm(); - if (decode.modrm.val >= 0xc0) { +// if (decode.modrm.val >= 0xc0) { + if (decode.modrm.mod == 3) { dyn_fpu_top(); switch (decode.modrm.reg){ case 0x00: //FADD ST,STi @@ -144,7 +146,8 @@ static void dyn_fpu_esc0(){ static void dyn_fpu_esc1(){ dyn_get_modrm(); - if (decode.modrm.val >= 0xc0) { +// if (decode.modrm.val >= 0xc0) { + if (decode.modrm.mod == 3) { switch (decode.modrm.reg){ case 0x00: /* FLD STi */ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); @@ -331,7 +334,8 @@ static void dyn_fpu_esc1(){ static void dyn_fpu_esc2(){ dyn_get_modrm(); - if (decode.modrm.val >= 0xc0) { +// if (decode.modrm.val >= 0xc0) { + if (decode.modrm.mod == 3) { switch(decode.modrm.reg){ case 0x05: switch(decode.modrm.rm){ @@ -363,7 +367,8 @@ static void dyn_fpu_esc2(){ static void dyn_fpu_esc3(){ dyn_get_modrm(); - if (decode.modrm.val >= 0xc0) { +// if (decode.modrm.val >= 0xc0) { + if (decode.modrm.mod == 3) { switch (decode.modrm.reg) { case 0x04: switch (decode.modrm.rm) { @@ -427,7 +432,8 @@ static void dyn_fpu_esc3(){ static void dyn_fpu_esc4(){ dyn_get_modrm(); - if (decode.modrm.val >= 0xc0) { +// if (decode.modrm.val >= 0xc0) { + if (decode.modrm.mod == 3) { switch(decode.modrm.reg){ case 0x00: /* FADD STi,ST*/ dyn_fpu_top_swapped(); @@ -475,7 +481,8 @@ static void dyn_fpu_esc4(){ static void dyn_fpu_esc5(){ dyn_get_modrm(); - if (decode.modrm.val >= 0xc0) { +// if (decode.modrm.val >= 0xc0) { + if (decode.modrm.mod == 3) { dyn_fpu_top(); switch(decode.modrm.reg){ case 0x00: /* FFREE STi */ @@ -534,7 +541,7 @@ static void dyn_fpu_esc5(){ gen_mov_word_to_reg(FC_OP1,(void*)(&TOP),true); gen_call_function_R((void*)&FPU_SET_TOP,FC_OP1); dyn_fill_ea(FC_OP1); - gen_mov_word_to_reg(FC_OP2,(void*)(&fpu.sw),true); + gen_mov_word_to_reg(FC_OP2,(void*)(&fpu.sw),false); gen_call_function_RR((void*)&mem_writew,FC_OP1,FC_OP2); break; default: @@ -545,7 +552,8 @@ static void dyn_fpu_esc5(){ static void dyn_fpu_esc6(){ dyn_get_modrm(); - if (decode.modrm.val >= 0xc0) { +// if (decode.modrm.val >= 0xc0) { + if (decode.modrm.mod == 3) { switch(decode.modrm.reg){ case 0x00: /*FADDP STi,ST*/ dyn_fpu_top_swapped(); @@ -601,7 +609,8 @@ static void dyn_fpu_esc6(){ static void dyn_fpu_esc7(){ dyn_get_modrm(); - if (decode.modrm.val >= 0xc0) { +// if (decode.modrm.val >= 0xc0) { + if (decode.modrm.mod == 3) { switch (decode.modrm.reg){ case 0x00: /* FFREEP STi */ dyn_fpu_top(); diff --git a/src/cpu/core_dynrec/operators.h b/src/cpu/core_dynrec/operators.h index 52de968..5f62835 100644 --- a/src/cpu/core_dynrec/operators.h +++ b/src/cpu/core_dynrec/operators.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/cpu/core_dynrec/risc_armv4le-common.h b/src/cpu/core_dynrec/risc_armv4le-common.h index 0c2c53e..9b0cae6 100644 --- a/src/cpu/core_dynrec/risc_armv4le-common.h +++ b/src/cpu/core_dynrec/risc_armv4le-common.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/cpu/core_dynrec/risc_armv4le-o3.h b/src/cpu/core_dynrec/risc_armv4le-o3.h index 44cf859..88a1f06 100644 --- a/src/cpu/core_dynrec/risc_armv4le-o3.h +++ b/src/cpu/core_dynrec/risc_armv4le-o3.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,14 +11,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* ARMv4 (little endian) backend by M-HT (size-tweaked arm version) */ +/* ARMv4/ARMv7 (little endian) backend by M-HT (arm version) */ // temporary registers @@ -50,15 +50,14 @@ // temporary register for LEA #define TEMP_REG_DRC HOST_v2 -#ifdef DRC_USE_REGS_ADDR // used to hold the address of "cpu_regs" - preferably filled in function gen_run_code #define FC_REGS_ADDR HOST_v7 -#endif -#ifdef DRC_USE_SEGS_ADDR // used to hold the address of "Segs" - preferably filled in function gen_run_code #define FC_SEGS_ADDR HOST_v8 -#endif + +// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code +#define readdata_addr HOST_v5 // helper macro @@ -88,6 +87,12 @@ #define MOV_REG_ROR_REG(dst, src, rreg) (0xe1a00070 + ((dst) << 12) + (src) + ((rreg) << 8) ) // mvn dst, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 #define MVN_IMM(dst, imm, rimm) (0xe3e00000 + ((dst) << 12) + (imm) + ((rimm) << 7) ) +#if C_TARGETCPU == ARMV7LE +// movw dst, #imm @ 0 <= imm <= 65535 +#define MOVW(dst, imm) (0xe3000000 + ((dst) << 12) + (((imm) & 0xf000) << 4) + ((imm) & 0x0fff) ) +// movt dst, #imm @ 0 <= imm <= 65535 +#define MOVT(dst, imm) (0xe3400000 + ((dst) << 12) + (((imm) & 0xf000) << 4) + ((imm) & 0x0fff) ) +#endif // arithmetic // add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 @@ -103,7 +108,11 @@ // cmp src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 #define CMP_IMM(src, imm, rimm) (0xe3500000 + ((src) << 16) + (imm) + ((rimm) << 7) ) // nop +#if C_TARGETCPU == ARMV7LE +#define NOP (0xe320f000) +#else #define NOP MOV_REG_LSL_IMM(HOST_r0, HOST_r0, 0) +#endif // logical // tst src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 @@ -122,34 +131,70 @@ #define EOR_REG_LSL_IMM(dst, src1, src2, imm) (0xe0200000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) ) // bic dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 #define BIC_IMM(dst, src, imm, rimm) (0xe3c00000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) +// bic dst, src1, src2, lsl #imm @ 0 <= imm <= 31 +#define BIC_REG_LSL_IMM(dst, src1, src2, imm) (0xe1c00000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) ) // load // ldr reg, [addr, #imm] @ 0 <= imm < 4096 #define LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) ) +// ldr reg, [addr, #-(imm)] @ 0 <= imm < 4096 +#define LDR_IMM_M(reg, addr, imm) (0xe5100000 + ((reg) << 12) + ((addr) << 16) + (imm) ) // ldrh reg, [addr, #imm] @ 0 <= imm < 256 #define LDRH_IMM(reg, addr, imm) (0xe1d000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) ) +// ldrh reg, [addr, #-(imm)] @ 0 <= imm < 256 +#define LDRH_IMM_M(reg, addr, imm) (0xe15000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) ) // ldrb reg, [addr, #imm] @ 0 <= imm < 4096 #define LDRB_IMM(reg, addr, imm) (0xe5d00000 + ((reg) << 12) + ((addr) << 16) + (imm) ) +// ldrb reg, [addr, #-(imm)] @ 0 <= imm < 4096 +#define LDRB_IMM_M(reg, addr, imm) (0xe5500000 + ((reg) << 12) + ((addr) << 16) + (imm) ) +// ldr reg, [addr1, addr2, lsl #imm] @ 0 <= imm < 31 +#define LDR_REG_LSL_IMM(reg, addr1, addr2, imm) (0xe7900000 + ((reg) << 12) + ((addr1) << 16) + (addr2) + ((imm) << 7) ) // store // str reg, [addr, #imm] @ 0 <= imm < 4096 #define STR_IMM(reg, addr, imm) (0xe5800000 + ((reg) << 12) + ((addr) << 16) + (imm) ) +// str reg, [addr, #-(imm)] @ 0 <= imm < 4096 +#define STR_IMM_M(reg, addr, imm) (0xe5000000 + ((reg) << 12) + ((addr) << 16) + (imm) ) // strh reg, [addr, #imm] @ 0 <= imm < 256 #define STRH_IMM(reg, addr, imm) (0xe1c000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) ) +// strh reg, [addr, #-(imm)] @ 0 <= imm < 256 +#define STRH_IMM_M(reg, addr, imm) (0xe14000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) ) // strb reg, [addr, #imm] @ 0 <= imm < 4096 #define STRB_IMM(reg, addr, imm) (0xe5c00000 + ((reg) << 12) + ((addr) << 16) + (imm) ) +// strb reg, [addr, #-(imm)] @ 0 <= imm < 4096 +#define STRB_IMM_M(reg, addr, imm) (0xe5400000 + ((reg) << 12) + ((addr) << 16) + (imm) ) // branch // beq pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 #define BEQ_FWD(imm) (0x0a000000 + ((imm) >> 2) ) // bne pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 #define BNE_FWD(imm) (0x1a000000 + ((imm) >> 2) ) -// bgt pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 -#define BGT_FWD(imm) (0xca000000 + ((imm) >> 2) ) +// ble pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 +#define BLE_FWD(imm) (0xda000000 + ((imm) >> 2) ) // b pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 #define B_FWD(imm) (0xea000000 + ((imm) >> 2) ) // bx reg #define BX(reg) (0xe12fff10 + (reg) ) +#if C_TARGETCPU == ARMV7LE +// blx reg +#define BLX_REG(reg) (0xe12fff30 + (reg) ) + +// extend +// sxth dst, src, ror #rimm @ rimm = 0 | 8 | 16 | 24 +#define SXTH(dst, src, rimm) (0xe6bf0070 + ((dst) << 12) + (src) + (((rimm) & 24) << 7) ) +// sxtb dst, src, ror #rimm @ rimm = 0 | 8 | 16 | 24 +#define SXTB(dst, src, rimm) (0xe6af0070 + ((dst) << 12) + (src) + (((rimm) & 24) << 7) ) +// uxth dst, src, ror #rimm @ rimm = 0 | 8 | 16 | 24 +#define UXTH(dst, src, rimm) (0xe6ff0070 + ((dst) << 12) + (src) + (((rimm) & 24) << 7) ) +// uxtb dst, src, ror #rimm @ rimm = 0 | 8 | 16 | 24 +#define UXTB(dst, src, rimm) (0xe6ef0070 + ((dst) << 12) + (src) + (((rimm) & 24) << 7) ) + +// bit field +// bfi dst, src, #lsb, #width @ lsb >= 0, width >= 1, lsb+width <= 32 +#define BFI(dst, src, lsb, width) (0xe7c00010 + ((dst) << 12) + (src) + ((lsb) << 7) + (((lsb) + (width) - 1) << 16) ) +// bfc dst, #lsb, #width @ lsb >= 0, width >= 1, lsb+width <= 32 +#define BFC(dst, lsb, width) (0xe7c0001f + ((dst) << 12) + ((lsb) << 7) + (((lsb) + (width) - 1) << 16) ) +#endif // move a full register from reg_src to reg_dst @@ -158,6 +203,28 @@ static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { cache_addd( MOV_REG_LSL_IMM(reg_dst, reg_src, 0) ); // mov reg_dst, reg_src } +// helper function +static bool val_is_operand2(Bit32u value, Bit32u *val_shift) { + Bit32u shift; + + if (GCC_UNLIKELY(value == 0)) { + *val_shift = 0; + return true; + } + + shift = 0; + while ((value & 3) == 0) { + value>>=2; + shift+=2; + } + + if ((value >> 8) != 0) return false; + + *val_shift = shift; + return true; +} + +#if C_TARGETCPU != ARMV7LE // helper function static Bits get_imm_gen_len(Bit32u imm) { Bits ret; @@ -177,79 +244,44 @@ static Bits get_imm_gen_len(Bit32u imm) { } // helper function -static Bits get_method_imm_gen_len(Bit32u imm, Bits preffer00, Bits *num) { - Bits num00, num15, numadd, numsub, numret, ret; - num00 = get_imm_gen_len(imm); - num15 = get_imm_gen_len(~imm); - numadd = get_imm_gen_len(imm - ((Bit32u)cache.pos+8)); - numsub = get_imm_gen_len(((Bit32u)cache.pos+8) - imm); - if (numsub < numadd && numsub < num00 && numsub < num15) { - ret = 0; - numret = numsub; - } else if (numadd < num00 && numadd < num15) { - ret = 1; - numret = numadd; - } else if (num00 < num15 || (num00 == num15 && preffer00)) { - ret = 2; - numret = num00; - } else { - ret = 3; - numret = num15; - } - if (num != NULL) *num = numret; - return ret; +static Bits get_min_imm_gen_len(Bit32u imm) { + Bits num1, num2; + + num1 = get_imm_gen_len(imm); + num2 = get_imm_gen_len(~imm); + + return (num1 <= num2)?num1:num2; } +#endif // move a 32bit constant value into dest_reg static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { - Bits first, method, scale; - Bit32u imm2, dist; - if (imm == 0) { - cache_addd( MOV_IMM(dest_reg, 0, 0) ); // mov dest_reg, #0 - } else if (imm == 0xffffffff) { - cache_addd( MVN_IMM(dest_reg, 0, 0) ); // mvn dest_reg, #0 - } else { - method = get_method_imm_gen_len(imm, 1, NULL); +#if C_TARGETCPU == ARMV7LE + Bit32u scale; - scale = 0; - first = 1; - if (method == 0) { - dist = ((Bit32u)cache.pos+8) - imm; - while (dist) { - while ((dist & 3) == 0) { - dist>>=2; - scale+=2; - } - if (first) { - cache_addd( SUB_IMM(dest_reg, HOST_pc, dist & 0xff, ROTATE_SCALE(scale)) ); // sub dest_reg, pc, #((dist & 0xff) << scale) - first = 0; - } else { - cache_addd( SUB_IMM(dest_reg, dest_reg, dist & 0xff, ROTATE_SCALE(scale)) ); // sub dest_reg, dest_reg, #((dist & 0xff) << scale) - } - dist>>=8; - scale+=8; - } - } else if (method == 1) { - dist = imm - ((Bit32u)cache.pos+8); - if (dist == 0) { - cache_addd( MOV_REG_LSL_IMM(dest_reg, HOST_pc, 0) ); // mov dest_reg, pc - } else { - while (dist) { - while ((dist & 3) == 0) { - dist>>=2; - scale+=2; - } - if (first) { - cache_addd( ADD_IMM(dest_reg, HOST_pc, dist & 0xff, ROTATE_SCALE(scale)) ); // add dest_reg, pc, #((dist & 0xff) << scale) - first = 0; - } else { - cache_addd( ADD_IMM(dest_reg, dest_reg, dist & 0xff, ROTATE_SCALE(scale)) ); // add dest_reg, dest_reg, #((dist & 0xff) << scale) - } - dist>>=8; - scale+=8; - } - } - } else if (method == 2) { + if ( val_is_operand2(imm, &scale) ) { + cache_addd( MOV_IMM(dest_reg, imm >> scale, ROTATE_SCALE(scale)) ); // mov dest_reg, #imm + } else if ( val_is_operand2(~imm, &scale) ) { + cache_addd( MVN_IMM(dest_reg, (~imm) >> scale, ROTATE_SCALE(scale)) ); // mvn dest_reg, #~imm + } else { + cache_addd( MOVW(dest_reg, imm & 0xffff) ); // movw dest_reg, #(imm & 0xffff) + + if (imm >= 0x10000) + { + cache_addd( MOVT(dest_reg, imm >> 16) ); // movt dest_reg, #(imm >> 16) + } + } +#else + Bit32u imm2, first, scale; + + scale = 0; + first = 1; + imm2 = ~imm; + + if (get_imm_gen_len(imm) <= get_imm_gen_len(imm2)) { + if (imm == 0) { + cache_addd( MOV_IMM(dest_reg, 0, 0) ); // mov dest_reg, #0 + } else { while (imm) { while ((imm & 3) == 0) { imm>>=2; @@ -264,8 +296,11 @@ static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { imm>>=8; scale+=8; } + } + } else { + if (imm2 == 0) { + cache_addd( MVN_IMM(dest_reg, 0, 0) ); // mvn dest_reg, #0 } else { - imm2 = ~imm; while (imm2) { while ((imm2 & 3) == 0) { imm2>>=2; @@ -282,12 +317,67 @@ static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { } } } +#endif +} + +// helper function +static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) { + switch (size) { + case 4: +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) + if ((data & 3) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 4096)) { + cache_addd( LDR_IMM(dest_reg, addr_reg, data - addr_data) ); // ldr dest_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data) && (data > addr_data - 4096)) { + cache_addd( LDR_IMM_M(dest_reg, addr_reg, addr_data - data) ); // ldr dest_reg, [addr_reg, #-(addr_data - data)] + return true; + } + } + break; + case 2: +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) + if ((data & 1) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 256)) { + cache_addd( LDRH_IMM(dest_reg, addr_reg, data - addr_data) ); // ldrh dest_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data) && (data > addr_data - 256)) { + cache_addd( LDRH_IMM_M(dest_reg, addr_reg, addr_data - data) ); // ldrh dest_reg, [addr_reg, #-(addr_data - data)] + return true; + } + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 4096)) { + cache_addd( LDRB_IMM(dest_reg, addr_reg, data - addr_data) ); // ldrb dest_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data) && (data > addr_data - 4096)) { + cache_addd( LDRB_IMM_M(dest_reg, addr_reg, addr_data - data) ); // ldrb dest_reg, [addr_reg, #-(addr_data - data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) { + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true; + return false; } // helper function for gen_mov_word_to_reg static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) { // alignment.... if (dword) { +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) if ((Bit32u)data & 3) { if ( ((Bit32u)data & 3) == 2 ) { cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] @@ -300,15 +390,20 @@ static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,Ho cache_addd( LDRB_IMM(temp2, data_reg, 3) ); // ldrb temp2, [data_reg, #3] cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 24) ); // orr dest_reg, dest_reg, temp2, lsl #24 } - } else { + } else +#endif + { cache_addd( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg] } } else { +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) if ((Bit32u)data & 1) { cache_addd( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg] cache_addd( LDRB_IMM(temp2, data_reg, 1) ); // ldrb temp2, [data_reg, #1] cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 8) ); // orr dest_reg, dest_reg, temp2, lsl #8 - } else { + } else +#endif + { cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] } } @@ -317,42 +412,76 @@ static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,Ho // move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg // 16bit moves may destroy the upper 16bit of the destination register static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { - gen_mov_dword_to_reg_imm(temp1, (Bit32u)data); - gen_mov_word_to_reg_helper(dest_reg, data, dword, temp1); + if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(temp1, (Bit32u)data); + gen_mov_word_to_reg_helper(dest_reg, data, dword, temp1); + } } // move a 16bit constant value into dest_reg // the upper 16bit of the destination register may be destroyed -static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { - Bits first, scale; - Bit32u imm2; - if (imm == 0) { - cache_addd( MOV_IMM(dest_reg, 0, 0) ); // mov dest_reg, #0 - } else { - scale = 0; - first = 1; - imm2 = (Bit32u)imm; - while (imm2) { - while ((imm2 & 3) == 0) { - imm2>>=2; - scale+=2; +static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { + gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm); +} + +// helper function +static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) { + switch (size) { + case 4: +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) + if ((data & 3) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 4096)) { + cache_addd( STR_IMM(src_reg, addr_reg, data - addr_data) ); // str src_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data) && (data > addr_data - 4096)) { + cache_addd( STR_IMM_M(src_reg, addr_reg, addr_data - data) ); // str src_reg, [addr_reg, #-(addr_data - data)] + return true; + } } - if (first) { - cache_addd( MOV_IMM(dest_reg, imm2 & 0xff, ROTATE_SCALE(scale)) ); // mov dest_reg, #((imm2 & 0xff) << scale) - first = 0; - } else { - cache_addd( ORR_IMM(dest_reg, dest_reg, imm2 & 0xff, ROTATE_SCALE(scale)) ); // orr dest_reg, dest_reg, #((imm2 & 0xff) << scale) + break; + case 2: +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) + if ((data & 1) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 256)) { + cache_addd( STRH_IMM(src_reg, addr_reg, data - addr_data) ); // strh src_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data) && (data > addr_data - 256)) { + cache_addd( STRH_IMM_M(src_reg, addr_reg, addr_data - data) ); // strh src_reg, [addr_reg, #-(addr_data - data)] + return true; + } } - imm2>>=8; - scale+=8; - } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 4096)) { + cache_addd( STRB_IMM(src_reg, addr_reg, data - addr_data) ); // strb src_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data) && (data > addr_data - 4096)) { + cache_addd( STRB_IMM_M(src_reg, addr_reg, addr_data - data) ); // strb src_reg, [addr_reg, #-(addr_data - data)] + return true; + } + default: + break; } + return false; +} + +// helper function +static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) { + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true; + return false; } // helper function for gen_mov_word_from_reg static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) { // alignment.... if (dword) { +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) if ((Bit32u)dest & 3) { if ( ((Bit32u)dest & 3) == 2 ) { cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] @@ -365,15 +494,20 @@ static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, cache_addd( MOV_REG_LSR_IMM(temp2, temp2, 16) ); // mov temp2, temp2, lsr #16 cache_addd( STRB_IMM(temp2, data_reg, 3) ); // strb temp2, [data_reg, #3] } - } else { + } else +#endif + { cache_addd( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg] } } else { +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) if ((Bit32u)dest & 1) { cache_addd( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg] cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 8) ); // mov temp2, src_reg, lsr #8 cache_addd( STRB_IMM(temp2, data_reg, 1) ); // strb temp2, [data_reg, #1] - } else { + } else +#endif + { cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] } } @@ -381,8 +515,10 @@ static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, // move 32bit (dword==true) or 16bit (dword==false) of a register into memory static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_from_reg_helper(src_reg, dest, dword, temp1); + if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); + gen_mov_word_from_reg_helper(src_reg, dest, dword, temp1); + } } // move an 8bit value from memory into dest_reg @@ -390,8 +526,10 @@ static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { // this function does not use FC_OP1/FC_OP2 as dest_reg as these // registers might not be directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { - gen_mov_dword_to_reg_imm(temp1, (Bit32u)data); - cache_addd( LDRB_IMM(dest_reg, temp1, 0) ); // ldrb dest_reg, [temp1] + if (!gen_mov_memval_to_reg(dest_reg, data, 1)) { + gen_mov_dword_to_reg_imm(temp1, (Bit32u)data); + cache_addd( LDRB_IMM(dest_reg, temp1, 0) ); // ldrb dest_reg, [temp1] + } } // move an 8bit value from memory into dest_reg @@ -420,8 +558,10 @@ static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u // move the lowest 8bit of a register into memory static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - cache_addd( STRB_IMM(src_reg, temp1, 0) ); // strb src_reg, [temp1] + if (!gen_mov_memval_from_reg(src_reg, dest, 1)) { + gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); + cache_addd( STRB_IMM(src_reg, temp1, 0) ); // strb src_reg, [temp1] + } } @@ -430,10 +570,18 @@ static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { // the register is zero-extended (sign==false) or sign-extended (sign==true) static void gen_extend_byte(bool sign,HostReg reg) { if (sign) { +#if C_TARGETCPU == ARMV7LE + cache_addd( SXTB(reg, reg, 0) ); // sxtb reg, reg +#else cache_addd( MOV_REG_LSL_IMM(reg, reg, 24) ); // mov reg, reg, lsl #24 cache_addd( MOV_REG_ASR_IMM(reg, reg, 24) ); // mov reg, reg, asr #24 +#endif } else { +#if C_TARGETCPU == ARMV7LE + cache_addd( UXTB(reg, reg, 0) ); // uxtb reg, reg +#else cache_addd( AND_IMM(reg, reg, 0xff, 0) ); // and reg, reg, #0xff +#endif } } @@ -441,11 +589,19 @@ static void gen_extend_byte(bool sign,HostReg reg) { // the register is zero-extended (sign==false) or sign-extended (sign==true) static void gen_extend_word(bool sign,HostReg reg) { if (sign) { +#if C_TARGETCPU == ARMV7LE + cache_addd( SXTH(reg, reg, 0) ); // sxth reg, reg +#else cache_addd( MOV_REG_LSL_IMM(reg, reg, 16) ); // mov reg, reg, lsl #16 cache_addd( MOV_REG_ASR_IMM(reg, reg, 16) ); // mov reg, reg, asr #16 +#endif } else { +#if C_TARGETCPU == ARMV7LE + cache_addd( UXTH(reg, reg, 0) ); // uxth reg, reg +#else cache_addd( MOV_REG_LSL_IMM(reg, reg, 16) ); // mov reg, reg, lsl #16 cache_addd( MOV_REG_LSR_IMM(reg, reg, 16) ); // mov reg, reg, lsr #16 +#endif } } @@ -457,72 +613,57 @@ static void gen_add(HostReg reg,void* op) { // add a 32bit constant value to a full register static void gen_add_imm(HostReg reg,Bit32u imm) { - Bits method1, method2, num1, num2, scale, sub; - if(!imm) return; - if (imm == 1) { - cache_addd( ADD_IMM(reg, reg, 1, 0) ); // add reg, reg, #1 - } else if (imm == 0xffffffff) { - cache_addd( SUB_IMM(reg, reg, 1, 0) ); // sub reg, reg, #1 - } else { - method1 = get_method_imm_gen_len(imm, 1, &num1); - method2 = get_method_imm_gen_len(-((Bit32s)imm), 1, &num2); - if (num2 < num1) { - method1 = method2; - imm = (Bit32u)(-((Bit32s)imm)); - sub = 1; - } else sub = 0; + Bit32u imm2, scale; - if (method1 != 2) { - gen_mov_dword_to_reg_imm(temp3, imm); - if (sub) { - cache_addd( SUB_REG_LSL_IMM(reg, reg, temp3, 0) ); // sub reg, reg, temp3 - } else { - cache_addd( ADD_REG_LSL_IMM(reg, reg, temp3, 0) ); // add reg, reg, temp3 - } + if(!imm) return; + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if ( val_is_operand2(imm, &scale) ) { + cache_addd( ADD_IMM(reg, reg, imm >> scale, ROTATE_SCALE(scale)) ); // add reg, reg, #imm + } else if ( val_is_operand2(imm2, &scale) ) { + cache_addd( SUB_IMM(reg, reg, imm2 >> scale, ROTATE_SCALE(scale)) ); // sub reg, reg, #(-imm) +#if C_TARGETCPU == ARMV7LE + } else if (imm2 < 0x10000) { + cache_addd( MOVW(temp2, imm2) ); // movw temp2, #(-imm) + cache_addd( SUB_REG_LSL_IMM(reg, reg, temp2, 0) ); // sub reg, reg, temp2 +#endif + } else { +#if C_TARGETCPU != ARMV7LE + if (get_min_imm_gen_len(imm) <= get_min_imm_gen_len(imm2)) { +#endif + gen_mov_dword_to_reg_imm(temp2, imm); + cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2 +#if C_TARGETCPU != ARMV7LE } else { - scale = 0; - while (imm) { - while ((imm & 3) == 0) { - imm>>=2; - scale+=2; - } - if (sub) { - cache_addd( SUB_IMM(reg, reg, imm & 0xff, ROTATE_SCALE(scale)) ); // sub reg, reg, #((imm & 0xff) << scale) - } else { - cache_addd( ADD_IMM(reg, reg, imm & 0xff, ROTATE_SCALE(scale)) ); // add reg, reg, #((imm & 0xff) << scale) - } - imm>>=8; - scale+=8; - } + gen_mov_dword_to_reg_imm(temp2, imm2); + cache_addd( SUB_REG_LSL_IMM(reg, reg, temp2, 0) ); // sub reg, reg, temp2 } +#endif } } // and a 32bit constant value with a full register static void gen_and_imm(HostReg reg,Bit32u imm) { - Bits method, scale; - Bit32u imm2; + Bit32u imm2, scale; + imm2 = ~imm; if(!imm2) return; + if (!imm) { cache_addd( MOV_IMM(reg, 0, 0) ); // mov reg, #0 + } else if ( val_is_operand2(imm, &scale) ) { + cache_addd( AND_IMM(reg, reg, imm >> scale, ROTATE_SCALE(scale)) ); // and reg, reg, #imm + } else if ( val_is_operand2(imm2, &scale) ) { + cache_addd( BIC_IMM(reg, reg, imm2 >> scale, ROTATE_SCALE(scale)) ); // bic reg, reg, #(~imm) +#if C_TARGETCPU == ARMV7LE + } else if (imm2 < 0x10000) { + cache_addd( MOVW(temp2, imm2) ); // movw temp2, #(~imm) + cache_addd( BIC_REG_LSL_IMM(reg, reg, temp2, 0) ); // bic reg, reg, temp2 +#endif } else { - method = get_method_imm_gen_len(imm, 0, NULL); - if (method != 3) { - gen_mov_dword_to_reg_imm(temp3, imm); - cache_addd( AND_REG_LSL_IMM(reg, reg, temp3, 0) ); // and reg, reg, temp3 - } else { - scale = 0; - while (imm2) { - while ((imm2 & 3) == 0) { - imm2>>=2; - scale+=2; - } - cache_addd( BIC_IMM(reg, reg, imm2 & 0xff, ROTATE_SCALE(scale)) ); // bic reg, reg, #((imm2 & 0xff) << scale) - imm2>>=8; - scale+=8; - } - } + gen_mov_dword_to_reg_imm(temp2, imm); + cache_addd( AND_REG_LSL_IMM(reg, reg, temp2, 0) ); // and reg, reg, temp2 } } @@ -538,68 +679,71 @@ static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { gen_mov_direct_dword(dest,(Bit32u)imm); } -// add an 8bit constant value to a dword memory value -static void gen_add_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_to_reg_helper(temp3, dest, 1, temp1); - if (imm >= 0) { - cache_addd( ADD_IMM(temp3, temp3, (Bit32s)imm, 0) ); // add temp3, temp3, #(imm) - } else { - cache_addd( SUB_IMM(temp3, temp3, -((Bit32s)imm), 0) ); // sub temp3, temp3, #(-imm) - } - gen_mov_word_from_reg_helper(temp3, dest, 1, temp1); -} - // add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if (!dword) imm &= 0xffff; if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_add_direct_byte(dest,(Bit8s)imm); - return; + + if (!gen_mov_memval_to_reg(temp3, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); + gen_mov_word_to_reg_helper(temp3, dest, dword, temp1); } - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_to_reg_helper(temp3, dest, dword, temp1); - // maybe use function gen_add_imm - if (dword) { - gen_mov_dword_to_reg_imm(temp2, imm); - } else { - gen_mov_word_to_reg_imm(temp2, (Bit16u)imm); + gen_add_imm(temp3, imm); + if (!gen_mov_memval_from_reg(temp3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(temp3, dest, dword, temp1); } - cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2 - gen_mov_word_from_reg_helper(temp3, dest, dword, temp1); } -// subtract an 8bit constant value from a dword memory value -static void gen_sub_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_to_reg_helper(temp3, dest, 1, temp1); - if (imm >= 0) { - cache_addd( SUB_IMM(temp3, temp3, (Bit32s)imm, 0) ); // sub temp3, temp3, #(imm) - } else { - cache_addd( ADD_IMM(temp3, temp3, -((Bit32s)imm), 0) ); // add temp3, temp3, #(-imm) - } - gen_mov_word_from_reg_helper(temp3, dest, 1, temp1); +// add an 8bit constant value to a dword memory value +static void gen_add_direct_byte(void* dest,Bit8s imm) { + gen_add_direct_word(dest, (Bit32s)imm, 1); } // subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + Bit32u imm2, scale; + + if (!dword) imm &= 0xffff; if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_sub_direct_byte(dest,(Bit8s)imm); - return; + + if (!gen_mov_memval_to_reg(temp3, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); + gen_mov_word_to_reg_helper(temp3, dest, dword, temp1); } - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_to_reg_helper(temp3, dest, dword, temp1); - // maybe use function gen_add_imm/gen_sub_imm - if (dword) { - gen_mov_dword_to_reg_imm(temp2, imm); + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if ( val_is_operand2(imm, &scale) ) { + cache_addd( SUB_IMM(temp3, temp3, imm >> scale, ROTATE_SCALE(scale)) ); // sub temp3, temp3, #imm + } else if ( val_is_operand2(imm2, &scale) ) { + cache_addd( ADD_IMM(temp3, temp3, imm2 >> scale, ROTATE_SCALE(scale)) ); // add temp3, temp3, #(-imm) +#if C_TARGETCPU == ARMV7LE + } else if (imm2 < 0x10000) { + cache_addd( MOVW(temp2, imm2) ); // movw temp2, #(-imm) + cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2 +#endif } else { - gen_mov_word_to_reg_imm(temp2, (Bit16u)imm); +#if C_TARGETCPU != ARMV7LE + if (get_min_imm_gen_len(imm) <= get_min_imm_gen_len(imm2)) { +#endif + gen_mov_dword_to_reg_imm(temp2, imm); + cache_addd( SUB_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // sub temp3, temp3, temp2 +#if C_TARGETCPU != ARMV7LE + } else { + gen_mov_dword_to_reg_imm(temp2, imm2); + cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2 + } +#endif } - cache_addd( SUB_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // sub temp3, temp3, temp2 - gen_mov_word_from_reg_helper(temp3, dest, dword, temp1); + + if (!gen_mov_memval_from_reg(temp3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(temp3, dest, dword, temp1); + } +} + +// subtract an 8bit constant value from a dword memory value +static void gen_sub_direct_byte(void* dest,Bit8s imm) { + gen_sub_direct_word(dest, (Bit32s)imm, 1); } // effective address calculation, destination is dest_reg @@ -622,10 +766,16 @@ static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) { // generate a call to a parameterless function static void INLINE gen_call_function_raw(void * func) { +#if C_TARGETCPU == ARMV7LE + cache_addd( MOVW(temp1, ((Bit32u)func) & 0xffff) ); // movw temp1, #(func & 0xffff) + cache_addd( MOVT(temp1, ((Bit32u)func) >> 16) ); // movt temp1, #(func >> 16) + cache_addd( BLX_REG(temp1) ); // blx temp1 +#else cache_addd( LDR_IMM(temp1, HOST_pc, 4) ); // ldr temp1, [pc, #4] cache_addd( ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4 cache_addd( BX(temp1) ); // bx temp1 cache_addd((Bit32u)func); // .int func +#endif } // generate a call to a function with paramcount parameters @@ -665,43 +815,24 @@ static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { // jump to an address pointed at by ptr, offset is in imm static void gen_jmp_ptr(void * ptr,Bits imm=0) { - Bits num1, num2, scale, sub; - Bitu imm2; + Bit32u scale; + gen_mov_word_to_reg(temp3, ptr, 1); - if (imm) { - num1 = get_imm_gen_len(imm); - num2 = get_imm_gen_len(-imm); - - if (num2 < num1) { - imm = -imm; - sub = 1; - } else sub = 0; - - scale = 0; - imm2 = (Bitu)imm; - while (imm2) { - while ((imm2 & 3) == 0) { - imm2>>=2; - scale+=2; - } - if (sub) { - cache_addd( SUB_IMM(temp3, temp3, imm2 & 0xff, ROTATE_SCALE(scale)) ); // sub temp3, temp3, #((imm2 & 0xff) << scale) - } else { - cache_addd( ADD_IMM(temp3, temp3, imm2 & 0xff, ROTATE_SCALE(scale)) ); // add temp3, temp3, #((imm2 & 0xff) << scale) - } - imm2>>=8; - scale+=8; - } - } - -#if (1) -// (*ptr) should be word aligned +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) +// (*ptr) should be word aligned if ((imm & 0x03) == 0) { - cache_addd( LDR_IMM(temp1, temp3, 0) ); // ldr temp1, [temp3] - } else #endif - { + if ((imm >= 0) && (imm < 4096)) { + cache_addd( LDR_IMM(temp1, temp3, imm) ); // ldr temp1, [temp3, #imm] + } else { + gen_mov_dword_to_reg_imm(temp2, imm); + cache_addd( LDR_REG_LSL_IMM(temp1, temp3, temp2, 0) ); // ldr temp1, [temp3, temp2] + } +#if !(defined(C_UNALIGNED_MEMORY) || (C_TARGETCPU == ARMV7LE)) + } else { + gen_add_imm(temp3, imm); + cache_addd( LDRB_IMM(temp1, temp3, 0) ); // ldrb temp1, [temp3] cache_addd( LDRB_IMM(temp2, temp3, 1) ); // ldrb temp2, [temp3, #1] cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 8) ); // orr temp1, temp1, temp2, lsl #8 @@ -710,6 +841,7 @@ static void gen_jmp_ptr(void * ptr,Bits imm=0) { cache_addd( LDRB_IMM(temp2, temp3, 3) ); // ldrb temp2, [temp3, #3] cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 24) ); // orr temp1, temp1, temp2, lsl #24 } +#endif cache_addd( BX(temp1) ); // bx temp1 } @@ -757,67 +889,74 @@ static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) { } else { cache_addd( TST_IMM(reg, 0xff, 0) ); // tst reg, #0xff } - cache_addd( BEQ_FWD(8) ); // beq nobranch (pc +8) - cache_addd( LDR_IMM(temp1, HOST_pc, 0) ); // ldr temp1, [pc, #0] - cache_addd( BX(temp1) ); // bx temp1 - cache_addd(0); // fill j - // nobranch: + cache_addd( BNE_FWD(0) ); // bne j return ((Bit32u)cache.pos-4); } // compare 32bit-register against zero and jump if value less/equal than zero static Bit32u gen_create_branch_long_leqzero(HostReg reg) { cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0 - cache_addd( BGT_FWD(8) ); // bgt nobranch (pc+8) - cache_addd( LDR_IMM(temp1, HOST_pc, 0) ); // ldr temp1, [pc, #0] - cache_addd( BX(temp1) ); // bx temp1 - cache_addd(0); // fill j - // nobranch: + cache_addd( BLE_FWD(0) ); // ble j return ((Bit32u)cache.pos-4); } // calculate long relative offset and fill it into the location pointed to by data static void INLINE gen_fill_branch_long(Bit32u data) { - // this is an absolute branch - *(Bit32u*)data=(Bit32u)cache.pos; + *(Bit32u*)data=( (*(Bit32u*)data) & 0xff000000 ) | ( ( ((Bit32u)cache.pos - (data+8)) >> 2 ) & 0x00ffffff ); } static void gen_run_code(void) { - cache_addd(0xe92d4000); // stmfd sp!, {lr} - cache_addd(0xe92d0cf0); // stmfd sp!, {v1-v4,v7,v8} +#if C_TARGETCPU == ARMV7LE + cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr} - // adr: 8 - cache_addd( LDR_IMM(FC_SEGS_ADDR, HOST_pc, 64 - (8 + 8)) ); // ldr FC_SEGS_ADDR, [pc, #(&Segs)] - // adr: 12 - cache_addd( LDR_IMM(FC_REGS_ADDR, HOST_pc, 68 - (12 + 8)) ); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)] + cache_addd( MOVW(FC_SEGS_ADDR, ((Bit32u)&Segs) & 0xffff) ); // movw FC_SEGS_ADDR, #(&Segs & 0xffff) + cache_addd( MOVT(FC_SEGS_ADDR, ((Bit32u)&Segs) >> 16) ); // movt FC_SEGS_ADDR, #(&Segs >> 16) - cache_addd( ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4 - cache_addd(0xe92d4000); // stmfd sp!, {lr} - cache_addd( BX(HOST_r0) ); // bx r0 + cache_addd( MOVW(FC_REGS_ADDR, ((Bit32u)&cpu_regs) & 0xffff) ); // movw FC_REGS_ADDR, #(&cpu_regs & 0xffff) + cache_addd( MOVT(FC_REGS_ADDR, ((Bit32u)&cpu_regs) >> 16) ); // movt FC_REGS_ADDR, #(&cpu_regs >> 16) - cache_addd(0xe8bd0cf0); // ldmfd sp!, {v1-v4,v7,v8} + cache_addd( MOVW(readdata_addr, ((Bitu)&core_dynrec.readdata) & 0xffff) ); // movw readdata_addr, #(&core_dynrec.readdata & 0xffff) + cache_addd( MOVT(readdata_addr, ((Bitu)&core_dynrec.readdata) >> 16) ); // movt readdata_addr, #(&core_dynrec.readdata >> 16) - cache_addd(0xe8bd4000); // ldmfd sp!, {lr} - cache_addd( BX(HOST_lr) ); // bx lr + cache_addd( BX(HOST_r0) ); // bx r0 +#else + Bit8u *pos1, *pos2, *pos3; - // fill up to 64 bytes - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop + cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr} - // adr: 64 + pos1 = cache.pos; + cache_addd( 0 ); + pos2 = cache.pos; + cache_addd( 0 ); + pos3 = cache.pos; + cache_addd( 0 ); + + cache_addd( BX(HOST_r0) ); // bx r0 + + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } + + *(Bit32u*)pos1 = LDR_IMM(FC_SEGS_ADDR, HOST_pc, cache.pos - (pos1 + 8)); // ldr FC_SEGS_ADDR, [pc, #(&Segs)] cache_addd((Bit32u)&Segs); // address of "Segs" - // adr: 68 + + *(Bit32u*)pos2 = LDR_IMM(FC_REGS_ADDR, HOST_pc, cache.pos - (pos2 + 8)); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)] cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs" + + *(Bit32u*)pos3 = LDR_IMM(readdata_addr, HOST_pc, cache.pos - (pos3 + 8)); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)] + cache_addd((Bit32u)&core_dynrec.readdata); // address of "core_dynrec.readdata" + + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } +#endif } // return from a function static void gen_return_function(void) { - cache_addd(0xe8bd4000); // ldmfd sp!, {lr} - cache_addd( BX(HOST_lr) ); // bx lr + cache_addd(0xe8bd8df0); // ldmfd sp!, {v1-v5,v7,v8,pc} } #ifdef DRC_FLAGS_INVALIDATION @@ -831,42 +970,52 @@ static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) { case t_ADDb: case t_ADDw: case t_ADDd: - *(Bit32u*)pos=ADD_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // add FC_RETOP, a1, a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=ADD_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // add FC_RETOP, a1, a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_ORb: case t_ORw: case t_ORd: - *(Bit32u*)pos=ORR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // orr FC_RETOP, a1, a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=ORR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // orr FC_RETOP, a1, a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_ANDb: case t_ANDw: case t_ANDd: - *(Bit32u*)pos=AND_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // and FC_RETOP, a1, a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=AND_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // and FC_RETOP, a1, a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_SUBb: case t_SUBw: case t_SUBd: - *(Bit32u*)pos=SUB_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // sub FC_RETOP, a1, a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // sub FC_RETOP, a1, a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_XORb: case t_XORw: case t_XORd: - *(Bit32u*)pos=EOR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // eor FC_RETOP, a1, a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=EOR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // eor FC_RETOP, a1, a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_CMPb: case t_CMPw: @@ -874,114 +1023,185 @@ static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) { case t_TESTb: case t_TESTw: case t_TESTd: - *(Bit32u*)pos=B_FWD(8); // b (pc+2*4) + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=NOP; // nop +#if C_TARGETCPU != ARMV7LE + *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_INCb: case t_INCw: case t_INCd: - *(Bit32u*)pos=ADD_IMM(FC_RETOP, HOST_a1, 1, 0); // add FC_RETOP, a1, #1 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=ADD_IMM(FC_RETOP, HOST_a1, 1, 0); // add FC_RETOP, a1, #1 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_DECb: case t_DECw: case t_DECd: - *(Bit32u*)pos=SUB_IMM(FC_RETOP, HOST_a1, 1, 0); // sub FC_RETOP, a1, #1 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=SUB_IMM(FC_RETOP, HOST_a1, 1, 0); // sub FC_RETOP, a1, #1 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_SHLb: case t_SHLw: case t_SHLd: - *(Bit32u*)pos=MOV_REG_LSL_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsl a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=MOV_REG_LSL_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsl a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_SHRb: - *(Bit32u*)pos=AND_IMM(FC_RETOP, HOST_a1, 0xff, 0); // and FC_RETOP, a1, #0xff - *(Bit32u*)(pos+4)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2 - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)pos=NOP; // nop +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)(pos+4)=BFC(HOST_a1, 8, 24); // bfc a1, 8, 24 + *(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2 +#else + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=AND_IMM(FC_RETOP, HOST_a1, 0xff, 0); // and FC_RETOP, a1, #0xff + *(Bit32u*)(pos+12)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2 +#endif break; case t_SHRw: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 - *(Bit32u*)(pos+4)=MOV_REG_LSR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, lsr #16 - *(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2 - *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)pos=NOP; // nop +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)(pos+4)=BFC(HOST_a1, 16, 16); // bfc a1, 16, 16 + *(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2 +#else + *(Bit32u*)(pos+4)=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 + *(Bit32u*)(pos+8)=MOV_REG_LSR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, lsr #16 + *(Bit32u*)(pos+12)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2 +#endif break; case t_SHRd: - *(Bit32u*)pos=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_SARb: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24 - *(Bit32u*)(pos+4)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 24); // mov FC_RETOP, FC_RETOP, asr #24 + *(Bit32u*)pos=NOP; // nop +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)(pos+4)=SXTB(FC_RETOP, HOST_a1, 0); // sxtb FC_RETOP, a1 *(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2 - *(Bit32u*)(pos+12)=NOP; // nop +#else + *(Bit32u*)(pos+4)=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24 + *(Bit32u*)(pos+8)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 24); // mov FC_RETOP, FC_RETOP, asr #24 + *(Bit32u*)(pos+12)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2 +#endif break; case t_SARw: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 - *(Bit32u*)(pos+4)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, asr #16 + *(Bit32u*)pos=NOP; // nop +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)(pos+4)=SXTH(FC_RETOP, HOST_a1, 0); // sxth FC_RETOP, a1 *(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2 - *(Bit32u*)(pos+12)=NOP; // nop +#else + *(Bit32u*)(pos+4)=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 + *(Bit32u*)(pos+8)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, asr #16 + *(Bit32u*)(pos+12)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2 +#endif break; case t_SARd: - *(Bit32u*)pos=MOV_REG_ASR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, asr a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, asr a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_RORb: +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)pos=BFI(HOST_a1, HOST_a1, 8, 8); // bfi a1, a1, 8, 8 + *(Bit32u*)(pos+4)=BFI(HOST_a1, HOST_a1, 16, 16); // bfi a1, a1, 16, 16 + *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 +#else *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24 *(Bit32u*)(pos+4)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 8); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #8 *(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16 *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2 +#endif break; case t_RORw: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 - *(Bit32u*)(pos+4)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16 - *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2 - *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)pos=NOP; // nop +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)(pos+4)=BFI(HOST_a1, HOST_a1, 16, 16); // bfi a1, a1, 16, 16 + *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 +#else + *(Bit32u*)(pos+4)=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 + *(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16 + *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2 +#endif break; case t_RORd: - *(Bit32u*)pos=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; case t_ROLw: +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)pos=BFI(HOST_a1, HOST_a1, 16, 16); // bfi a1, a1, 16, 16 + *(Bit32u*)(pos+4)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32 + *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 +#else *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 *(Bit32u*)(pos+4)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32 *(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16 *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2 +#endif break; case t_ROLd: - *(Bit32u*)pos=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32 - *(Bit32u*)(pos+4)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)pos=NOP; // nop +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)(pos+4)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32 + *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 +#else + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32 + *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 +#endif break; case t_NEGb: case t_NEGw: case t_NEGd: - *(Bit32u*)pos=RSB_IMM(FC_RETOP, HOST_a1, 0, 0); // rsb FC_RETOP, a1, #0 + *(Bit32u*)pos=NOP; // nop *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+8)=RSB_IMM(FC_RETOP, HOST_a1, 0, 0); // rsb FC_RETOP, a1, #0 +#if C_TARGETCPU != ARMV7LE *(Bit32u*)(pos+12)=NOP; // nop +#endif break; default: +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)pos=MOVW(temp1, ((Bit32u)fct_ptr) & 0xffff); // movw temp1, #(fct_ptr & 0xffff) + *(Bit32u*)(pos+4)=MOVT(temp1, ((Bit32u)fct_ptr) >> 16); // movt temp1, #(fct_ptr >> 16) +#else *(Bit32u*)(pos+12)=(Bit32u)fct_ptr; // simple_func +#endif break; } +#else +#if C_TARGETCPU == ARMV7LE + *(Bit32u*)pos=MOVW(temp1, ((Bit32u)fct_ptr) & 0xffff); // movw temp1, #(fct_ptr & 0xffff) + *(Bit32u*)(pos+4)=MOVT(temp1, ((Bit32u)fct_ptr) >> 16); // movt temp1, #(fct_ptr >> 16) #else *(Bit32u*)(pos+12)=(Bit32u)fct_ptr; // simple_func #endif +#endif } #endif @@ -1043,7 +1263,7 @@ static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) { // the upper 24bit of the destination register can be destroyed // this function can use FC_OP1/FC_OP2 as dest_reg which are // not directly byte-accessible on some architectures -static void INLINE gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) { +static void gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) { cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index] } diff --git a/src/cpu/core_dynrec/risc_armv4le-s3.h b/src/cpu/core_dynrec/risc_armv4le-s3.h deleted file mode 100644 index d4e7800..0000000 --- a/src/cpu/core_dynrec/risc_armv4le-s3.h +++ /dev/null @@ -1,918 +0,0 @@ -/* - * Copyright (C) 2002-2011 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - - - -/* ARMv4 (little endian) backend by M-HT (speed-tweaked arm version) */ - - -// temporary registers -#define temp1 HOST_ip -#define temp2 HOST_v3 -#define temp3 HOST_v4 - -// register that holds function return values -#define FC_RETOP HOST_a1 - -// register used for address calculations, -#define FC_ADDR HOST_v1 // has to be saved across calls, see DRC_PROTECT_ADDR_REG - -// register that holds the first parameter -#define FC_OP1 HOST_a1 - -// register that holds the second parameter -#define FC_OP2 HOST_a2 - -// special register that holds the third parameter for _R3 calls (byte accessible) -#define FC_OP3 HOST_v2 - -// register that holds byte-accessible temporary values -#define FC_TMP_BA1 HOST_a1 - -// register that holds byte-accessible temporary values -#define FC_TMP_BA2 HOST_a2 - -// temporary register for LEA -#define TEMP_REG_DRC HOST_v2 - -#ifdef DRC_USE_REGS_ADDR -// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code -#define FC_REGS_ADDR HOST_v7 -#endif - -#ifdef DRC_USE_SEGS_ADDR -// used to hold the address of "Segs" - preferably filled in function gen_run_code -#define FC_SEGS_ADDR HOST_v8 -#endif - - -// helper macro -#define ROTATE_SCALE(x) ( (x)?(32 - x):(0) ) - - -// instruction encodings - -// move -// mov dst, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define MOV_IMM(dst, imm, rimm) (0xe3a00000 + ((dst) << 12) + (imm) + ((rimm) << 7) ) -// mov dst, src, lsl #imm -#define MOV_REG_LSL_IMM(dst, src, imm) (0xe1a00000 + ((dst) << 12) + (src) + ((imm) << 7) ) -// movs dst, src, lsl #imm -#define MOVS_REG_LSL_IMM(dst, src, imm) (0xe1b00000 + ((dst) << 12) + (src) + ((imm) << 7) ) -// mov dst, src, lsr #imm -#define MOV_REG_LSR_IMM(dst, src, imm) (0xe1a00020 + ((dst) << 12) + (src) + ((imm) << 7) ) -// mov dst, src, asr #imm -#define MOV_REG_ASR_IMM(dst, src, imm) (0xe1a00040 + ((dst) << 12) + (src) + ((imm) << 7) ) -// mov dst, src, lsl rreg -#define MOV_REG_LSL_REG(dst, src, rreg) (0xe1a00010 + ((dst) << 12) + (src) + ((rreg) << 8) ) -// mov dst, src, lsr rreg -#define MOV_REG_LSR_REG(dst, src, rreg) (0xe1a00030 + ((dst) << 12) + (src) + ((rreg) << 8) ) -// mov dst, src, asr rreg -#define MOV_REG_ASR_REG(dst, src, rreg) (0xe1a00050 + ((dst) << 12) + (src) + ((rreg) << 8) ) -// mov dst, src, ror rreg -#define MOV_REG_ROR_REG(dst, src, rreg) (0xe1a00070 + ((dst) << 12) + (src) + ((rreg) << 8) ) -// mvn dst, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define MVN_IMM(dst, imm, rimm) (0xe3e00000 + ((dst) << 12) + (imm) + ((rimm) << 7) ) - -// arithmetic -// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) -// add dst, src1, src2, lsl #imm -#define ADD_REG_LSL_IMM(dst, src1, src2, imm) (0xe0800000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) ) -// sub dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define SUB_IMM(dst, src, imm, rimm) (0xe2400000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) -// sub dst, src1, src2, lsl #imm -#define SUB_REG_LSL_IMM(dst, src1, src2, imm) (0xe0400000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) ) -// rsb dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define RSB_IMM(dst, src, imm, rimm) (0xe2600000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) -// cmp src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define CMP_IMM(src, imm, rimm) (0xe3500000 + ((src) << 16) + (imm) + ((rimm) << 7) ) -// nop -#define NOP MOV_REG_LSL_IMM(HOST_r0, HOST_r0, 0) - -// logical -// tst src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define TST_IMM(src, imm, rimm) (0xe3100000 + ((src) << 16) + (imm) + ((rimm) << 7) ) -// and dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define AND_IMM(dst, src, imm, rimm) (0xe2000000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) -// and dst, src1, src2, lsl #imm -#define AND_REG_LSL_IMM(dst, src1, src2, imm) (0xe0000000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) ) -// orr dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define ORR_IMM(dst, src, imm, rimm) (0xe3800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) -// orr dst, src1, src2, lsl #imm -#define ORR_REG_LSL_IMM(dst, src1, src2, imm) (0xe1800000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) ) -// orr dst, src1, src2, lsr #imm -#define ORR_REG_LSR_IMM(dst, src1, src2, imm) (0xe1800020 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) ) -// eor dst, src1, src2, lsl #imm -#define EOR_REG_LSL_IMM(dst, src1, src2, imm) (0xe0200000 + ((dst) << 12) + ((src1) << 16) + (src2) + ((imm) << 7) ) -// bic dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 -#define BIC_IMM(dst, src, imm, rimm) (0xe3c00000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) - -// load -// ldr reg, [addr, #imm] @ 0 <= imm < 4096 -#define LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) ) -// ldrh reg, [addr, #imm] @ 0 <= imm < 256 -#define LDRH_IMM(reg, addr, imm) (0xe1d000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) ) -// ldrb reg, [addr, #imm] @ 0 <= imm < 4096 -#define LDRB_IMM(reg, addr, imm) (0xe5d00000 + ((reg) << 12) + ((addr) << 16) + (imm) ) - -// store -// str reg, [addr, #imm] @ 0 <= imm < 4096 -#define STR_IMM(reg, addr, imm) (0xe5800000 + ((reg) << 12) + ((addr) << 16) + (imm) ) -// strh reg, [addr, #imm] @ 0 <= imm < 256 -#define STRH_IMM(reg, addr, imm) (0xe1c000b0 + ((reg) << 12) + ((addr) << 16) + (((imm) & 0xf0) << 4) + ((imm) & 0x0f) ) -// strb reg, [addr, #imm] @ 0 <= imm < 4096 -#define STRB_IMM(reg, addr, imm) (0xe5c00000 + ((reg) << 12) + ((addr) << 16) + (imm) ) - -// branch -// beq pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 -#define BEQ_FWD(imm) (0x0a000000 + ((imm) >> 2) ) -// bne pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 -#define BNE_FWD(imm) (0x1a000000 + ((imm) >> 2) ) -// bgt pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 -#define BGT_FWD(imm) (0xca000000 + ((imm) >> 2) ) -// b pc+imm @ 0 <= imm < 32M & imm mod 4 = 0 -#define B_FWD(imm) (0xea000000 + ((imm) >> 2) ) -// bx reg -#define BX(reg) (0xe12fff10 + (reg) ) - - -// move a full register from reg_src to reg_dst -static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { - if(reg_src == reg_dst) return; - cache_addd( MOV_REG_LSL_IMM(reg_dst, reg_src, 0) ); // mov reg_dst, reg_src -} - -// move a 32bit constant value into dest_reg -static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { - Bits first, scale; - if (imm == 0) { - cache_addd( MOV_IMM(dest_reg, 0, 0) ); // mov dest_reg, #0 - } else { - scale = 0; - first = 1; - while (imm) { - while ((imm & 3) == 0) { - imm>>=2; - scale+=2; - } - if (first) { - cache_addd( MOV_IMM(dest_reg, imm & 0xff, ROTATE_SCALE(scale)) ); // mov dest_reg, #((imm & 0xff) << scale) - first = 0; - } else { - cache_addd( ORR_IMM(dest_reg, dest_reg, imm & 0xff, ROTATE_SCALE(scale)) ); // orr dest_reg, dest_reg, #((imm & 0xff) << scale) - } - imm>>=8; - scale+=8; - } - } -} - -// helper function for gen_mov_word_to_reg -static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) { - // alignment.... - if (dword) { - if ((Bit32u)data & 3) { - if ( ((Bit32u)data & 3) == 2 ) { - cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] - cache_addd( LDRH_IMM(temp2, data_reg, 2) ); // ldrh temp2, [data_reg, #2] - cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 16) ); // orr dest_reg, dest_reg, temp2, lsl #16 - } else { - cache_addd( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg] - cache_addd( LDRH_IMM(temp2, data_reg, 1) ); // ldrh temp2, [data_reg, #1] - cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 8) ); // orr dest_reg, dest_reg, temp2, lsl #8 - cache_addd( LDRB_IMM(temp2, data_reg, 3) ); // ldrb temp2, [data_reg, #3] - cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 24) ); // orr dest_reg, dest_reg, temp2, lsl #24 - } - } else { - cache_addd( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg] - } - } else { - if ((Bit32u)data & 1) { - cache_addd( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg] - cache_addd( LDRB_IMM(temp2, data_reg, 1) ); // ldrb temp2, [data_reg, #1] - cache_addd( ORR_REG_LSL_IMM(dest_reg, dest_reg, temp2, 8) ); // orr dest_reg, dest_reg, temp2, lsl #8 - } else { - cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] - } - } -} - -// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg -// 16bit moves may destroy the upper 16bit of the destination register -static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { - gen_mov_dword_to_reg_imm(temp1, (Bit32u)data); - gen_mov_word_to_reg_helper(dest_reg, data, dword, temp1); -} - -// move a 16bit constant value into dest_reg -// the upper 16bit of the destination register may be destroyed -static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { - gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm); -} - -// helper function for gen_mov_word_from_reg -static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) { - // alignment.... - if (dword) { - if ((Bit32u)dest & 3) { - if ( ((Bit32u)dest & 3) == 2 ) { - cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] - cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 16) ); // mov temp2, src_reg, lsr #16 - cache_addd( STRH_IMM(temp2, data_reg, 2) ); // strh temp2, [data_reg, #2] - } else { - cache_addd( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg] - cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 8) ); // mov temp2, src_reg, lsr #8 - cache_addd( STRH_IMM(temp2, data_reg, 1) ); // strh temp2, [data_reg, #1] - cache_addd( MOV_REG_LSR_IMM(temp2, temp2, 16) ); // mov temp2, temp2, lsr #16 - cache_addd( STRB_IMM(temp2, data_reg, 3) ); // strb temp2, [data_reg, #3] - } - } else { - cache_addd( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg] - } - } else { - if ((Bit32u)dest & 1) { - cache_addd( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg] - cache_addd( MOV_REG_LSR_IMM(temp2, src_reg, 8) ); // mov temp2, src_reg, lsr #8 - cache_addd( STRB_IMM(temp2, data_reg, 1) ); // strb temp2, [data_reg, #1] - } else { - cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] - } - } -} - -// move 32bit (dword==true) or 16bit (dword==false) of a register into memory -static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_from_reg_helper(src_reg, dest, dword, temp1); -} - -// move an 8bit value from memory into dest_reg -// the upper 24bit of the destination register can be destroyed -// this function does not use FC_OP1/FC_OP2 as dest_reg as these -// registers might not be directly byte-accessible on some architectures -static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { - gen_mov_dword_to_reg_imm(temp1, (Bit32u)data); - cache_addd( LDRB_IMM(dest_reg, temp1, 0) ); // ldrb dest_reg, [temp1] -} - -// move an 8bit value from memory into dest_reg -// the upper 24bit of the destination register can be destroyed -// this function can use FC_OP1/FC_OP2 as dest_reg which are -// not directly byte-accessible on some architectures -static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) { - gen_mov_byte_to_reg_low(dest_reg, data); -} - -// move an 8bit constant value into dest_reg -// the upper 24bit of the destination register can be destroyed -// this function does not use FC_OP1/FC_OP2 as dest_reg as these -// registers might not be directly byte-accessible on some architectures -static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) { - cache_addd( MOV_IMM(dest_reg, imm, 0) ); // mov dest_reg, #(imm) -} - -// move an 8bit constant value into dest_reg -// the upper 24bit of the destination register can be destroyed -// this function can use FC_OP1/FC_OP2 as dest_reg which are -// not directly byte-accessible on some architectures -static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) { - gen_mov_byte_to_reg_low_imm(dest_reg, imm); -} - -// move the lowest 8bit of a register into memory -static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - cache_addd( STRB_IMM(src_reg, temp1, 0) ); // strb src_reg, [temp1] -} - - - -// convert an 8bit word to a 32bit dword -// the register is zero-extended (sign==false) or sign-extended (sign==true) -static void gen_extend_byte(bool sign,HostReg reg) { - if (sign) { - cache_addd( MOV_REG_LSL_IMM(reg, reg, 24) ); // mov reg, reg, lsl #24 - cache_addd( MOV_REG_ASR_IMM(reg, reg, 24) ); // mov reg, reg, asr #24 - } else { - cache_addd( AND_IMM(reg, reg, 0xff, 0) ); // and reg, reg, #0xff - } -} - -// convert a 16bit word to a 32bit dword -// the register is zero-extended (sign==false) or sign-extended (sign==true) -static void gen_extend_word(bool sign,HostReg reg) { - if (sign) { - cache_addd( MOV_REG_LSL_IMM(reg, reg, 16) ); // mov reg, reg, lsl #16 - cache_addd( MOV_REG_ASR_IMM(reg, reg, 16) ); // mov reg, reg, asr #16 - } else { - cache_addd( MOV_REG_LSL_IMM(reg, reg, 16) ); // mov reg, reg, lsl #16 - cache_addd( MOV_REG_LSR_IMM(reg, reg, 16) ); // mov reg, reg, lsr #16 - } -} - -// add a 32bit value from memory to a full register -static void gen_add(HostReg reg,void* op) { - gen_mov_word_to_reg(temp3, op, 1); - cache_addd( ADD_REG_LSL_IMM(reg, reg, temp3, 0) ); // add reg, reg, temp3 -} - -// add a 32bit constant value to a full register -static void gen_add_imm(HostReg reg,Bit32u imm) { - Bits scale; - if(!imm) return; - if (imm == 0xffffffff) { - cache_addd( SUB_IMM(reg, reg, 1, 0) ); // sub reg, reg, #1 - } else { - scale = 0; - while (imm) { - while ((imm & 3) == 0) { - imm>>=2; - scale+=2; - } - cache_addd( ADD_IMM(reg, reg, imm & 0xff, ROTATE_SCALE(scale)) ); // add reg, reg, #((imm & 0xff) << scale) - imm>>=8; - scale+=8; - } - } -} - -// and a 32bit constant value with a full register -static void gen_and_imm(HostReg reg,Bit32u imm) { - Bits scale; - Bit32u imm2; - imm2 = ~imm; - if(!imm2) return; - if (!imm) { - cache_addd( MOV_IMM(reg, 0, 0) ); // mov reg, #0 - } else { - scale = 0; - while (imm2) { - while ((imm2 & 3) == 0) { - imm2>>=2; - scale+=2; - } - cache_addd( BIC_IMM(reg, reg, imm2 & 0xff, ROTATE_SCALE(scale)) ); // bic reg, reg, #((imm2 & 0xff) << scale) - imm2>>=8; - scale+=8; - } - } -} - - -// move a 32bit constant value into memory -static void gen_mov_direct_dword(void* dest,Bit32u imm) { - gen_mov_dword_to_reg_imm(temp3, imm); - gen_mov_word_from_reg(temp3, dest, 1); -} - -// move an address into memory -static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { - gen_mov_direct_dword(dest,(Bit32u)imm); -} - -// add an 8bit constant value to a dword memory value -static void gen_add_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_to_reg_helper(temp3, dest, 1, temp1); - if (imm >= 0) { - cache_addd( ADD_IMM(temp3, temp3, (Bit32s)imm, 0) ); // add temp3, temp3, #(imm) - } else { - cache_addd( SUB_IMM(temp3, temp3, -((Bit32s)imm), 0) ); // sub temp3, temp3, #(-imm) - } - gen_mov_word_from_reg_helper(temp3, dest, 1, temp1); -} - -// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value -static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { - if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_add_direct_byte(dest,(Bit8s)imm); - return; - } - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_to_reg_helper(temp3, dest, dword, temp1); - // maybe use function gen_add_imm - if (dword) { - gen_mov_dword_to_reg_imm(temp2, imm); - } else { - gen_mov_word_to_reg_imm(temp2, (Bit16u)imm); - } - cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2 - gen_mov_word_from_reg_helper(temp3, dest, dword, temp1); -} - -// subtract an 8bit constant value from a dword memory value -static void gen_sub_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_to_reg_helper(temp3, dest, 1, temp1); - if (imm >= 0) { - cache_addd( SUB_IMM(temp3, temp3, (Bit32s)imm, 0) ); // sub temp3, temp3, #(imm) - } else { - cache_addd( ADD_IMM(temp3, temp3, -((Bit32s)imm), 0) ); // add temp3, temp3, #(-imm) - } - gen_mov_word_from_reg_helper(temp3, dest, 1, temp1); -} - -// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value -static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { - if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_sub_direct_byte(dest,(Bit8s)imm); - return; - } - gen_mov_dword_to_reg_imm(temp1, (Bit32u)dest); - gen_mov_word_to_reg_helper(temp3, dest, dword, temp1); - // maybe use function gen_add_imm/gen_sub_imm - if (dword) { - gen_mov_dword_to_reg_imm(temp2, imm); - } else { - gen_mov_word_to_reg_imm(temp2, (Bit16u)imm); - } - cache_addd( SUB_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // sub temp3, temp3, temp2 - gen_mov_word_from_reg_helper(temp3, dest, dword, temp1); -} - -// effective address calculation, destination is dest_reg -// scale_reg is scaled by scale (scale_reg*(2^scale)) and -// added to dest_reg, then the immediate value is added -static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) { - cache_addd( ADD_REG_LSL_IMM(dest_reg, dest_reg, scale_reg, scale) ); // add dest_reg, dest_reg, scale_reg, lsl #(scale) - gen_add_imm(dest_reg, imm); -} - -// effective address calculation, destination is dest_reg -// dest_reg is scaled by scale (dest_reg*(2^scale)), -// then the immediate value is added -static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) { - if (scale) { - cache_addd( MOV_REG_LSL_IMM(dest_reg, dest_reg, scale) ); // mov dest_reg, dest_reg, lsl #(scale) - } - gen_add_imm(dest_reg, imm); -} - -// generate a call to a parameterless function -static void INLINE gen_call_function_raw(void * func) { - cache_addd( LDR_IMM(temp1, HOST_pc, 4) ); // ldr temp1, [pc, #4] - cache_addd( ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4 - cache_addd( BX(temp1) ); // bx temp1 - cache_addd((Bit32u)func); // .int func -} - -// generate a call to a function with paramcount parameters -// note: the parameters are loaded in the architecture specific way -// using the gen_load_param_ functions below -static Bit32u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) { - Bit32u proc_addr = (Bit32u)cache.pos; - gen_call_function_raw(func); - return proc_addr; -} - -#if (1) -// max of 4 parameters in a1-a4 - -// load an immediate value as param'th function parameter -static void INLINE gen_load_param_imm(Bitu imm,Bitu param) { - gen_mov_dword_to_reg_imm(param, imm); -} - -// load an address as param'th function parameter -static void INLINE gen_load_param_addr(Bitu addr,Bitu param) { - gen_mov_dword_to_reg_imm(param, addr); -} - -// load a host-register as param'th function parameter -static void INLINE gen_load_param_reg(Bitu reg,Bitu param) { - gen_mov_regs(param, reg); -} - -// load a value from memory as param'th function parameter -static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { - gen_mov_word_to_reg(param, (void *)mem, 1); -} -#else - other arm abis -#endif - -// jump to an address pointed at by ptr, offset is in imm -static void gen_jmp_ptr(void * ptr,Bits imm=0) { - Bits scale; - Bitu imm2; - gen_mov_word_to_reg(temp3, ptr, 1); - - if (imm) { - scale = 0; - imm2 = (Bitu)imm; - while (imm2) { - while ((imm2 & 3) == 0) { - imm2>>=2; - scale+=2; - } - cache_addd( ADD_IMM(temp3, temp3, imm2 & 0xff, ROTATE_SCALE(scale)) ); // add temp3, temp3, #((imm2 & 0xff) << scale) - imm2>>=8; - scale+=8; - } - } - -#if (1) -// (*ptr) should be word aligned - if ((imm & 0x03) == 0) { - cache_addd( LDR_IMM(temp1, temp3, 0) ); // ldr temp1, [temp3] - } else -#endif - { - cache_addd( LDRB_IMM(temp1, temp3, 0) ); // ldrb temp1, [temp3] - cache_addd( LDRB_IMM(temp2, temp3, 1) ); // ldrb temp2, [temp3, #1] - cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 8) ); // orr temp1, temp1, temp2, lsl #8 - cache_addd( LDRB_IMM(temp2, temp3, 2) ); // ldrb temp2, [temp3, #2] - cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 16) ); // orr temp1, temp1, temp2, lsl #16 - cache_addd( LDRB_IMM(temp2, temp3, 3) ); // ldrb temp2, [temp3, #3] - cache_addd( ORR_REG_LSL_IMM(temp1, temp1, temp2, 24) ); // orr temp1, temp1, temp2, lsl #24 - } - - cache_addd( BX(temp1) ); // bx temp1 -} - -// short conditional jump (+-127 bytes) if register is zero -// the destination is set by gen_fill_branch() later -static Bit32u gen_create_branch_on_zero(HostReg reg,bool dword) { - if (dword) { - cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0 - } else { - cache_addd( MOVS_REG_LSL_IMM(temp1, reg, 16) ); // movs temp1, reg, lsl #16 - } - cache_addd( BEQ_FWD(0) ); // beq j - return ((Bit32u)cache.pos-4); -} - -// short conditional jump (+-127 bytes) if register is nonzero -// the destination is set by gen_fill_branch() later -static Bit32u gen_create_branch_on_nonzero(HostReg reg,bool dword) { - if (dword) { - cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0 - } else { - cache_addd( MOVS_REG_LSL_IMM(temp1, reg, 16) ); // movs temp1, reg, lsl #16 - } - cache_addd( BNE_FWD(0) ); // bne j - return ((Bit32u)cache.pos-4); -} - -// calculate relative offset and fill it into the location pointed to by data -static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) { -#if C_DEBUG - Bits len=(Bit32u)cache.pos-(data+8); - if (len<0) len=-len; - if (len>0x02000000) LOG_MSG("Big jump %d",len); -#endif - *(Bit32u*)data=( (*(Bit32u*)data) & 0xff000000 ) | ( ( ((Bit32u)cache.pos - (data+8)) >> 2 ) & 0x00ffffff ); -} - -// conditional jump if register is nonzero -// for isdword==true the 32bit of the register are tested -// for isdword==false the lowest 8bit of the register are tested -static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) { - if (isdword) { - cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0 - } else { - cache_addd( TST_IMM(reg, 0xff, 0) ); // tst reg, #0xff - } - cache_addd( BEQ_FWD(8) ); // beq nobranch (pc +8) - cache_addd( LDR_IMM(temp1, HOST_pc, 0) ); // ldr temp1, [pc, #0] - cache_addd( BX(temp1) ); // bx temp1 - cache_addd(0); // fill j - // nobranch: - return ((Bit32u)cache.pos-4); -} - -// compare 32bit-register against zero and jump if value less/equal than zero -static Bit32u gen_create_branch_long_leqzero(HostReg reg) { - cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0 - cache_addd( BGT_FWD(8) ); // bgt nobranch (pc+8) - cache_addd( LDR_IMM(temp1, HOST_pc, 0) ); // ldr temp1, [pc, #0] - cache_addd( BX(temp1) ); // bx temp1 - cache_addd(0); // fill j - // nobranch: - return ((Bit32u)cache.pos-4); -} - -// calculate long relative offset and fill it into the location pointed to by data -static void INLINE gen_fill_branch_long(Bit32u data) { - // this is an absolute branch - *(Bit32u*)data=(Bit32u)cache.pos; -} - -static void gen_run_code(void) { - cache_addd(0xe92d4000); // stmfd sp!, {lr} - cache_addd(0xe92d0cf0); // stmfd sp!, {v1-v4,v7,v8} - - // adr: 8 - cache_addd( LDR_IMM(FC_SEGS_ADDR, HOST_pc, 64 - (8 + 8)) ); // ldr FC_SEGS_ADDR, [pc, #(&Segs)] - // adr: 12 - cache_addd( LDR_IMM(FC_REGS_ADDR, HOST_pc, 68 - (12 + 8)) ); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)] - - cache_addd( ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4 - cache_addd(0xe92d4000); // stmfd sp!, {lr} - cache_addd( BX(HOST_r0) ); // bx r0 - - cache_addd(0xe8bd0cf0); // ldmfd sp!, {v1-v4,v7,v8} - - cache_addd(0xe8bd4000); // ldmfd sp!, {lr} - cache_addd( BX(HOST_lr) ); // bx lr - - // fill up to 64 bytes - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - cache_addd( NOP ); // nop - - // adr: 64 - cache_addd((Bit32u)&Segs); // address of "Segs" - // adr: 68 - cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs" -} - -// return from a function -static void gen_return_function(void) { - cache_addd(0xe8bd4000); // ldmfd sp!, {lr} - cache_addd( BX(HOST_lr) ); // bx lr -} - -#ifdef DRC_FLAGS_INVALIDATION - -// called when a call to a function can be replaced by a -// call to a simpler function -static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) { -#ifdef DRC_FLAGS_INVALIDATION_DCODE - // try to avoid function calls but rather directly fill in code - switch (flags_type) { - case t_ADDb: - case t_ADDw: - case t_ADDd: - *(Bit32u*)pos=ADD_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // add FC_RETOP, a1, a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_ORb: - case t_ORw: - case t_ORd: - *(Bit32u*)pos=ORR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // orr FC_RETOP, a1, a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_ANDb: - case t_ANDw: - case t_ANDd: - *(Bit32u*)pos=AND_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // and FC_RETOP, a1, a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_SUBb: - case t_SUBw: - case t_SUBd: - *(Bit32u*)pos=SUB_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // sub FC_RETOP, a1, a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_XORb: - case t_XORw: - case t_XORd: - *(Bit32u*)pos=EOR_REG_LSL_IMM(FC_RETOP, HOST_a1, HOST_a2, 0); // eor FC_RETOP, a1, a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_CMPb: - case t_CMPw: - case t_CMPd: - case t_TESTb: - case t_TESTw: - case t_TESTd: - *(Bit32u*)pos=B_FWD(8); // b (pc+2*4) - break; - case t_INCb: - case t_INCw: - case t_INCd: - *(Bit32u*)pos=ADD_IMM(FC_RETOP, HOST_a1, 1, 0); // add FC_RETOP, a1, #1 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_DECb: - case t_DECw: - case t_DECd: - *(Bit32u*)pos=SUB_IMM(FC_RETOP, HOST_a1, 1, 0); // sub FC_RETOP, a1, #1 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_SHLb: - case t_SHLw: - case t_SHLd: - *(Bit32u*)pos=MOV_REG_LSL_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsl a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_SHRb: - *(Bit32u*)pos=AND_IMM(FC_RETOP, HOST_a1, 0xff, 0); // and FC_RETOP, a1, #0xff - *(Bit32u*)(pos+4)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2 - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_SHRw: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 - *(Bit32u*)(pos+4)=MOV_REG_LSR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, lsr #16 - *(Bit32u*)(pos+8)=MOV_REG_LSR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, lsr a2 - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_SHRd: - *(Bit32u*)pos=MOV_REG_LSR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, lsr a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_SARb: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24 - *(Bit32u*)(pos+4)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 24); // mov FC_RETOP, FC_RETOP, asr #24 - *(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2 - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_SARw: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 - *(Bit32u*)(pos+4)=MOV_REG_ASR_IMM(FC_RETOP, FC_RETOP, 16); // mov FC_RETOP, FC_RETOP, asr #16 - *(Bit32u*)(pos+8)=MOV_REG_ASR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, asr a2 - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_SARd: - *(Bit32u*)pos=MOV_REG_ASR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, asr a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_RORb: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 24); // mov FC_RETOP, a1, lsl #24 - *(Bit32u*)(pos+4)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 8); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #8 - *(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16 - *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2 - break; - case t_RORw: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 - *(Bit32u*)(pos+4)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16 - *(Bit32u*)(pos+8)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2 - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_RORd: - *(Bit32u*)pos=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_ROLw: - *(Bit32u*)pos=MOV_REG_LSL_IMM(FC_RETOP, HOST_a1, 16); // mov FC_RETOP, a1, lsl #16 - *(Bit32u*)(pos+4)=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32 - *(Bit32u*)(pos+8)=ORR_REG_LSR_IMM(FC_RETOP, FC_RETOP, FC_RETOP, 16); // orr FC_RETOP, FC_RETOP, FC_RETOP, lsr #16 - *(Bit32u*)(pos+12)=MOV_REG_ROR_REG(FC_RETOP, FC_RETOP, HOST_a2); // mov FC_RETOP, FC_RETOP, ror a2 - break; - case t_ROLd: - *(Bit32u*)pos=RSB_IMM(HOST_a2, HOST_a2, 32, 0); // rsb a2, a2, #32 - *(Bit32u*)(pos+4)=MOV_REG_ROR_REG(FC_RETOP, HOST_a1, HOST_a2); // mov FC_RETOP, a1, ror a2 - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - case t_NEGb: - case t_NEGw: - case t_NEGd: - *(Bit32u*)pos=RSB_IMM(FC_RETOP, HOST_a1, 0, 0); // rsb FC_RETOP, a1, #0 - *(Bit32u*)(pos+4)=NOP; // nop - *(Bit32u*)(pos+8)=NOP; // nop - *(Bit32u*)(pos+12)=NOP; // nop - break; - default: - *(Bit32u*)(pos+12)=(Bit32u)fct_ptr; // simple_func - break; - - } -#else - *(Bit32u*)(pos+12)=(Bit32u)fct_ptr; // simple_func -#endif -} -#endif - -static void cache_block_before_close(void) { } - -#ifdef DRC_USE_SEGS_ADDR - -// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero) -// 16bit moves may destroy the upper 16bit of the destination register -static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) { - cache_addd( LDRH_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldrh dest_reg, [FC_SEGS_ADDR, #index] -} - -// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero) -static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) { - cache_addd( LDR_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldr dest_reg, [FC_SEGS_ADDR, #index] -} - -// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero) -static void gen_add_seg32_to_reg(HostReg reg,Bitu index) { - cache_addd( LDR_IMM(temp1, FC_SEGS_ADDR, index) ); // ldr temp1, [FC_SEGS_ADDR, #index] - cache_addd( ADD_REG_LSL_IMM(reg, reg, temp1, 0) ); // add reg, reg, temp1 -} - -#endif - -#ifdef DRC_USE_REGS_ADDR - -// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero) -// 16bit moves may destroy the upper 16bit of the destination register -static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) { - cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index] -} - -// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero) -static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) { - cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index] -} - -// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero) -// 16bit moves may destroy the upper 16bit of the destination register -static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) { - if (dword) { - cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index] - } else { - cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index] - } -} - -// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR -// the upper 24bit of the destination register can be destroyed -// this function does not use FC_OP1/FC_OP2 as dest_reg as these -// registers might not be directly byte-accessible on some architectures -static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) { - cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index] -} - -// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR -// the upper 24bit of the destination register can be destroyed -// this function can use FC_OP1/FC_OP2 as dest_reg which are -// not directly byte-accessible on some architectures -static void INLINE gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) { - cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index] -} - - -// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero) -static void gen_add_regval32_to_reg(HostReg reg,Bitu index) { - cache_addd( LDR_IMM(temp2, FC_REGS_ADDR, index) ); // ldr temp2, [FC_REGS_ADDR, #index] - cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2 -} - - -// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero) -static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) { - cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index] -} - -// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero) -static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) { - cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index] -} - -// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero) -static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) { - if (dword) { - cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index] - } else { - cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index] - } -} - -// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR -static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) { - cache_addd( STRB_IMM(src_reg, FC_REGS_ADDR, index) ); // strb src_reg, [FC_REGS_ADDR, #index] -} - -#endif diff --git a/src/cpu/core_dynrec/risc_armv4le-thumb-iw.h b/src/cpu/core_dynrec/risc_armv4le-thumb-iw.h index f1866fe..400ae9b 100644 --- a/src/cpu/core_dynrec/risc_armv4le-thumb-iw.h +++ b/src/cpu/core_dynrec/risc_armv4le-thumb-iw.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -50,15 +50,14 @@ // temporary register for LEA #define TEMP_REG_DRC HOST_a4 -#ifdef DRC_USE_REGS_ADDR // used to hold the address of "cpu_regs" - preferably filled in function gen_run_code #define FC_REGS_ADDR HOST_v7 -#endif -#ifdef DRC_USE_SEGS_ADDR // used to hold the address of "Segs" - preferably filled in function gen_run_code #define FC_SEGS_ADDR HOST_v8 -#endif + +// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code +#define readdata_addr HOST_v5 // instruction encodings @@ -98,10 +97,14 @@ // logical // and dst, src #define AND(dst, src) (0x4000 + (dst) + ((src) << 3) ) +// bic dst, src +#define BIC(dst, src) (0x4380 + (dst) + ((src) << 3) ) // eor dst, src #define EOR(dst, src) (0x4040 + (dst) + ((src) << 3) ) // orr dst, src #define ORR(dst, src) (0x4300 + (dst) + ((src) << 3) ) +// mvn dst, src +#define MVN(dst, src) (0x43c0 + (dst) + ((src) << 3) ) // shift/rotate // lsl dst, src, #imm @@ -128,6 +131,8 @@ #define LDRB_IMM(reg, addr, imm) (0x7800 + (reg) + ((addr) << 3) + ((imm) << 6) ) // ldr reg, [pc, #imm] @ 0 <= imm < 1024 & imm mod 4 = 0 #define LDR_PC_IMM(reg, imm) (0x4800 + ((reg) << 8) + ((imm) >> 2) ) +// ldr reg, [addr1, addr2] +#define LDR_REG(reg, addr1, addr2) (0x5800 + (reg) + ((addr1) << 3) + ((addr2) << 6) ) // store // str reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0 @@ -150,6 +155,25 @@ #define BX(reg) (0x4700 + ((reg) << 3) ) +// arm instructions + +// arithmetic +// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 +#define ARM_ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) + +// load +// ldr reg, [addr, #imm] @ 0 <= imm < 4096 +#define ARM_LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) ) + +// store +// str reg, [addr, #-(imm)]! @ 0 <= imm < 4096 +#define ARM_STR_IMM_M_W(reg, addr, imm) (0xe5200000 + ((reg) << 12) + ((addr) << 16) + (imm) ) + +// branch +// bx reg +#define ARM_BX(reg) (0xe12fff10 + (reg) ) + + // data pool defines #define CACHE_DATA_JUMP (2) #define CACHE_DATA_ALIGN (32) @@ -193,7 +217,7 @@ static void cache_checkinstr(Bit32u size) { cache_datapos = (Bit8u *) (((Bitu)cache.block.active->cache.start + cache.block.active->cache.size - CACHE_DATA_ALIGN) & ~(CACHE_DATA_ALIGN - 1)); } else { register Bit32u cachemodsize; - + cachemodsize = (cache.pos - cache.block.active->cache.start) & (CACHE_MAXSIZE - 1); if (cachemodsize + CACHE_DATA_MAX + CACHE_DATA_ALIGN <= CACHE_MAXSIZE || @@ -275,30 +299,49 @@ static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { cache_addw( MOV_REG(reg_dst, reg_src) ); // mov reg_dst, reg_src } +// helper function +static bool val_single_shift(Bit32u value, Bit32u *val_shift) { + Bit32u shift; + + if (GCC_UNLIKELY(value == 0)) { + *val_shift = 0; + return true; + } + + shift = 0; + while ((value & 1) == 0) { + value>>=1; + shift+=1; + } + + if ((value >> 8) != 0) return false; + + *val_shift = shift; + return true; +} + // move a 32bit constant value into dest_reg static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { - if ((imm & 0xffffff00) == 0) { + Bit32u scale; + + if (imm < 256) { cache_checkinstr(2); cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #(imm) - } else if ((imm & 0xffff00ff) == 0) { + } else if ((~imm) < 256) { cache_checkinstr(4); - cache_addw( MOV_IMM(dest_reg, imm >> 8) ); // mov dest_reg, #(imm >> 8) - cache_addw( LSL_IMM(dest_reg, dest_reg, 8) ); // lsl dest_reg, dest_reg, #8 - } else if ((imm & 0xff00ffff) == 0) { + cache_addw( MOV_IMM(dest_reg, ~imm) ); // mov dest_reg, #(~imm) + cache_addw( MVN(dest_reg, dest_reg) ); // mvn dest_reg, dest_reg + } else if (val_single_shift(imm, &scale)) { cache_checkinstr(4); - cache_addw( MOV_IMM(dest_reg, imm >> 16) ); // mov dest_reg, #(imm >> 16) - cache_addw( LSL_IMM(dest_reg, dest_reg, 16) ); // lsl dest_reg, dest_reg, #16 - } else if ((imm & 0x00ffffff) == 0) { - cache_checkinstr(4); - cache_addw( MOV_IMM(dest_reg, imm >> 24) ); // mov dest_reg, #(imm >> 24) - cache_addw( LSL_IMM(dest_reg, dest_reg, 24) ); // lsl dest_reg, dest_reg, #24 + cache_addw( MOV_IMM(dest_reg, imm >> scale) ); // mov dest_reg, #(imm >> scale) + cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #scale } else { Bit32u diff; cache_checkinstr(4); diff = imm - ((Bit32u)cache.pos+4); - + if ((diff < 1024) && ((imm & 0x03) == 0)) { if (((Bit32u)cache.pos & 0x03) == 0) { cache_addw( ADD_LO_PC_IMM(dest_reg, diff >> 2) ); // add dest_reg, pc, #(diff >> 2) @@ -321,10 +364,61 @@ static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { } } +// helper function +static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) { + switch (size) { + case 4: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 3) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDR_IMM(dest_reg, templo2, data - addr_data) ); // ldr dest_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 2: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 1) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDRH_IMM(dest_reg, templo2, data - addr_data) ); // ldrh dest_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 32)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDRB_IMM(dest_reg, templo2, data - addr_data) ); // ldrb dest_reg, [templo2, #(data - addr_data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) { + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true; + return false; +} + // helper function for gen_mov_word_to_reg static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) { // alignment.... if (dword) { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)data & 3) { if ( ((Bit32u)data & 3) == 2 ) { cache_checkinstr(8); @@ -343,18 +437,23 @@ static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,Ho cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24 cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1 } - } else { + } else +#endif + { cache_checkinstr(2); cache_addw( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg] } } else { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)data & 1) { cache_checkinstr(8); cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg] cache_addw( LDRB_IMM(templo1, data_reg, 1) ); // ldrb templo1, [data_reg, #1] cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8 cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1 - } else { + } else +#endif + { cache_checkinstr(2); cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] } @@ -364,8 +463,10 @@ static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,Ho // move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg // 16bit moves may destroy the upper 16bit of the destination register static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { - gen_mov_dword_to_reg_imm(templo2, (Bit32u)data); - gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2); + if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)data); + gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2); + } } // move a 16bit constant value into dest_reg @@ -374,10 +475,61 @@ static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm); } +// helper function +static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) { + switch (size) { + case 4: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 3) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STR_IMM(src_reg, templo2, data - addr_data) ); // str src_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 2: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 1) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STRH_IMM(src_reg, templo2, data - addr_data) ); // strh src_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 32)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STRB_IMM(src_reg, templo2, data - addr_data) ); // strb src_reg, [templo2, #(data - addr_data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) { + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true; + return false; +} + // helper function for gen_mov_word_from_reg static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) { // alignment.... if (dword) { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)dest & 3) { if ( ((Bit32u)dest & 3) == 2 ) { cache_checkinstr(8); @@ -398,18 +550,23 @@ static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, cache_addw( LSR_IMM(templo1, templo1, 24) ); // lsr templo1, templo1, #24 cache_addw( STRB_IMM(templo1, data_reg, 3) ); // strb templo1, [data_reg, #3] } - } else { + } else +#endif + { cache_checkinstr(2); cache_addw( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg] } } else { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)dest & 1) { cache_checkinstr(8); cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg] cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8 cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1] - } else { + } else +#endif + { cache_checkinstr(2); cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] } @@ -418,8 +575,10 @@ static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, // move 32bit (dword==true) or 16bit (dword==false) of a register into memory static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2); + if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2); + } } // move an 8bit value from memory into dest_reg @@ -427,9 +586,11 @@ static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { // this function does not use FC_OP1/FC_OP2 as dest_reg as these // registers might not be directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { - gen_mov_dword_to_reg_imm(templo1, (Bit32u)data); - cache_checkinstr(2); - cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1] + if (!gen_mov_memval_to_reg(dest_reg, data, 1)) { + gen_mov_dword_to_reg_imm(templo1, (Bit32u)data); + cache_checkinstr(2); + cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1] + } } // move an 8bit value from memory into dest_reg @@ -459,9 +620,11 @@ static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u // move the lowest 8bit of a register into memory static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { - gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest); - cache_checkinstr(2); - cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1] + if (!gen_mov_memval_from_reg(src_reg, dest, 1)) { + gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest); + cache_checkinstr(2); + cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1] + } } @@ -501,18 +664,58 @@ static void gen_add(HostReg reg,void* op) { // add a 32bit constant value to a full register static void gen_add_imm(HostReg reg,Bit32u imm) { + Bit32u imm2, scale; + if(!imm) return; - gen_mov_dword_to_reg_imm(templo1, imm); - cache_checkinstr(2); - cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1 + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if (imm <= 255) { + cache_checkinstr(2); + cache_addw( ADD_IMM8(reg, imm) ); // add reg, #imm + } else if (imm2 <= 255) { + cache_checkinstr(2); + cache_addw( SUB_IMM8(reg, imm2) ); // sub reg, #(-imm) + } else { + if (val_single_shift(imm2, &scale)) { + cache_checkinstr((scale)?6:4); + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( SUB_REG(reg, reg, templo1) ); // sub reg, reg, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_checkinstr(2); + cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1 + } + } } // and a 32bit constant value with a full register static void gen_and_imm(HostReg reg,Bit32u imm) { - if(imm == 0xffffffff) return; - gen_mov_dword_to_reg_imm(templo1, imm); - cache_checkinstr(2); - cache_addw( AND(reg, templo1) ); // and reg, templo1 + Bit32u imm2, scale; + + imm2 = ~imm; + if(!imm2) return; + + if (!imm) { + cache_checkinstr(2); + cache_addw( MOV_IMM(reg, 0) ); // mov reg, #0 + } else { + if (val_single_shift(imm2, &scale)) { + cache_checkinstr((scale)?6:4); + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( BIC(reg, templo1) ); // bic reg, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_checkinstr(2); + cache_addw( AND(reg, templo1) ); // and reg, templo1 + } + } } @@ -527,70 +730,69 @@ static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { gen_mov_direct_dword(dest,(Bit32u)imm); } -// add an 8bit constant value to a dword memory value -static void gen_add_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, 1, templo2); - cache_checkinstr(2); - if (imm >= 0) { - cache_addw( ADD_IMM8(templo3, (Bit32s)imm) ); // add templo3, #(imm) - } else { - cache_addw( SUB_IMM8(templo3, -((Bit32s)imm)) ); // sub templo3, #(-imm) - } - gen_mov_word_from_reg_helper(templo3, dest, 1, templo2); -} - // add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if (!dword) imm &= 0xffff; if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_add_direct_byte(dest,(Bit8s)imm); - return; + + if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); } - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); - if (dword) { - gen_mov_dword_to_reg_imm(templo1, imm); - } else { - gen_mov_word_to_reg_imm(templo1, (Bit16u)imm); + gen_add_imm(templo3, imm); + if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); } - cache_checkinstr(2); - cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1 - gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); } -// subtract an 8bit constant value from a dword memory value -static void gen_sub_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, 1, templo2); - cache_checkinstr(2); - if (imm >= 0) { - cache_addw( SUB_IMM8(templo3, (Bit32s)imm) ); // sub templo3, #(imm) - } else { - cache_addw( ADD_IMM8(templo3, -((Bit32s)imm)) ); // add templo3, #(-imm) - } - gen_mov_word_from_reg_helper(templo3, dest, 1, templo2); +// add an 8bit constant value to a dword memory value +static void gen_add_direct_byte(void* dest,Bit8s imm) { + gen_add_direct_word(dest, (Bit32s)imm, 1); } // subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + Bit32u imm2, scale; + + if (!dword) imm &= 0xffff; if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_sub_direct_byte(dest,(Bit8s)imm); - return; + + if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); } - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); - if (dword) { - gen_mov_dword_to_reg_imm(templo1, imm); + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if (imm <= 255) { + cache_checkinstr(2); + cache_addw( SUB_IMM8(templo3, imm) ); // sub templo3, #imm + } else if (imm2 <= 255) { + cache_checkinstr(2); + cache_addw( ADD_IMM8(templo3, imm2) ); // add templo3, #(-imm) } else { - gen_mov_word_to_reg_imm(templo1, (Bit16u)imm); + if (val_single_shift(imm2, &scale)) { + cache_checkinstr((scale)?6:4); + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_checkinstr(2); + cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1 + } } - cache_checkinstr(2); - cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1 - gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); + + if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); + } +} + +// subtract an 8bit constant value from a dword memory value +static void gen_sub_direct_byte(void* dest,Bit8s imm) { + gen_sub_direct_word(dest, (Bit32s)imm, 1); } // effective address calculation, destination is dest_reg @@ -694,20 +896,22 @@ static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { static void gen_jmp_ptr(void * ptr,Bits imm=0) { gen_mov_word_to_reg(templo3, ptr, 1); - if (imm) { - gen_mov_dword_to_reg_imm(templo2, imm); - cache_checkinstr(2); - cache_addw( ADD_REG(templo3, templo3, templo2) ); // add templo3, templo3, templo2 - } - -#if (1) -// (*ptr) should be word aligned +#if !defined(C_UNALIGNED_MEMORY) +// (*ptr) should be word aligned if ((imm & 0x03) == 0) { - cache_checkinstr(6); - cache_addw( LDR_IMM(templo2, templo3, 0) ); // ldr templo2, [templo3] - } else #endif - { + if ((imm >= 0) && (imm < 128) && ((imm & 3) == 0)) { + cache_checkinstr(6); + cache_addw( LDR_IMM(templo2, templo3, imm) ); // ldr templo2, [templo3, #imm] + } else { + gen_mov_dword_to_reg_imm(templo2, imm); + cache_checkinstr(6); + cache_addw( LDR_REG(templo2, templo3, templo2) ); // ldr templo2, [templo3, templo2] + } +#if !defined(C_UNALIGNED_MEMORY) + } else { + gen_add_imm(templo3, imm); + cache_checkinstr(24); cache_addw( LDRB_IMM(templo2, templo3, 0) ); // ldrb templo2, [templo3] cache_addw( LDRB_IMM(templo1, templo3, 1) ); // ldrb templo1, [templo3, #1] @@ -720,6 +924,7 @@ static void gen_jmp_ptr(void * ptr,Bits imm=0) { cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24 cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1 } +#endif // increase jmp address to keep thumb state cache_addw( ADD_IMM3(templo2, templo2, 1) ); // add templo2, templo2, #1 @@ -815,50 +1020,53 @@ static void INLINE gen_fill_branch_long(Bit32u data) { } static void gen_run_code(void) { - // switch from arm to thumb state - cache_addd(0xe2800000 + (HOST_r3 << 12) + (HOST_pc << 16) + (1)); // add r3, pc, #1 - cache_addd(0xe12fff10 + (HOST_r3)); // bx r3 + Bit8u *pos1, *pos2, *pos3; - // thumb state from now on - cache_addw(0xb500); // push {lr} - cache_addw( MOV_LO_HI(HOST_r3, FC_SEGS_ADDR) ); // mov r3, FC_SEGS_ADDR - cache_addw( MOV_LO_HI(HOST_r2, FC_REGS_ADDR) ); // mov r2, FC_REGS_ADDR - cache_addw(0xb4fc); // push {r2,r3,v1-v4} +#if (__ARM_EABI__) + // 8-byte stack alignment + cache_addd(0xe92d4ff0); // stmfd sp!, {v1-v8,lr} +#else + cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr} +#endif - // adr: 16 - cache_addw( LDR_PC_IMM(HOST_r3, 64 - (16 + 4)) ); // ldr r3, [pc, #(&Segs)] - // adr: 18 - cache_addw( LDR_PC_IMM(HOST_r2, 68 - (18 + 2)) ); // ldr r2, [pc, #(&cpu_regs)] - cache_addw( MOV_HI_LO(FC_SEGS_ADDR, HOST_r3) ); // mov FC_SEGS_ADDR, r3 - cache_addw( MOV_HI_LO(FC_REGS_ADDR, HOST_r2) ); // mov FC_REGS_ADDR, r2 + cache_addd( ARM_ADD_IMM(HOST_r0, HOST_r0, 1, 0) ); // add r0, r0, #1 - // align 4 - cache_addw( ADD_LO_PC_IMM(HOST_r3, 8) ); // add r3, pc, #8 - cache_addw( ADD_IMM8(HOST_r0, 1) ); // add r0, #1 - cache_addw( ADD_IMM8(HOST_r3, 1) ); // add r3, #1 - cache_addw(0xb408); // push {r3} - cache_addw( BX(HOST_r0) ); // bx r0 - cache_addw( NOP ); // nop + pos1 = cache.pos; + cache_addd( 0 ); + pos2 = cache.pos; + cache_addd( 0 ); + pos3 = cache.pos; + cache_addd( 0 ); - // align 4 - cache_addw(0xbcfc); // pop {r2,r3,v1-v4} - cache_addw( MOV_HI_LO(FC_SEGS_ADDR, HOST_r3) ); // mov FC_SEGS_ADDR, r3 - cache_addw( MOV_HI_LO(FC_REGS_ADDR, HOST_r2) ); // mov FC_REGS_ADDR, r2 + cache_addd( ARM_ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4 + cache_addd( ARM_STR_IMM_M_W(HOST_lr, HOST_sp, 4) ); // str lr, [sp, #-4]! + cache_addd( ARM_BX(HOST_r0) ); // bx r0 - cache_addw(0xbc08); // pop {r3} - cache_addw( BX(HOST_r3) ); // bx r3 +#if (__ARM_EABI__) + cache_addd(0xe8bd4ff0); // ldmfd sp!, {v1-v8,lr} +#else + cache_addd(0xe8bd4df0); // ldmfd sp!, {v1-v5,v7,v8,lr} +#endif + cache_addd( ARM_BX(HOST_lr) ); // bx lr - // fill up to 64 bytes - cache_addw( NOP ); // nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } - // adr: 64 + *(Bit32u*)pos1 = ARM_LDR_IMM(FC_SEGS_ADDR, HOST_pc, cache.pos - (pos1 + 8)); // ldr FC_SEGS_ADDR, [pc, #(&Segs)] cache_addd((Bit32u)&Segs); // address of "Segs" - // adr: 68 + + *(Bit32u*)pos2 = ARM_LDR_IMM(FC_REGS_ADDR, HOST_pc, cache.pos - (pos2 + 8)); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)] cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs" + + *(Bit32u*)pos3 = ARM_LDR_IMM(readdata_addr, HOST_pc, cache.pos - (pos3 + 8)); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)] + cache_addd((Bit32u)&core_dynrec.readdata); // address of "core_dynrec.readdata" + + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } } // return from a function diff --git a/src/cpu/core_dynrec/risc_armv4le-thumb-niw.h b/src/cpu/core_dynrec/risc_armv4le-thumb-niw.h index 4b91f22..c6e0c9f 100644 --- a/src/cpu/core_dynrec/risc_armv4le-thumb-niw.h +++ b/src/cpu/core_dynrec/risc_armv4le-thumb-niw.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -50,15 +50,14 @@ // temporary register for LEA #define TEMP_REG_DRC HOST_a4 -#ifdef DRC_USE_REGS_ADDR // used to hold the address of "cpu_regs" - preferably filled in function gen_run_code #define FC_REGS_ADDR HOST_v7 -#endif -#ifdef DRC_USE_SEGS_ADDR // used to hold the address of "Segs" - preferably filled in function gen_run_code #define FC_SEGS_ADDR HOST_v8 -#endif + +// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code +#define readdata_addr HOST_v5 // instruction encodings @@ -98,10 +97,14 @@ // logical // and dst, src #define AND(dst, src) (0x4000 + (dst) + ((src) << 3) ) +// bic dst, src +#define BIC(dst, src) (0x4380 + (dst) + ((src) << 3) ) // eor dst, src #define EOR(dst, src) (0x4040 + (dst) + ((src) << 3) ) // orr dst, src #define ORR(dst, src) (0x4300 + (dst) + ((src) << 3) ) +// mvn dst, src +#define MVN(dst, src) (0x43c0 + (dst) + ((src) << 3) ) // shift/rotate // lsl dst, src, #imm @@ -128,6 +131,8 @@ #define LDRB_IMM(reg, addr, imm) (0x7800 + (reg) + ((addr) << 3) + ((imm) << 6) ) // ldr reg, [pc, #imm] @ 0 <= imm < 1024 & imm mod 4 = 0 #define LDR_PC_IMM(reg, imm) (0x4800 + ((reg) << 8) + ((imm) >> 2) ) +// ldr reg, [addr1, addr2] +#define LDR_REG(reg, addr1, addr2) (0x5800 + (reg) + ((addr1) << 3) + ((addr2) << 6) ) // store // str reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0 @@ -150,6 +155,25 @@ #define BX(reg) (0x4700 + ((reg) << 3) ) +// arm instructions + +// arithmetic +// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 +#define ARM_ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) + +// load +// ldr reg, [addr, #imm] @ 0 <= imm < 4096 +#define ARM_LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) ) + +// store +// str reg, [addr, #-(imm)]! @ 0 <= imm < 4096 +#define ARM_STR_IMM_M_W(reg, addr, imm) (0xe5200000 + ((reg) << 12) + ((addr) << 16) + (imm) ) + +// branch +// bx reg +#define ARM_BX(reg) (0xe12fff10 + (reg) ) + + // data pool defines #define CACHE_DATA_JUMP (2) #define CACHE_DATA_ALIGN (32) @@ -193,7 +217,7 @@ static void cache_checkinstr(Bit32u size) { cache_datapos = (Bit8u *) (((Bitu)cache.block.active->cache.start + cache.block.active->cache.size - CACHE_DATA_ALIGN) & ~(CACHE_DATA_ALIGN - 1)); } else { register Bit32u cachemodsize; - + cachemodsize = (cache.pos - cache.block.active->cache.start) & (CACHE_MAXSIZE - 1); if (cachemodsize + CACHE_DATA_MAX + CACHE_DATA_ALIGN <= CACHE_MAXSIZE || @@ -275,30 +299,49 @@ static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { cache_addw( MOV_REG(reg_dst, reg_src) ); // mov reg_dst, reg_src } +// helper function +static bool val_single_shift(Bit32u value, Bit32u *val_shift) { + Bit32u shift; + + if (GCC_UNLIKELY(value == 0)) { + *val_shift = 0; + return true; + } + + shift = 0; + while ((value & 1) == 0) { + value>>=1; + shift+=1; + } + + if ((value >> 8) != 0) return false; + + *val_shift = shift; + return true; +} + // move a 32bit constant value into dest_reg static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { - if ((imm & 0xffffff00) == 0) { + Bit32u scale; + + if (imm < 256) { cache_checkinstr(2); cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #(imm) - } else if ((imm & 0xffff00ff) == 0) { + } else if ((~imm) < 256) { cache_checkinstr(4); - cache_addw( MOV_IMM(dest_reg, imm >> 8) ); // mov dest_reg, #(imm >> 8) - cache_addw( LSL_IMM(dest_reg, dest_reg, 8) ); // lsl dest_reg, dest_reg, #8 - } else if ((imm & 0xff00ffff) == 0) { + cache_addw( MOV_IMM(dest_reg, ~imm) ); // mov dest_reg, #(~imm) + cache_addw( MVN(dest_reg, dest_reg) ); // mvn dest_reg, dest_reg + } else if (val_single_shift(imm, &scale)) { cache_checkinstr(4); - cache_addw( MOV_IMM(dest_reg, imm >> 16) ); // mov dest_reg, #(imm >> 16) - cache_addw( LSL_IMM(dest_reg, dest_reg, 16) ); // lsl dest_reg, dest_reg, #16 - } else if ((imm & 0x00ffffff) == 0) { - cache_checkinstr(4); - cache_addw( MOV_IMM(dest_reg, imm >> 24) ); // mov dest_reg, #(imm >> 24) - cache_addw( LSL_IMM(dest_reg, dest_reg, 24) ); // lsl dest_reg, dest_reg, #24 + cache_addw( MOV_IMM(dest_reg, imm >> scale) ); // mov dest_reg, #(imm >> scale) + cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #scale } else { Bit32u diff; cache_checkinstr(4); diff = imm - ((Bit32u)cache.pos+4); - + if ((diff < 1024) && ((imm & 0x03) == 0)) { if (((Bit32u)cache.pos & 0x03) == 0) { cache_addw( ADD_LO_PC_IMM(dest_reg, diff >> 2) ); // add dest_reg, pc, #(diff >> 2) @@ -321,10 +364,61 @@ static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { } } +// helper function +static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) { + switch (size) { + case 4: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 3) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDR_IMM(dest_reg, templo2, data - addr_data) ); // ldr dest_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 2: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 1) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDRH_IMM(dest_reg, templo2, data - addr_data) ); // ldrh dest_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 32)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDRB_IMM(dest_reg, templo2, data - addr_data) ); // ldrb dest_reg, [templo2, #(data - addr_data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) { + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true; + return false; +} + // helper function for gen_mov_word_to_reg static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) { // alignment.... if (dword) { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)data & 3) { if ( ((Bit32u)data & 3) == 2 ) { cache_checkinstr(8); @@ -343,18 +437,23 @@ static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,Ho cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24 cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1 } - } else { + } else +#endif + { cache_checkinstr(2); cache_addw( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg] } } else { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)data & 1) { cache_checkinstr(8); cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg] cache_addw( LDRB_IMM(templo1, data_reg, 1) ); // ldrb templo1, [data_reg, #1] cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8 cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1 - } else { + } else +#endif + { cache_checkinstr(2); cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] } @@ -364,8 +463,10 @@ static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,Ho // move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg // 16bit moves may destroy the upper 16bit of the destination register static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { - gen_mov_dword_to_reg_imm(templo2, (Bit32u)data); - gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2); + if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)data); + gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2); + } } // move a 16bit constant value into dest_reg @@ -374,10 +475,61 @@ static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm); } +// helper function +static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) { + switch (size) { + case 4: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 3) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STR_IMM(src_reg, templo2, data - addr_data) ); // str src_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 2: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 1) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STRH_IMM(src_reg, templo2, data - addr_data) ); // strh src_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 32)) { + cache_checkinstr(4); + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STRB_IMM(src_reg, templo2, data - addr_data) ); // strb src_reg, [templo2, #(data - addr_data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) { + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true; + return false; +} + // helper function for gen_mov_word_from_reg static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) { // alignment.... if (dword) { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)dest & 3) { if ( ((Bit32u)dest & 3) == 2 ) { cache_checkinstr(8); @@ -398,18 +550,23 @@ static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, cache_addw( LSR_IMM(templo1, templo1, 24) ); // lsr templo1, templo1, #24 cache_addw( STRB_IMM(templo1, data_reg, 3) ); // strb templo1, [data_reg, #3] } - } else { + } else +#endif + { cache_checkinstr(2); cache_addw( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg] } } else { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)dest & 1) { cache_checkinstr(8); cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg] cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8 cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1] - } else { + } else +#endif + { cache_checkinstr(2); cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] } @@ -418,8 +575,10 @@ static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, // move 32bit (dword==true) or 16bit (dword==false) of a register into memory static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2); + if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2); + } } // move an 8bit value from memory into dest_reg @@ -427,9 +586,11 @@ static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { // this function does not use FC_OP1/FC_OP2 as dest_reg as these // registers might not be directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { - gen_mov_dword_to_reg_imm(templo1, (Bit32u)data); - cache_checkinstr(2); - cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1] + if (!gen_mov_memval_to_reg(dest_reg, data, 1)) { + gen_mov_dword_to_reg_imm(templo1, (Bit32u)data); + cache_checkinstr(2); + cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1] + } } // move an 8bit value from memory into dest_reg @@ -459,9 +620,11 @@ static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u // move the lowest 8bit of a register into memory static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { - gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest); - cache_checkinstr(2); - cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1] + if (!gen_mov_memval_from_reg(src_reg, dest, 1)) { + gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest); + cache_checkinstr(2); + cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1] + } } @@ -501,18 +664,58 @@ static void gen_add(HostReg reg,void* op) { // add a 32bit constant value to a full register static void gen_add_imm(HostReg reg,Bit32u imm) { + Bit32u imm2, scale; + if(!imm) return; - gen_mov_dword_to_reg_imm(templo1, imm); - cache_checkinstr(2); - cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1 + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if (imm <= 255) { + cache_checkinstr(2); + cache_addw( ADD_IMM8(reg, imm) ); // add reg, #imm + } else if (imm2 <= 255) { + cache_checkinstr(2); + cache_addw( SUB_IMM8(reg, imm2) ); // sub reg, #(-imm) + } else { + if (val_single_shift(imm2, &scale)) { + cache_checkinstr((scale)?6:4); + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( SUB_REG(reg, reg, templo1) ); // sub reg, reg, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_checkinstr(2); + cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1 + } + } } // and a 32bit constant value with a full register static void gen_and_imm(HostReg reg,Bit32u imm) { - if(imm == 0xffffffff) return; - gen_mov_dword_to_reg_imm(templo1, imm); - cache_checkinstr(2); - cache_addw( AND(reg, templo1) ); // and reg, templo1 + Bit32u imm2, scale; + + imm2 = ~imm; + if(!imm2) return; + + if (!imm) { + cache_checkinstr(2); + cache_addw( MOV_IMM(reg, 0) ); // mov reg, #0 + } else { + if (val_single_shift(imm2, &scale)) { + cache_checkinstr((scale)?6:4); + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( BIC(reg, templo1) ); // bic reg, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_checkinstr(2); + cache_addw( AND(reg, templo1) ); // and reg, templo1 + } + } } @@ -527,70 +730,69 @@ static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { gen_mov_direct_dword(dest,(Bit32u)imm); } -// add an 8bit constant value to a dword memory value -static void gen_add_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, 1, templo2); - cache_checkinstr(2); - if (imm >= 0) { - cache_addw( ADD_IMM8(templo3, (Bit32s)imm) ); // add templo3, #(imm) - } else { - cache_addw( SUB_IMM8(templo3, -((Bit32s)imm)) ); // sub templo3, #(-imm) - } - gen_mov_word_from_reg_helper(templo3, dest, 1, templo2); -} - // add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if (!dword) imm &= 0xffff; if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_add_direct_byte(dest,(Bit8s)imm); - return; + + if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); } - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); - if (dword) { - gen_mov_dword_to_reg_imm(templo1, imm); - } else { - gen_mov_word_to_reg_imm(templo1, (Bit16u)imm); + gen_add_imm(templo3, imm); + if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); } - cache_checkinstr(2); - cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1 - gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); } -// subtract an 8bit constant value from a dword memory value -static void gen_sub_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, 1, templo2); - cache_checkinstr(2); - if (imm >= 0) { - cache_addw( SUB_IMM8(templo3, (Bit32s)imm) ); // sub templo3, #(imm) - } else { - cache_addw( ADD_IMM8(templo3, -((Bit32s)imm)) ); // add templo3, #(-imm) - } - gen_mov_word_from_reg_helper(templo3, dest, 1, templo2); +// add an 8bit constant value to a dword memory value +static void gen_add_direct_byte(void* dest,Bit8s imm) { + gen_add_direct_word(dest, (Bit32s)imm, 1); } // subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + Bit32u imm2, scale; + + if (!dword) imm &= 0xffff; if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_sub_direct_byte(dest,(Bit8s)imm); - return; + + if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); } - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); - if (dword) { - gen_mov_dword_to_reg_imm(templo1, imm); + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if (imm <= 255) { + cache_checkinstr(2); + cache_addw( SUB_IMM8(templo3, imm) ); // sub templo3, #imm + } else if (imm2 <= 255) { + cache_checkinstr(2); + cache_addw( ADD_IMM8(templo3, imm2) ); // add templo3, #(-imm) } else { - gen_mov_word_to_reg_imm(templo1, (Bit16u)imm); + if (val_single_shift(imm2, &scale)) { + cache_checkinstr((scale)?6:4); + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_checkinstr(2); + cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1 + } } - cache_checkinstr(2); - cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1 - gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); + + if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); + } +} + +// subtract an 8bit constant value from a dword memory value +static void gen_sub_direct_byte(void* dest,Bit8s imm) { + gen_sub_direct_word(dest, (Bit32s)imm, 1); } // effective address calculation, destination is dest_reg @@ -696,20 +898,22 @@ static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { static void gen_jmp_ptr(void * ptr,Bits imm=0) { gen_mov_word_to_reg(templo3, ptr, 1); - if (imm) { - gen_mov_dword_to_reg_imm(templo2, imm); - cache_checkinstr(2); - cache_addw( ADD_REG(templo3, templo3, templo2) ); // add templo3, templo3, templo2 - } - -#if (1) -// (*ptr) should be word aligned +#if !defined(C_UNALIGNED_MEMORY) +// (*ptr) should be word aligned if ((imm & 0x03) == 0) { - cache_checkinstr(6); - cache_addw( LDR_IMM(templo2, templo3, 0) ); // ldr templo2, [templo3] - } else #endif - { + if ((imm >= 0) && (imm < 128) && ((imm & 3) == 0)) { + cache_checkinstr(6); + cache_addw( LDR_IMM(templo2, templo3, imm) ); // ldr templo2, [templo3, #imm] + } else { + gen_mov_dword_to_reg_imm(templo2, imm); + cache_checkinstr(6); + cache_addw( LDR_REG(templo2, templo3, templo2) ); // ldr templo2, [templo3, templo2] + } +#if !defined(C_UNALIGNED_MEMORY) + } else { + gen_add_imm(templo3, imm); + cache_checkinstr(24); cache_addw( LDRB_IMM(templo2, templo3, 0) ); // ldrb templo2, [templo3] cache_addw( LDRB_IMM(templo1, templo3, 1) ); // ldrb templo1, [templo3, #1] @@ -722,6 +926,7 @@ static void gen_jmp_ptr(void * ptr,Bits imm=0) { cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24 cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1 } +#endif // increase jmp address to keep thumb state cache_addw( ADD_IMM3(templo2, templo2, 1) ); // add templo2, templo2, #1 @@ -817,50 +1022,53 @@ static void INLINE gen_fill_branch_long(Bit32u data) { } static void gen_run_code(void) { - // switch from arm to thumb state - cache_addd(0xe2800000 + (HOST_r3 << 12) + (HOST_pc << 16) + (1)); // add r3, pc, #1 - cache_addd(0xe12fff10 + (HOST_r3)); // bx r3 + Bit8u *pos1, *pos2, *pos3; - // thumb state from now on - cache_addw(0xb500); // push {lr} - cache_addw( MOV_LO_HI(HOST_r3, FC_SEGS_ADDR) ); // mov r3, FC_SEGS_ADDR - cache_addw( MOV_LO_HI(HOST_r2, FC_REGS_ADDR) ); // mov r2, FC_REGS_ADDR - cache_addw(0xb4fc); // push {r2,r3,v1-v4} +#if (__ARM_EABI__) + // 8-byte stack alignment + cache_addd(0xe92d4ff0); // stmfd sp!, {v1-v8,lr} +#else + cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr} +#endif - // adr: 16 - cache_addw( LDR_PC_IMM(HOST_r3, 64 - (16 + 4)) ); // ldr r3, [pc, #(&Segs)] - // adr: 18 - cache_addw( LDR_PC_IMM(HOST_r2, 68 - (18 + 2)) ); // ldr r2, [pc, #(&cpu_regs)] - cache_addw( MOV_HI_LO(FC_SEGS_ADDR, HOST_r3) ); // mov FC_SEGS_ADDR, r3 - cache_addw( MOV_HI_LO(FC_REGS_ADDR, HOST_r2) ); // mov FC_REGS_ADDR, r2 + cache_addd( ARM_ADD_IMM(HOST_r0, HOST_r0, 1, 0) ); // add r0, r0, #1 - // align 4 - cache_addw( ADD_LO_PC_IMM(HOST_r3, 8) ); // add r3, pc, #8 - cache_addw( ADD_IMM8(HOST_r0, 1) ); // add r0, #1 - cache_addw( ADD_IMM8(HOST_r3, 1) ); // add r3, #1 - cache_addw(0xb408); // push {r3} - cache_addw( BX(HOST_r0) ); // bx r0 - cache_addw( NOP ); // nop + pos1 = cache.pos; + cache_addd( 0 ); + pos2 = cache.pos; + cache_addd( 0 ); + pos3 = cache.pos; + cache_addd( 0 ); - // align 4 - cache_addw(0xbcfc); // pop {r2,r3,v1-v4} - cache_addw( MOV_HI_LO(FC_SEGS_ADDR, HOST_r3) ); // mov FC_SEGS_ADDR, r3 - cache_addw( MOV_HI_LO(FC_REGS_ADDR, HOST_r2) ); // mov FC_REGS_ADDR, r2 + cache_addd( ARM_ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4 + cache_addd( ARM_STR_IMM_M_W(HOST_lr, HOST_sp, 4) ); // str lr, [sp, #-4]! + cache_addd( ARM_BX(HOST_r0) ); // bx r0 - cache_addw(0xbc08); // pop {r3} - cache_addw( BX(HOST_r3) ); // bx r3 +#if (__ARM_EABI__) + cache_addd(0xe8bd4ff0); // ldmfd sp!, {v1-v8,lr} +#else + cache_addd(0xe8bd4df0); // ldmfd sp!, {v1-v5,v7,v8,lr} +#endif + cache_addd( ARM_BX(HOST_lr) ); // bx lr - // fill up to 64 bytes - cache_addw( NOP ); // nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } - // adr: 64 + *(Bit32u*)pos1 = ARM_LDR_IMM(FC_SEGS_ADDR, HOST_pc, cache.pos - (pos1 + 8)); // ldr FC_SEGS_ADDR, [pc, #(&Segs)] cache_addd((Bit32u)&Segs); // address of "Segs" - // adr: 68 + + *(Bit32u*)pos2 = ARM_LDR_IMM(FC_REGS_ADDR, HOST_pc, cache.pos - (pos2 + 8)); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)] cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs" + + *(Bit32u*)pos3 = ARM_LDR_IMM(readdata_addr, HOST_pc, cache.pos - (pos3 + 8)); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)] + cache_addd((Bit32u)&core_dynrec.readdata); // address of "core_dynrec.readdata" + + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } } // return from a function diff --git a/src/cpu/core_dynrec/risc_armv4le-thumb.h b/src/cpu/core_dynrec/risc_armv4le-thumb.h index ae8b732..8db5f1e 100644 --- a/src/cpu/core_dynrec/risc_armv4le-thumb.h +++ b/src/cpu/core_dynrec/risc_armv4le-thumb.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -50,15 +50,14 @@ // temporary register for LEA #define TEMP_REG_DRC HOST_a4 -#ifdef DRC_USE_REGS_ADDR // used to hold the address of "cpu_regs" - preferably filled in function gen_run_code #define FC_REGS_ADDR HOST_v7 -#endif -#ifdef DRC_USE_SEGS_ADDR // used to hold the address of "Segs" - preferably filled in function gen_run_code #define FC_SEGS_ADDR HOST_v8 -#endif + +// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code +#define readdata_addr HOST_v5 // instruction encodings @@ -98,10 +97,14 @@ // logical // and dst, src #define AND(dst, src) (0x4000 + (dst) + ((src) << 3) ) +// bic dst, src +#define BIC(dst, src) (0x4380 + (dst) + ((src) << 3) ) // eor dst, src #define EOR(dst, src) (0x4040 + (dst) + ((src) << 3) ) // orr dst, src #define ORR(dst, src) (0x4300 + (dst) + ((src) << 3) ) +// mvn dst, src +#define MVN(dst, src) (0x43c0 + (dst) + ((src) << 3) ) // shift/rotate // lsl dst, src, #imm @@ -128,6 +131,8 @@ #define LDRB_IMM(reg, addr, imm) (0x7800 + (reg) + ((addr) << 3) + ((imm) << 6) ) // ldr reg, [pc, #imm] @ 0 <= imm < 1024 & imm mod 4 = 0 #define LDR_PC_IMM(reg, imm) (0x4800 + ((reg) << 8) + ((imm) >> 2) ) +// ldr reg, [addr1, addr2] +#define LDR_REG(reg, addr1, addr2) (0x5800 + (reg) + ((addr1) << 3) + ((addr2) << 6) ) // store // str reg, [addr, #imm] @ 0 <= imm < 128 & imm mod 4 = 0 @@ -150,30 +155,69 @@ #define BX(reg) (0x4700 + ((reg) << 3) ) +// arm instructions + +// arithmetic +// add dst, src, #(imm ror rimm) @ 0 <= imm <= 255 & rimm mod 2 = 0 +#define ARM_ADD_IMM(dst, src, imm, rimm) (0xe2800000 + ((dst) << 12) + ((src) << 16) + (imm) + ((rimm) << 7) ) + +// load +// ldr reg, [addr, #imm] @ 0 <= imm < 4096 +#define ARM_LDR_IMM(reg, addr, imm) (0xe5900000 + ((reg) << 12) + ((addr) << 16) + (imm) ) + +// store +// str reg, [addr, #-(imm)]! @ 0 <= imm < 4096 +#define ARM_STR_IMM_M_W(reg, addr, imm) (0xe5200000 + ((reg) << 12) + ((addr) << 16) + (imm) ) + +// branch +// bx reg +#define ARM_BX(reg) (0xe12fff10 + (reg) ) + + // move a full register from reg_src to reg_dst static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { if(reg_src == reg_dst) return; cache_addw( MOV_REG(reg_dst, reg_src) ); // mov reg_dst, reg_src } +// helper function +static bool val_single_shift(Bit32u value, Bit32u *val_shift) { + Bit32u shift; + + if (GCC_UNLIKELY(value == 0)) { + *val_shift = 0; + return true; + } + + shift = 0; + while ((value & 1) == 0) { + value>>=1; + shift+=1; + } + + if ((value >> 8) != 0) return false; + + *val_shift = shift; + return true; +} + // move a 32bit constant value into dest_reg static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { - if ((imm & 0xffffff00) == 0) { - cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #(imm) - } else if ((imm & 0xffff00ff) == 0) { - cache_addw( MOV_IMM(dest_reg, imm >> 8) ); // mov dest_reg, #(imm >> 8) - cache_addw( LSL_IMM(dest_reg, dest_reg, 8) ); // lsl dest_reg, dest_reg, #8 - } else if ((imm & 0xff00ffff) == 0) { - cache_addw( MOV_IMM(dest_reg, imm >> 16) ); // mov dest_reg, #(imm >> 16) - cache_addw( LSL_IMM(dest_reg, dest_reg, 16) ); // lsl dest_reg, dest_reg, #16 - } else if ((imm & 0x00ffffff) == 0) { - cache_addw( MOV_IMM(dest_reg, imm >> 24) ); // mov dest_reg, #(imm >> 24) - cache_addw( LSL_IMM(dest_reg, dest_reg, 24) ); // lsl dest_reg, dest_reg, #24 + Bit32u scale; + + if (imm < 256) { + cache_addw( MOV_IMM(dest_reg, imm) ); // mov dest_reg, #imm + } else if ((~imm) < 256) { + cache_addw( MOV_IMM(dest_reg, ~imm) ); // mov dest_reg, #(~imm) + cache_addw( MVN(dest_reg, dest_reg) ); // mvn dest_reg, dest_reg + } else if (val_single_shift(imm, &scale)) { + cache_addw( MOV_IMM(dest_reg, imm >> scale) ); // mov dest_reg, #(imm >> scale) + cache_addw( LSL_IMM(dest_reg, dest_reg, scale) ); // lsl dest_reg, dest_reg, #scale } else { Bit32u diff; - + diff = imm - ((Bit32u)cache.pos+4); - + if ((diff < 1024) && ((imm & 0x03) == 0)) { if (((Bit32u)cache.pos & 0x03) == 0) { cache_addw( ADD_LO_PC_IMM(dest_reg, diff) ); // add dest_reg, pc, #(diff >> 2) @@ -198,10 +242,58 @@ static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { } } +// helper function +static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) { + switch (size) { + case 4: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 3) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) { + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDR_IMM(dest_reg, templo2, data - addr_data) ); // ldr dest_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 2: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 1) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) { + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDRH_IMM(dest_reg, templo2, data - addr_data) ); // ldrh dest_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 32)) { + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( LDRB_IMM(dest_reg, templo2, data - addr_data) ); // ldrb dest_reg, [templo2, #(data - addr_data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) { + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit32u)data, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true; + return false; +} + // helper function for gen_mov_word_to_reg static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) { // alignment.... if (dword) { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)data & 3) { if ( ((Bit32u)data & 3) == 2 ) { cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] @@ -218,16 +310,21 @@ static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,Ho cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24 cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1 } - } else { + } else +#endif + { cache_addw( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg] } } else { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)data & 1) { cache_addw( LDRB_IMM(dest_reg, data_reg, 0) ); // ldrb dest_reg, [data_reg] cache_addw( LDRB_IMM(templo1, data_reg, 1) ); // ldrb templo1, [data_reg, #1] cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8 cache_addw( ORR(dest_reg, templo1) ); // orr dest_reg, templo1 - } else { + } else +#endif + { cache_addw( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] } } @@ -236,8 +333,10 @@ static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,Ho // move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg // 16bit moves may destroy the upper 16bit of the destination register static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { - gen_mov_dword_to_reg_imm(templo2, (Bit32u)data); - gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2); + if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)data); + gen_mov_word_to_reg_helper(dest_reg, data, dword, templo2); + } } // move a 16bit constant value into dest_reg @@ -246,10 +345,58 @@ static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm); } +// helper function +static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit32u data, Bitu size, HostReg addr_reg, Bit32u addr_data) { + switch (size) { + case 4: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 3) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 128) && (((data - addr_data) & 3) == 0)) { + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STR_IMM(src_reg, templo2, data - addr_data) ); // str src_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 2: +#if !defined(C_UNALIGNED_MEMORY) + if ((data & 1) == 0) +#endif + { + if ((data >= addr_data) && (data < addr_data + 64) && (((data - addr_data) & 1) == 0)) { + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STRH_IMM(src_reg, templo2, data - addr_data) ); // strh src_reg, [templo2, #(data - addr_data)] + return true; + } + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 32)) { + cache_addw( MOV_LO_HI(templo2, addr_reg) ); // mov templo2, addr_reg + cache_addw( STRB_IMM(src_reg, templo2, data - addr_data) ); // strb src_reg, [templo2, #(data - addr_data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) { + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_REGS_ADDR, (Bit32u)&cpu_regs)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, readdata_addr, (Bit32u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit32u)dest, size, FC_SEGS_ADDR, (Bit32u)&Segs)) return true; + return false; +} + // helper function for gen_mov_word_from_reg static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) { // alignment.... if (dword) { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)dest & 3) { if ( ((Bit32u)dest & 3) == 2 ) { cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] @@ -268,16 +415,21 @@ static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, cache_addw( LSR_IMM(templo1, templo1, 24) ); // lsr templo1, templo1, #24 cache_addw( STRB_IMM(templo1, data_reg, 3) ); // strb templo1, [data_reg, #3] } - } else { + } else +#endif + { cache_addw( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg] } } else { +#if !defined(C_UNALIGNED_MEMORY) if ((Bit32u)dest & 1) { cache_addw( STRB_IMM(src_reg, data_reg, 0) ); // strb src_reg, [data_reg] cache_addw( MOV_REG(templo1, src_reg) ); // mov templo1, src_reg cache_addw( LSR_IMM(templo1, templo1, 8) ); // lsr templo1, templo1, #8 cache_addw( STRB_IMM(templo1, data_reg, 1) ); // strb templo1, [data_reg, #1] - } else { + } else +#endif + { cache_addw( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] } } @@ -285,8 +437,10 @@ static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, // move 32bit (dword==true) or 16bit (dword==false) of a register into memory static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2); + if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_from_reg_helper(src_reg, dest, dword, templo2); + } } // move an 8bit value from memory into dest_reg @@ -294,8 +448,10 @@ static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { // this function does not use FC_OP1/FC_OP2 as dest_reg as these // registers might not be directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { - gen_mov_dword_to_reg_imm(templo1, (Bit32u)data); - cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1] + if (!gen_mov_memval_to_reg(dest_reg, data, 1)) { + gen_mov_dword_to_reg_imm(templo1, (Bit32u)data); + cache_addw( LDRB_IMM(dest_reg, templo1, 0) ); // ldrb dest_reg, [templo1] + } } // move an 8bit value from memory into dest_reg @@ -324,8 +480,10 @@ static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u // move the lowest 8bit of a register into memory static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { - gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest); - cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1] + if (!gen_mov_memval_from_reg(src_reg, dest, 1)) { + gen_mov_dword_to_reg_imm(templo1, (Bit32u)dest); + cache_addw( STRB_IMM(src_reg, templo1, 0) ); // strb src_reg, [templo1] + } } @@ -362,16 +520,51 @@ static void gen_add(HostReg reg,void* op) { // add a 32bit constant value to a full register static void gen_add_imm(HostReg reg,Bit32u imm) { + Bit32u imm2, scale; + if(!imm) return; - gen_mov_dword_to_reg_imm(templo1, imm); - cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1 + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if (imm <= 255) { + cache_addw( ADD_IMM8(reg, imm) ); // add reg, #imm + } else if (imm2 <= 255) { + cache_addw( SUB_IMM8(reg, imm2) ); // sub reg, #(-imm) + } else { + if (val_single_shift(imm2, &scale)) { + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( SUB_REG(reg, reg, templo1) ); // sub reg, reg, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_addw( ADD_REG(reg, reg, templo1) ); // add reg, reg, templo1 + } + } } // and a 32bit constant value with a full register static void gen_and_imm(HostReg reg,Bit32u imm) { - if(imm == 0xffffffff) return; - gen_mov_dword_to_reg_imm(templo1, imm); - cache_addw( AND(reg, templo1) ); // and reg, templo1 + Bit32u imm2, scale; + + imm2 = ~imm; + if(!imm2) return; + + if (!imm) { + cache_addw( MOV_IMM(reg, 0) ); // mov reg, #0 + } else { + if (val_single_shift(imm2, &scale)) { + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( BIC(reg, templo1) ); // bic reg, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_addw( AND(reg, templo1) ); // and reg, templo1 + } + } } @@ -386,66 +579,65 @@ static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { gen_mov_direct_dword(dest,(Bit32u)imm); } -// add an 8bit constant value to a dword memory value -static void gen_add_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, 1, templo2); - if (imm >= 0) { - cache_addw( ADD_IMM8(templo3, (Bit32s)imm) ); // add templo3, #(imm) - } else { - cache_addw( SUB_IMM8(templo3, -((Bit32s)imm)) ); // sub templo3, #(-imm) - } - gen_mov_word_from_reg_helper(templo3, dest, 1, templo2); -} - // add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if (!dword) imm &= 0xffff; if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_add_direct_byte(dest,(Bit8s)imm); - return; + + if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); } - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); - if (dword) { - gen_mov_dword_to_reg_imm(templo1, imm); - } else { - gen_mov_word_to_reg_imm(templo1, (Bit16u)imm); + gen_add_imm(templo3, imm); + if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); } - cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1 - gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); } -// subtract an 8bit constant value from a dword memory value -static void gen_sub_direct_byte(void* dest,Bit8s imm) { - if(!imm) return; - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, 1, templo2); - if (imm >= 0) { - cache_addw( SUB_IMM8(templo3, (Bit32s)imm) ); // sub templo3, #(imm) - } else { - cache_addw( ADD_IMM8(templo3, -((Bit32s)imm)) ); // add templo3, #(-imm) - } - gen_mov_word_from_reg_helper(templo3, dest, 1, templo2); +// add an 8bit constant value to a dword memory value +static void gen_add_direct_byte(void* dest,Bit8s imm) { + gen_add_direct_word(dest, (Bit32s)imm, 1); } // subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + Bit32u imm2, scale; + + if (!dword) imm &= 0xffff; if(!imm) return; - if (dword && ( (imm<128) || (imm>=0xffffff80) ) ) { - gen_sub_direct_byte(dest,(Bit8s)imm); - return; + + if (!gen_mov_memval_to_reg(templo3, dest, (dword)?4:2)) { + gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); + gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); } - gen_mov_dword_to_reg_imm(templo2, (Bit32u)dest); - gen_mov_word_to_reg_helper(templo3, dest, dword, templo2); - if (dword) { - gen_mov_dword_to_reg_imm(templo1, imm); + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if (imm <= 255) { + cache_addw( SUB_IMM8(templo3, imm) ); // sub templo3, #imm + } else if (imm2 <= 255) { + cache_addw( ADD_IMM8(templo3, imm2) ); // add templo3, #(-imm) } else { - gen_mov_word_to_reg_imm(templo1, (Bit16u)imm); + if (val_single_shift(imm2, &scale)) { + cache_addw( MOV_IMM(templo1, imm2 >> scale) ); // mov templo1, #(~imm >> scale) + if (scale) { + cache_addw( LSL_IMM(templo1, templo1, scale) ); // lsl templo1, templo1, #scale + } + cache_addw( ADD_REG(templo3, templo3, templo1) ); // add templo3, templo3, templo1 + } else { + gen_mov_dword_to_reg_imm(templo1, imm); + cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1 + } } - cache_addw( SUB_REG(templo3, templo3, templo1) ); // sub templo3, templo3, templo1 - gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); + + if (!gen_mov_memval_from_reg(templo3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(templo3, dest, dword, templo2); + } +} + +// subtract an 8bit constant value from a dword memory value +static void gen_sub_direct_byte(void* dest,Bit8s imm) { + gen_sub_direct_word(dest, (Bit32s)imm, 1); } // effective address calculation, destination is dest_reg @@ -491,7 +683,7 @@ static void INLINE gen_call_function_raw(void * func) { // switch from arm to thumb state cache_addd(0xe2800000 + (templo1 << 12) + (HOST_pc << 16) + (1)); // add templo1, pc, #1 cache_addd(0xe12fff10 + (templo1)); // bx templo1 - + // thumb state from now on } @@ -537,18 +729,20 @@ static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { static void gen_jmp_ptr(void * ptr,Bits imm=0) { gen_mov_word_to_reg(templo3, ptr, 1); - if (imm) { - gen_mov_dword_to_reg_imm(templo2, imm); - cache_addw( ADD_REG(templo3, templo3, templo2) ); // add templo3, templo3, templo2 - } - -#if (1) -// (*ptr) should be word aligned +#if !defined(C_UNALIGNED_MEMORY) +// (*ptr) should be word aligned if ((imm & 0x03) == 0) { - cache_addw( LDR_IMM(templo2, templo3, 0) ); // ldr templo2, [templo3] - } else #endif - { + if ((imm >= 0) && (imm < 128) && ((imm & 3) == 0)) { + cache_addw( LDR_IMM(templo2, templo3, imm) ); // ldr templo2, [templo3, #imm] + } else { + gen_mov_dword_to_reg_imm(templo2, imm); + cache_addw( LDR_REG(templo2, templo3, templo2) ); // ldr templo2, [templo3, templo2] + } +#if !defined(C_UNALIGNED_MEMORY) + } else { + gen_add_imm(templo3, imm); + cache_addw( LDRB_IMM(templo2, templo3, 0) ); // ldrb templo2, [templo3] cache_addw( LDRB_IMM(templo1, templo3, 1) ); // ldrb templo1, [templo3, #1] cache_addw( LSL_IMM(templo1, templo1, 8) ); // lsl templo1, templo1, #8 @@ -560,6 +754,7 @@ static void gen_jmp_ptr(void * ptr,Bits imm=0) { cache_addw( LSL_IMM(templo1, templo1, 24) ); // lsl templo1, templo1, #24 cache_addw( ORR(templo2, templo1) ); // orr templo2, templo1 } +#endif // increase jmp address to keep thumb state cache_addw( ADD_IMM3(templo2, templo2, 1) ); // add templo2, templo2, #1 @@ -650,50 +845,53 @@ static void INLINE gen_fill_branch_long(Bit32u data) { } static void gen_run_code(void) { - // switch from arm to thumb state - cache_addd(0xe2800000 + (HOST_r3 << 12) + (HOST_pc << 16) + (1)); // add r3, pc, #1 - cache_addd(0xe12fff10 + (HOST_r3)); // bx r3 + Bit8u *pos1, *pos2, *pos3; - // thumb state from now on - cache_addw(0xb500); // push {lr} - cache_addw( MOV_LO_HI(HOST_r3, FC_SEGS_ADDR) ); // mov r3, FC_SEGS_ADDR - cache_addw( MOV_LO_HI(HOST_r2, FC_REGS_ADDR) ); // mov r2, FC_REGS_ADDR - cache_addw(0xb4fc); // push {r2,r3,v1-v4} +#if (__ARM_EABI__) + // 8-byte stack alignment + cache_addd(0xe92d4ff0); // stmfd sp!, {v1-v8,lr} +#else + cache_addd(0xe92d4df0); // stmfd sp!, {v1-v5,v7,v8,lr} +#endif - // adr: 16 - cache_addw( LDR_PC_IMM(HOST_r3, 64 - (16 + 4)) ); // ldr r3, [pc, #(&Segs)] - // adr: 18 - cache_addw( LDR_PC_IMM(HOST_r2, 68 - (18 + 2)) ); // ldr r2, [pc, #(&cpu_regs)] - cache_addw( MOV_HI_LO(FC_SEGS_ADDR, HOST_r3) ); // mov FC_SEGS_ADDR, r3 - cache_addw( MOV_HI_LO(FC_REGS_ADDR, HOST_r2) ); // mov FC_REGS_ADDR, r2 + cache_addd( ARM_ADD_IMM(HOST_r0, HOST_r0, 1, 0) ); // add r0, r0, #1 - // align 4 - cache_addw( ADD_LO_PC_IMM(HOST_r3, 8) ); // add r3, pc, #8 - cache_addw( ADD_IMM8(HOST_r0, 1) ); // add r0, #1 - cache_addw( ADD_IMM8(HOST_r3, 1) ); // add r3, #1 - cache_addw(0xb408); // push {r3} - cache_addw( BX(HOST_r0) ); // bx r0 - cache_addw( NOP ); // nop + pos1 = cache.pos; + cache_addd( 0 ); + pos2 = cache.pos; + cache_addd( 0 ); + pos3 = cache.pos; + cache_addd( 0 ); - // align 4 - cache_addw(0xbcfc); // pop {r2,r3,v1-v4} - cache_addw( MOV_HI_LO(FC_SEGS_ADDR, HOST_r3) ); // mov FC_SEGS_ADDR, r3 - cache_addw( MOV_HI_LO(FC_REGS_ADDR, HOST_r2) ); // mov FC_REGS_ADDR, r2 + cache_addd( ARM_ADD_IMM(HOST_lr, HOST_pc, 4, 0) ); // add lr, pc, #4 + cache_addd( ARM_STR_IMM_M_W(HOST_lr, HOST_sp, 4) ); // str lr, [sp, #-4]! + cache_addd( ARM_BX(HOST_r0) ); // bx r0 - cache_addw(0xbc08); // pop {r3} - cache_addw( BX(HOST_r3) ); // bx r3 +#if (__ARM_EABI__) + cache_addd(0xe8bd4ff0); // ldmfd sp!, {v1-v8,lr} +#else + cache_addd(0xe8bd4df0); // ldmfd sp!, {v1-v5,v7,v8,lr} +#endif + cache_addd( ARM_BX(HOST_lr) ); // bx lr - // fill up to 64 bytes - cache_addw( NOP ); // nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop - cache_addd( NOP | (NOP << 16) ); // nop, nop + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } - // adr: 64 + *(Bit32u*)pos1 = ARM_LDR_IMM(FC_SEGS_ADDR, HOST_pc, cache.pos - (pos1 + 8)); // ldr FC_SEGS_ADDR, [pc, #(&Segs)] cache_addd((Bit32u)&Segs); // address of "Segs" - // adr: 68 + + *(Bit32u*)pos2 = ARM_LDR_IMM(FC_REGS_ADDR, HOST_pc, cache.pos - (pos2 + 8)); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)] cache_addd((Bit32u)&cpu_regs); // address of "cpu_regs" + + *(Bit32u*)pos3 = ARM_LDR_IMM(readdata_addr, HOST_pc, cache.pos - (pos3 + 8)); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)] + cache_addd((Bit32u)&core_dynrec.readdata); // address of "core_dynrec.readdata" + + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } } // return from a function @@ -1023,7 +1221,11 @@ static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) { } #endif -static void cache_block_before_close(void) { } +static void cache_block_before_close(void) { + if ((((Bit32u)cache.pos) & 3) != 0) { + cache_addw( NOP ); // nop + } +} #ifdef DRC_USE_SEGS_ADDR diff --git a/src/cpu/core_dynrec/risc_armv4le.h b/src/cpu/core_dynrec/risc_armv4le.h index 8723852..80f1060 100644 --- a/src/cpu/core_dynrec/risc_armv4le.h +++ b/src/cpu/core_dynrec/risc_armv4le.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,20 +11,26 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* ARMv4 (little endian) backend (switcher) by M-HT */ +/* ARMv4/ARMv7 (little endian) backend (switcher) by M-HT */ #include "risc_armv4le-common.h" // choose your destiny: -#include "risc_armv4le-thumb-niw.h" -//#include "risc_armv4le-thumb-iw.h" -//#include "risc_armv4le-thumb.h" -//#include "risc_armv4le-s3.h" -//#include "risc_armv4le-o3.h" +#if C_TARGETCPU == ARMV7LE + #include "risc_armv4le-o3.h" +#else + #if defined(__THUMB_INTERWORK__) + #include "risc_armv4le-thumb-iw.h" + #else + #include "risc_armv4le-o3.h" +// #include "risc_armv4le-thumb-niw.h" +// #include "risc_armv4le-thumb.h" + #endif +#endif diff --git a/src/cpu/core_dynrec/risc_armv8le.h b/src/cpu/core_dynrec/risc_armv8le.h new file mode 100644 index 0000000..3e455ad --- /dev/null +++ b/src/cpu/core_dynrec/risc_armv8le.h @@ -0,0 +1,1238 @@ +/* + * Copyright (C) 2002-2019 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + + +/* ARMv8 (little endian, 64-bit) backend by M-HT */ + + +// some configuring defines that specify the capabilities of this architecture +// or aspects of the recompiling + +// protect FC_ADDR over function calls if necessaray +// #define DRC_PROTECT_ADDR_REG + +// try to use non-flags generating functions if possible +#define DRC_FLAGS_INVALIDATION +// try to replace _simple functions by code +#define DRC_FLAGS_INVALIDATION_DCODE + +// type with the same size as a pointer +#define DRC_PTR_SIZE_IM Bit64u + +// calling convention modifier +#define DRC_CALL_CONV /* nothing */ +#define DRC_FC /* nothing */ + +// use FC_REGS_ADDR to hold the address of "cpu_regs" and to access it using FC_REGS_ADDR +#define DRC_USE_REGS_ADDR +// use FC_SEGS_ADDR to hold the address of "Segs" and to access it using FC_SEGS_ADDR +#define DRC_USE_SEGS_ADDR + +// register mapping +typedef Bit8u HostReg; + +// registers +#define HOST_r0 0 +#define HOST_r1 1 +#define HOST_r2 2 +#define HOST_r3 3 +#define HOST_r4 4 +#define HOST_r5 5 +#define HOST_r6 6 +#define HOST_r7 7 +#define HOST_r8 8 +#define HOST_r9 9 +#define HOST_r10 10 +#define HOST_r11 11 +#define HOST_r12 12 +#define HOST_r13 13 +#define HOST_r14 14 +#define HOST_r15 15 +#define HOST_r16 16 +#define HOST_r17 17 +#define HOST_r18 18 +#define HOST_r19 19 +#define HOST_r20 20 +#define HOST_r21 21 +#define HOST_r22 22 +#define HOST_r23 23 +#define HOST_r24 24 +#define HOST_r25 25 +#define HOST_r26 26 +#define HOST_r27 27 +#define HOST_r28 28 +#define HOST_r29 29 +#define HOST_r30 30 +// special registers +#define HOST_sp 31 +#define HOST_zr 31 + +// register aliases +// 32-bit registers +#define HOST_w0 HOST_r0 +#define HOST_w1 HOST_r1 +#define HOST_w2 HOST_r2 +#define HOST_w3 HOST_r3 +#define HOST_w4 HOST_r4 +#define HOST_w5 HOST_r5 +#define HOST_w6 HOST_r6 +#define HOST_w7 HOST_r7 +#define HOST_w8 HOST_r8 +#define HOST_w9 HOST_r9 +#define HOST_w10 HOST_r10 +#define HOST_w11 HOST_r11 +#define HOST_w12 HOST_r12 +#define HOST_w13 HOST_r13 +#define HOST_w14 HOST_r14 +#define HOST_w15 HOST_r15 +#define HOST_w16 HOST_r16 +#define HOST_w17 HOST_r17 +#define HOST_w18 HOST_r18 +#define HOST_w19 HOST_r19 +#define HOST_w20 HOST_r20 +#define HOST_w21 HOST_r21 +#define HOST_w22 HOST_r22 +#define HOST_w23 HOST_r23 +#define HOST_w24 HOST_r24 +#define HOST_w25 HOST_r25 +#define HOST_w26 HOST_r26 +#define HOST_w27 HOST_r27 +#define HOST_w28 HOST_r28 +#define HOST_w29 HOST_r29 +#define HOST_w30 HOST_r30 +#define HOST_wsp HOST_sp +#define HOST_wzr HOST_zr +// 64-bit registers +#define HOST_x0 HOST_r0 +#define HOST_x1 HOST_r1 +#define HOST_x2 HOST_r2 +#define HOST_x3 HOST_r3 +#define HOST_x4 HOST_r4 +#define HOST_x5 HOST_r5 +#define HOST_x6 HOST_r6 +#define HOST_x7 HOST_r7 +#define HOST_x8 HOST_r8 +#define HOST_x9 HOST_r9 +#define HOST_x10 HOST_r10 +#define HOST_x11 HOST_r11 +#define HOST_x12 HOST_r12 +#define HOST_x13 HOST_r13 +#define HOST_x14 HOST_r14 +#define HOST_x15 HOST_r15 +#define HOST_x16 HOST_r16 +#define HOST_x17 HOST_r17 +#define HOST_x18 HOST_r18 +#define HOST_x19 HOST_r19 +#define HOST_x20 HOST_r20 +#define HOST_x21 HOST_r21 +#define HOST_x22 HOST_r22 +#define HOST_x23 HOST_r23 +#define HOST_x24 HOST_r24 +#define HOST_x25 HOST_r25 +#define HOST_x26 HOST_r26 +#define HOST_x27 HOST_r27 +#define HOST_x28 HOST_r28 +#define HOST_x29 HOST_r29 +#define HOST_x30 HOST_r30 +#define HOST_xzr HOST_zr +#define HOST_ip0 HOST_r16 +#define HOST_ip1 HOST_r17 +#define HOST_fp HOST_r29 +#define HOST_lr HOST_r30 + + +// temporary registers +#define temp1 HOST_r10 +#define temp2 HOST_r11 +#define temp3 HOST_r12 + +// register that holds function return values +#define FC_RETOP HOST_r0 + +// register used for address calculations, +#define FC_ADDR HOST_r19 // has to be saved across calls, see DRC_PROTECT_ADDR_REG + +// register that holds the first parameter +#define FC_OP1 HOST_r0 + +// register that holds the second parameter +#define FC_OP2 HOST_r1 + +// special register that holds the third parameter for _R3 calls (byte accessible) +#define FC_OP3 HOST_r2 + +// register that holds byte-accessible temporary values +#define FC_TMP_BA1 HOST_r0 + +// register that holds byte-accessible temporary values +#define FC_TMP_BA2 HOST_r1 + +// temporary register for LEA +#define TEMP_REG_DRC HOST_r9 + +// used to hold the address of "cpu_regs" - preferably filled in function gen_run_code +#define FC_REGS_ADDR HOST_r20 + +// used to hold the address of "Segs" - preferably filled in function gen_run_code +#define FC_SEGS_ADDR HOST_r21 + +// used to hold the address of "core_dynrec.readdata" - filled in function gen_run_code +#define readdata_addr HOST_r22 + + +// instruction encodings + +// move +// mov dst, src, lsl #imm +#define MOV_REG_LSL_IMM(dst, src, imm) ORR_REG_LSL_IMM(dst, HOST_wzr, src, imm) +// movz dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16 +#define MOVZ(dst, imm, simm) (0x52800000 + (dst) + ((imm) << 5) + ((simm)?0x00200000:0) ) +// movn dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16 +#define MOVN(dst, imm, simm) (0x12800000 + (dst) + ((imm) << 5) + ((simm)?0x00200000:0) ) +// movk dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16 +#define MOVK(dst, imm, simm) (0x72800000 + (dst) + ((imm) << 5) + ((simm)?0x00200000:0) ) +// movz dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16/32/48 +#define MOVZ64(dst, imm, simm) (0xd2800000 + (dst) + ((imm) << 5) + (((simm) >> 4) << 21) ) +// movk dst, #(imm lsl simm) @ 0 <= imm <= 65535 & simm = 0/16/32/48 +#define MOVK64(dst, imm, simm) (0xf2800000 + (dst) + ((imm) << 5) + (((simm) >> 4) << 21) ) +// lslv dst, src, rreg +#define LSLV(dst, src, rreg) (0x1ac02000 + (dst) + ((src) << 5) + ((rreg) << 16) ) +// lsrv dst, src, rreg +#define LSRV(dst, src, rreg) (0x1ac02400 + (dst) + ((src) << 5) + ((rreg) << 16) ) +// asrv dst, src, rreg +#define ASRV(dst, src, rreg) (0x1ac02800 + (dst) + ((src) << 5) + ((rreg) << 16) ) +// rorv dst, src, rreg +#define RORV(dst, src, rreg) (0x1ac02c00 + (dst) + ((src) << 5) + ((rreg) << 16) ) +// lslv dst, src, rreg +#define LSLV64(dst, src, rreg) (0x9ac02000 + (dst) + ((src) << 5) + ((rreg) << 16) ) +// lsrv dst, src, rreg +#define LSRV64(dst, src, rreg) (0x9ac02400 + (dst) + ((src) << 5) + ((rreg) << 16) ) +// lsr dst, src, #imm +#define LSR64_IMM(dst, src, imm) UBFM64(dst, src, imm, 63) + +// arithmetic +// add dst, src, #(imm lsl simm) @ 0 <= imm <= 4095 & simm = 0/12 +#define ADD_IMM(dst, src, imm, simm) (0x11000000 + (dst) + ((src) << 5) + ((imm) << 10) + ((simm)?0x00400000:0) ) +// add dst, src1, src2, lsl #imm +#define ADD_REG_LSL_IMM(dst, src1, src2, imm) (0x0b000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) ) +// sub dst, src, #(imm lsl simm) @ 0 <= imm <= 4095 & simm = 0/12 +#define SUB_IMM(dst, src, imm, simm) (0x51000000 + (dst) + ((src) << 5) + ((imm) << 10) + ((simm)?0x00400000:0) ) +// sub dst, src1, src2, lsl #imm +#define SUB_REG_LSL_IMM(dst, src1, src2, imm) (0x4b000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) ) +// cmp src, #(imm lsl simm) @ 0 <= imm <= 4095 & simm = 0/12 +#define CMP_IMM(src, imm, simm) (0x7100001f + ((src) << 5) + ((imm) << 10) + ((simm)?0x00400000:0) ) +// nop +#define NOP (0xd503201f) + +// logical +// and dst, src1, src2, lsl #imm @ 0 <= imm <= 31 +#define AND_REG_LSL_IMM(dst, src1, src2, imm) (0x0a000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) ) +// orr dst, src1, src2, lsl #imm @ 0 <= imm <= 31 +#define ORR_REG_LSL_IMM(dst, src1, src2, imm) (0x2a000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) ) +// eor dst, src1, src2, lsl #imm @ 0 <= imm <= 31 +#define EOR_REG_LSL_IMM(dst, src1, src2, imm) (0x4a000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) ) +// bic dst, src1, src2, lsl #imm @ 0 <= imm <= 31 +#define BIC_REG_LSL_IMM(dst, src1, src2, imm) (0x0a200000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) ) +// and dst, src1, src2, lsl #imm @ 0 <= imm <= 63 +#define AND64_REG_LSL_IMM(dst, src1, src2, imm) (0x8a000000 + (dst) + ((src1) << 5) + ((src2) << 16) + ((imm) << 10) ) + +// load +// ldr reg, [pc, #imm] @ -1M <= imm < 1M & imm mod 4 = 0 +#define LDR64_PC(reg, imm) (0x58000000 + (reg) + (((imm) << 3) & 0x00ffffe0) ) +// ldp reg1, reg2 [addr, #imm] @ -512 <= imm < 512 & imm mod 8 = 0 +#define LDP64_IMM(reg1, reg2, addr, imm) (0xa9400000 + (reg1) + ((reg2) << 10) + ((addr) << 5) + ((imm) << 12) ) +// ldr reg, [addr, #imm] @ 0 <= imm < 32768 & imm mod 8 = 0 +#define LDR64_IMM(reg, addr, imm) (0xf9400000 + (reg) + ((addr) << 5) + ((imm) << 7) ) +// ldr reg, [addr, #imm] @ 0 <= imm < 16384 & imm mod 4 = 0 +#define LDR_IMM(reg, addr, imm) (0xb9400000 + (reg) + ((addr) << 5) + ((imm) << 8) ) +// ldrh reg, [addr, #imm] @ 0 <= imm < 8192 & imm mod 2 = 0 +#define LDRH_IMM(reg, addr, imm) (0x79400000 + (reg) + ((addr) << 5) + ((imm) << 9) ) +// ldrb reg, [addr, #imm] @ 0 <= imm < 4096 +#define LDRB_IMM(reg, addr, imm) (0x39400000 + (reg) + ((addr) << 5) + ((imm) << 10) ) +// ldr reg, [addr1, addr2, lsl #imm] @ imm = 0/2 +#define LDR64_REG_LSL_IMM(reg, addr1, addr2, imm) (0xf8606800 + (reg) + ((addr1) << 5) + ((addr2) << 16) + ((imm)?0x00001000:0) ) +// ldur reg, [addr, #imm] @ -256 <= imm < 256 +#define LDUR64_IMM(reg, addr, imm) (0xf8400000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) ) +// ldur reg, [addr, #imm] @ -256 <= imm < 256 +#define LDUR_IMM(reg, addr, imm) (0xb8400000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) ) +// ldurh reg, [addr, #imm] @ -256 <= imm < 256 +#define LDURH_IMM(reg, addr, imm) (0x78400000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) ) +// ldurb reg, [addr, #imm] @ -256 <= imm < 256 +#define LDURB_IMM(reg, addr, imm) (0x38400000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) ) + +// store +// stp reg1, reg2 [addr, #imm] @ -512 <= imm < 512 & imm mod 8 = 0 +#define STP64_IMM(reg1, reg2, addr, imm) (0xa9000000 + (reg1) + ((reg2) << 10) + ((addr) << 5) + ((imm) << 12) ) +// str reg, [addr, #imm] @ 0 <= imm < 32768 & imm mod 8 = 0 +#define STR64_IMM(reg, addr, imm) (0xf9000000 + (reg) + ((addr) << 5) + ((imm) << 7) ) +// str reg, [addr, #imm] @ 0 <= imm < 16384 & imm mod 4 = 0 +#define STR_IMM(reg, addr, imm) (0xb9000000 + (reg) + ((addr) << 5) + ((imm) << 8) ) +// strh reg, [addr, #imm] @ 0 <= imm < 8192 & imm mod 2 = 0 +#define STRH_IMM(reg, addr, imm) (0x79000000 + (reg) + ((addr) << 5) + ((imm) << 9) ) +// strb reg, [addr, #imm] @ 0 <= imm < 4096 +#define STRB_IMM(reg, addr, imm) (0x39000000 + (reg) + ((addr) << 5) + ((imm) << 10) ) +// stur reg, [addr, #imm] @ -256 <= imm < 256 +#define STUR64_IMM(reg, addr, imm) (0xf8000000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) ) +// stur reg, [addr, #imm] @ -256 <= imm < 256 +#define STUR_IMM(reg, addr, imm) (0xb8000000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) ) +// sturh reg, [addr, #imm] @ -256 <= imm < 256 +#define STURH_IMM(reg, addr, imm) (0x78000000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) ) +// sturb reg, [addr, #imm] @ -256 <= imm < 256 +#define STURB_IMM(reg, addr, imm) (0x38000000 + (reg) + ((addr) << 5) + (((imm) << 12) & 0x001ff000) ) + +// branch +// bgt pc+imm @ 0 <= imm < 1M & imm mod 4 = 0 +#define BGT_FWD(imm) (0x5400000c + ((imm) << 3) ) +// b pc+imm @ 0 <= imm < 128M & imm mod 4 = 0 +#define B_FWD(imm) (0x14000000 + ((imm) >> 2) ) +// br reg +#define BR(reg) (0xd61f0000 + ((reg) << 5) ) +// blr reg +#define BLR_REG(reg) (0xd63f0000 + ((reg) << 5) ) +// cbz reg, pc+imm @ 0 <= imm < 1M & imm mod 4 = 0 +#define CBZ_FWD(reg, imm) (0x34000000 + (reg) + ((imm) << 3) ) +// cbnz reg, pc+imm @ 0 <= imm < 1M & imm mod 4 = 0 +#define CBNZ_FWD(reg, imm) (0x35000000 + (reg) + ((imm) << 3) ) +// ret reg +#define RET_REG(reg) (0xd65f0000 + ((reg) << 5) ) +// ret +#define RET RET_REG(HOST_x30) + +// extend +// sxth dst, src +#define SXTH(dst, src) SBFM(dst, src, 0, 15) +// sxtb dst, src +#define SXTB(dst, src) SBFM(dst, src, 0, 7) +// uxth dst, src +#define UXTH(dst, src) UBFM(dst, src, 0, 15) +// uxtb dst, src +#define UXTB(dst, src) UBFM(dst, src, 0, 7) + +// bit field +// bfi dst, src, #lsb, #width @ lsb >= 0, width >= 1, lsb+width <= 32 +#define BFI(dst, src, lsb, width) BFM(dst, src, (32 - (lsb)) & 0x1f, (width) - 1) +// bfm dst, src, #rimm, #simm @ 0 <= rimm < 32, 0 <= simm < 32 +#define BFM(dst, src, rimm, simm) (0x33000000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) ) +// sbfm dst, src, #rimm, #simm @ 0 <= rimm < 32, 0 <= simm < 32 +#define SBFM(dst, src, rimm, simm) (0x13000000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) ) +// ubfm dst, src, #rimm, #simm @ 0 <= rimm < 32, 0 <= simm < 32 +#define UBFM(dst, src, rimm, simm) (0x53000000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) ) +// bfi dst, src, #lsb, #width @ lsb >= 0, width >= 1, lsb+width <= 64 +#define BFI64(dst, src, lsb, width) BFM64(dst, src, (64 - (lsb)) & 0x3f, (width) - 1) +// bfm dst, src, #rimm, #simm @ 0 <= rimm < 64, 0 <= simm < 64 +#define BFM64(dst, src, rimm, simm) (0xb3400000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) ) +// ubfm dst, src, #rimm, #simm @ 0 <= rimm < 64, 0 <= simm < 64 +#define UBFM64(dst, src, rimm, simm) (0xd3400000 + (dst) + ((src) << 5) + ((rimm) << 16) + ((simm) << 10) ) + + +// move a full register from reg_src to reg_dst +static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { + if(reg_src == reg_dst) return; + cache_addd( MOV_REG_LSL_IMM(reg_dst, reg_src, 0) ); // mov reg_dst, reg_src +} + +// move a 32bit constant value into dest_reg +static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { + if ( (imm & 0xffff0000) == 0 ) { + cache_addd( MOVZ(dest_reg, imm, 0) ); // movz dest_reg, #imm + } else if ( (imm & 0x0000ffff) == 0 ) { + cache_addd( MOVZ(dest_reg, imm >> 16, 16) ); // movz dest_reg, #(imm >> 16), lsl #16 + } else if ( ((~imm) & 0xffff0000) == 0 ) { + cache_addd( MOVN(dest_reg, ~imm, 0) ); // movn dest_reg, #(~imm) + } else if ( ((~imm) & 0x0000ffff) == 0 ) { + cache_addd( MOVN(dest_reg, (~imm) >> 16, 16) ); // movn dest_reg, #(~imm >> 16), lsl #16 + } else { + cache_addd( MOVZ(dest_reg, imm & 0xffff, 0) ); // movz dest_reg, #(imm & 0xffff) + cache_addd( MOVK(dest_reg, imm >> 16, 16) ); // movk dest_reg, #(imm >> 16), lsl #16 + } +} + +// helper function +static bool gen_mov_memval_to_reg_helper(HostReg dest_reg, Bit64u data, Bitu size, HostReg addr_reg, Bit64u addr_data) { + switch (size) { + case 8: + if (((data & 7) == 0) && (data >= addr_data) && (data < addr_data + 32768)) { + cache_addd( LDR64_IMM(dest_reg, addr_reg, data - addr_data) ); // ldr dest_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data + 256) && (data >= addr_data - 256)) { + cache_addd( LDUR64_IMM(dest_reg, addr_reg, data - addr_data) ); // ldur dest_reg, [addr_reg, #(data - addr_data)] + return true; + } + break; + case 4: + if (((data & 3) == 0) && (data >= addr_data) && (data < addr_data + 16384)) { + cache_addd( LDR_IMM(dest_reg, addr_reg, data - addr_data) ); // ldr dest_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data + 256) && (data >= addr_data - 256)) { + cache_addd( LDUR_IMM(dest_reg, addr_reg, data - addr_data) ); // ldur dest_reg, [addr_reg, #(data - addr_data)] + return true; + } + break; + case 2: + if (((data & 1) == 0) && (data >= addr_data) && (data < addr_data + 8192)) { + cache_addd( LDRH_IMM(dest_reg, addr_reg, data - addr_data) ); // ldrh dest_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data + 256) && (data >= addr_data - 256)) { + cache_addd( LDURH_IMM(dest_reg, addr_reg, data - addr_data) ); // ldurh dest_reg, [addr_reg, #(data - addr_data)] + return true; + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 4096)) { + cache_addd( LDRB_IMM(dest_reg, addr_reg, data - addr_data) ); // ldrb dest_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data) && (data >= addr_data - 256)) { + cache_addd( LDURB_IMM(dest_reg, addr_reg, data - addr_data) ); // ldurb dest_reg, [addr_reg, #(data - addr_data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_to_reg(HostReg dest_reg, void *data, Bitu size) { + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit64u)data, size, FC_REGS_ADDR, (Bit64u)&cpu_regs)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit64u)data, size, readdata_addr, (Bit64u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_to_reg_helper(dest_reg, (Bit64u)data, size, FC_SEGS_ADDR, (Bit64u)&Segs)) return true; + return false; +} + +// helper function - move a 64bit constant value into dest_reg +static void gen_mov_qword_to_reg_imm(HostReg dest_reg,Bit64u imm) { + bool isfirst = true; + + if ( (imm & 0xffff) != 0 ) { + cache_addd( MOVZ64(dest_reg, imm & 0xffff, 0) ); // movz dest_reg, #(imm & 0xffff) + isfirst = false; + } + if ( ((imm >> 16) & 0xffff) != 0 ) { + if (isfirst) { + isfirst = false; + cache_addd( MOVZ64(dest_reg, (imm >> 16) & 0xffff, 16) ); // movz dest_reg, #((imm >> 16) & 0xffff), lsl #16 + } else { + cache_addd( MOVK64(dest_reg, (imm >> 16) & 0xffff, 16) ); // movk dest_reg, #((imm >> 16) & 0xffff), lsl #16 + } + } + if ( ((imm >> 32) & 0xffff) != 0 ) { + if (isfirst) { + isfirst = false; + cache_addd( MOVZ64(dest_reg, (imm >> 32) & 0xffff, 32) ); // movz dest_reg, #((imm >> 32) & 0xffff), lsl #32 + } else { + cache_addd( MOVK64(dest_reg, (imm >> 32) & 0xffff, 32) ); // movk dest_reg, #((imm >> 32) & 0xffff), lsl #32 + } + } + if ( ((imm >> 48) & 0xffff) != 0 ) { + if (isfirst) { + isfirst = false; + cache_addd( MOVZ64(dest_reg, (imm >> 48) & 0xffff, 48) ); // movz dest_reg, #((imm >> 48) & 0xffff), lsl #48 + } else { + cache_addd( MOVK64(dest_reg, (imm >> 48) & 0xffff, 48) ); // movk dest_reg, #((imm >> 48) & 0xffff), lsl #48 + } + } + if (isfirst) { + cache_addd( MOVZ64(dest_reg, 0, 0) ); // movz dest_reg, #0 + } +} + +// helper function for gen_mov_word_to_reg +static void gen_mov_word_to_reg_helper(HostReg dest_reg,void* data,bool dword,HostReg data_reg) { + if (dword) { + cache_addd( LDR_IMM(dest_reg, data_reg, 0) ); // ldr dest_reg, [data_reg] + } else { + cache_addd( LDRH_IMM(dest_reg, data_reg, 0) ); // ldrh dest_reg, [data_reg] + } +} + +// move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg +// 16bit moves may destroy the upper 16bit of the destination register +static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { + if (!gen_mov_memval_to_reg(dest_reg, data, (dword)?4:2)) { + gen_mov_qword_to_reg_imm(temp1, (Bit64u)data); + gen_mov_word_to_reg_helper(dest_reg, data, dword, temp1); + } +} + +// move a 16bit constant value into dest_reg +// the upper 16bit of the destination register may be destroyed +static void INLINE gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { + cache_addd( MOVZ(dest_reg, imm, 0) ); // movz dest_reg, #imm +} + +// helper function +static bool gen_mov_memval_from_reg_helper(HostReg src_reg, Bit64u data, Bitu size, HostReg addr_reg, Bit64u addr_data) { + switch (size) { + case 8: + if (((data & 7) == 0) && (data >= addr_data) && (data < addr_data + 32768)) { + cache_addd( STR64_IMM(src_reg, addr_reg, data - addr_data) ); // str src_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data + 256) && (data >= addr_data - 256)) { + cache_addd( STUR64_IMM(src_reg, addr_reg, data - addr_data) ); // stur src_reg, [addr_reg, #(data - addr_data)] + return true; + } + break; + case 4: + if (((data & 3) == 0) && (data >= addr_data) && (data < addr_data + 16384)) { + cache_addd( STR_IMM(src_reg, addr_reg, data - addr_data) ); // str src_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data + 256) && (data >= addr_data - 256)) { + cache_addd( STUR_IMM(src_reg, addr_reg, data - addr_data) ); // stur src_reg, [addr_reg, #(data - addr_data)] + return true; + } + break; + case 2: + if (((data & 1) == 0) && (data >= addr_data) && (data < addr_data + 8192)) { + cache_addd( STRH_IMM(src_reg, addr_reg, data - addr_data) ); // strh src_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data + 256) && (data >= addr_data - 256)) { + cache_addd( STURH_IMM(src_reg, addr_reg, data - addr_data) ); // sturh src_reg, [addr_reg, #(data - addr_data)] + return true; + } + break; + case 1: + if ((data >= addr_data) && (data < addr_data + 4096)) { + cache_addd( STRB_IMM(src_reg, addr_reg, data - addr_data) ); // strb src_reg, [addr_reg, #(data - addr_data)] + return true; + } else if ((data < addr_data) && (data >= addr_data - 256)) { + cache_addd( STURB_IMM(src_reg, addr_reg, data - addr_data) ); // sturb src_reg, [addr_reg, #(data - addr_data)] + return true; + } + default: + break; + } + return false; +} + +// helper function +static bool gen_mov_memval_from_reg(HostReg src_reg, void *dest, Bitu size) { + if (gen_mov_memval_from_reg_helper(src_reg, (Bit64u)dest, size, FC_REGS_ADDR, (Bit64u)&cpu_regs)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit64u)dest, size, readdata_addr, (Bit64u)&core_dynrec.readdata)) return true; + if (gen_mov_memval_from_reg_helper(src_reg, (Bit64u)dest, size, FC_SEGS_ADDR, (Bit64u)&Segs)) return true; + return false; +} + +// helper function for gen_mov_word_from_reg +static void gen_mov_word_from_reg_helper(HostReg src_reg,void* dest,bool dword, HostReg data_reg) { + if (dword) { + cache_addd( STR_IMM(src_reg, data_reg, 0) ); // str src_reg, [data_reg] + } else { + cache_addd( STRH_IMM(src_reg, data_reg, 0) ); // strh src_reg, [data_reg] + } +} + +// move 32bit (dword==true) or 16bit (dword==false) of a register into memory +static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { + if (!gen_mov_memval_from_reg(src_reg, dest, (dword)?4:2)) { + gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest); + gen_mov_word_from_reg_helper(src_reg, dest, dword, temp1); + } +} + +// move an 8bit value from memory into dest_reg +// the upper 24bit of the destination register can be destroyed +// this function does not use FC_OP1/FC_OP2 as dest_reg as these +// registers might not be directly byte-accessible on some architectures +static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { + if (!gen_mov_memval_to_reg(dest_reg, data, 1)) { + gen_mov_qword_to_reg_imm(temp1, (Bit64u)data); + cache_addd( LDRB_IMM(dest_reg, temp1, 0) ); // ldrb dest_reg, [temp1] + } +} + +// move an 8bit value from memory into dest_reg +// the upper 24bit of the destination register can be destroyed +// this function can use FC_OP1/FC_OP2 as dest_reg which are +// not directly byte-accessible on some architectures +static void INLINE gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) { + gen_mov_byte_to_reg_low(dest_reg, data); +} + +// move an 8bit constant value into dest_reg +// the upper 24bit of the destination register can be destroyed +// this function does not use FC_OP1/FC_OP2 as dest_reg as these +// registers might not be directly byte-accessible on some architectures +static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) { + cache_addd( MOVZ(dest_reg, imm, 0) ); // movz dest_reg, #imm +} + +// move an 8bit constant value into dest_reg +// the upper 24bit of the destination register can be destroyed +// this function can use FC_OP1/FC_OP2 as dest_reg which are +// not directly byte-accessible on some architectures +static void INLINE gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) { + gen_mov_byte_to_reg_low_imm(dest_reg, imm); +} + +// move the lowest 8bit of a register into memory +static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { + if (!gen_mov_memval_from_reg(src_reg, dest, 1)) { + gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest); + cache_addd( STRB_IMM(src_reg, temp1, 0) ); // strb src_reg, [temp1] + } +} + + + +// convert an 8bit word to a 32bit dword +// the register is zero-extended (sign==false) or sign-extended (sign==true) +static void gen_extend_byte(bool sign,HostReg reg) { + if (sign) { + cache_addd( SXTB(reg, reg) ); // sxtb reg, reg + } else { + cache_addd( UXTB(reg, reg) ); // uxtb reg, reg + } +} + +// convert a 16bit word to a 32bit dword +// the register is zero-extended (sign==false) or sign-extended (sign==true) +static void gen_extend_word(bool sign,HostReg reg) { + if (sign) { + cache_addd( SXTH(reg, reg) ); // sxth reg, reg + } else { + cache_addd( UXTH(reg, reg) ); // uxth reg, reg + } +} + +// add a 32bit value from memory to a full register +static void gen_add(HostReg reg,void* op) { + gen_mov_word_to_reg(temp3, op, 1); + cache_addd( ADD_REG_LSL_IMM(reg, reg, temp3, 0) ); // add reg, reg, temp3 +} + +// add a 32bit constant value to a full register +static void gen_add_imm(HostReg reg,Bit32u imm) { + Bit32u imm2; + + if(!imm) return; + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if (imm < 4096) { + cache_addd( ADD_IMM(reg, reg, imm, 0) ); // add reg, reg, #imm + } else if ((imm & 0xff000fff) == 0) { + cache_addd( ADD_IMM(reg, reg, imm >> 12, 12) ); // add reg, reg, #(imm >> 12), lsl #12 + } else if (imm2 < 4096) { + cache_addd( SUB_IMM(reg, reg, imm2, 0) ); // sub reg, reg, #(-imm) + } else if ((imm2 & 0xff000fff) == 0) { + cache_addd( SUB_IMM(reg, reg, imm2 >> 12, 12) ); // sub reg, reg, #(-imm >> 12), lsl #12 + } else if (imm2 < 0x10000) { + cache_addd( MOVZ(temp2, imm2, 0) ); // movz temp2, #(-imm) + cache_addd( SUB_REG_LSL_IMM(reg, reg, temp2, 0) ); // sub reg, reg, temp2 + } else { + gen_mov_dword_to_reg_imm(temp2, imm); + cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2 + } +} + +// and a 32bit constant value with a full register +static void gen_and_imm(HostReg reg,Bit32u imm) { + Bit32u imm2, scale; + + imm2 = ~imm; + if(!imm2) return; + + if (!imm) { + cache_addd( MOVZ(reg, 0, 0) ); // movz reg, #0 + } else if (imm2 < 0x10000) { + cache_addd( MOVZ(temp2, imm2, 0) ); // movz temp2, #(~imm) + cache_addd( BIC_REG_LSL_IMM(reg, reg, temp2, 0) ); // bic reg, reg, temp2 + } else if ((imm2 & 0xffff) == 0) { + cache_addd( MOVZ(temp2, imm2 >> 16, 16) ); // movz temp2, #(~imm >> 16), lsl #16 + cache_addd( BIC_REG_LSL_IMM(reg, reg, temp2, 0) ); // bic reg, reg, temp2 + } else { + gen_mov_dword_to_reg_imm(temp2, imm); + cache_addd( AND_REG_LSL_IMM(reg, reg, temp2, 0) ); // and reg, reg, temp2 + } +} + + +// move a 32bit constant value into memory +static void gen_mov_direct_dword(void* dest,Bit32u imm) { + gen_mov_dword_to_reg_imm(temp3, imm); + gen_mov_word_from_reg(temp3, dest, 1); +} + +// move an address into memory +static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { + gen_mov_qword_to_reg_imm(temp3, imm); + if (!gen_mov_memval_from_reg(temp3, dest, 8)) { + gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest); + cache_addd( STR64_IMM(temp3, temp1, 0) ); // str temp3, [temp1] + } +} + +// add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value +static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if (!dword) imm &= 0xffff; + if(!imm) return; + + if (!gen_mov_memval_to_reg(temp3, dest, (dword)?4:2)) { + gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest); + gen_mov_word_to_reg_helper(temp3, dest, dword, temp1); + } + gen_add_imm(temp3, imm); + if (!gen_mov_memval_from_reg(temp3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(temp3, dest, dword, temp1); + } +} + +// add an 8bit constant value to a dword memory value +static void gen_add_direct_byte(void* dest,Bit8s imm) { + gen_add_direct_word(dest, (Bit32s)imm, 1); +} + +// subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value +static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + Bit32u imm2; + + if (!dword) imm &= 0xffff; + if(!imm) return; + + if (!gen_mov_memval_to_reg(temp3, dest, (dword)?4:2)) { + gen_mov_qword_to_reg_imm(temp1, (Bit64u)dest); + gen_mov_word_to_reg_helper(temp3, dest, dword, temp1); + } + + imm2 = (Bit32u) (-((Bit32s)imm)); + + if (imm < 4096) { + cache_addd( SUB_IMM(temp3, temp3, imm, 0) ); // sub temp3, temp3, #imm + } else if ((imm & 0xff000fff) == 0) { + cache_addd( SUB_IMM(temp3, temp3, imm >> 12, 12) ); // sub temp3, temp3, #(imm >> 12), lsl #12 + } else if (imm2 < 4096) { + cache_addd( ADD_IMM(temp3, temp3, imm2, 0) ); // add temp3, temp3, #(-imm) + } else if ((imm2 & 0xff000fff) == 0) { + cache_addd( ADD_IMM(temp3, temp3, imm2 >> 12, 12) ); // add temp3, temp3, #(-imm >> 12), lsl #12 + } else if (imm2 < 0x10000) { + cache_addd( MOVZ(temp2, imm2, 0) ); // movz temp2, #(-imm) + cache_addd( ADD_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // add temp3, temp3, temp2 + } else { + gen_mov_dword_to_reg_imm(temp2, imm); + cache_addd( SUB_REG_LSL_IMM(temp3, temp3, temp2, 0) ); // sub temp3, temp3, temp2 + } + + if (!gen_mov_memval_from_reg(temp3, dest, (dword)?4:2)) { + gen_mov_word_from_reg_helper(temp3, dest, dword, temp1); + } +} + +// subtract an 8bit constant value from a dword memory value +static void gen_sub_direct_byte(void* dest,Bit8s imm) { + gen_sub_direct_word(dest, (Bit32s)imm, 1); +} + +// effective address calculation, destination is dest_reg +// scale_reg is scaled by scale (scale_reg*(2^scale)) and +// added to dest_reg, then the immediate value is added +static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) { + cache_addd( ADD_REG_LSL_IMM(dest_reg, dest_reg, scale_reg, scale) ); // add dest_reg, dest_reg, scale_reg, lsl #scale + gen_add_imm(dest_reg, imm); +} + +// effective address calculation, destination is dest_reg +// dest_reg is scaled by scale (dest_reg*(2^scale)), +// then the immediate value is added +static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) { + if (scale) { + cache_addd( MOV_REG_LSL_IMM(dest_reg, dest_reg, scale) ); // mov dest_reg, dest_reg, lsl #scale + } + gen_add_imm(dest_reg, imm); +} + +// generate a call to a parameterless function +static void INLINE gen_call_function_raw(void * func) { + cache_addd( MOVZ64(temp1, ((Bit64u)func) & 0xffff, 0) ); // movz dest_reg, #(func & 0xffff) + cache_addd( MOVK64(temp1, (((Bit64u)func) >> 16) & 0xffff, 16) ); // movk dest_reg, #((func >> 16) & 0xffff), lsl #16 + cache_addd( MOVK64(temp1, (((Bit64u)func) >> 32) & 0xffff, 32) ); // movk dest_reg, #((func >> 32) & 0xffff), lsl #32 + cache_addd( MOVK64(temp1, (((Bit64u)func) >> 48) & 0xffff, 48) ); // movk dest_reg, #((func >> 48) & 0xffff), lsl #48 + cache_addd( BLR_REG(temp1) ); // blr temp1 +} + +// generate a call to a function with paramcount parameters +// note: the parameters are loaded in the architecture specific way +// using the gen_load_param_ functions below +static DRC_PTR_SIZE_IM INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) { + DRC_PTR_SIZE_IM proc_addr = (DRC_PTR_SIZE_IM)cache.pos; + gen_call_function_raw(func); + return proc_addr; +} + +// load an immediate value as param'th function parameter +static void INLINE gen_load_param_imm(Bitu imm,Bitu param) { + gen_mov_qword_to_reg_imm(param, imm); +} + +// load an address as param'th function parameter +static void INLINE gen_load_param_addr(DRC_PTR_SIZE_IM addr,Bitu param) { + gen_mov_qword_to_reg_imm(param, addr); +} + +// load a host-register as param'th function parameter +static void INLINE gen_load_param_reg(Bitu reg,Bitu param) { + gen_mov_regs(param, reg); +} + +// load a value from memory as param'th function parameter +static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { + gen_mov_word_to_reg(param, (void *)mem, 1); +} + +// jump to an address pointed at by ptr, offset is in imm +static void gen_jmp_ptr(void * ptr,Bits imm=0) { + if (!gen_mov_memval_to_reg(temp3, ptr, 8)) { + gen_mov_qword_to_reg_imm(temp1, (Bit64u)ptr); + cache_addd( LDR64_IMM(temp3, temp1, 0) ); // ldr temp3, [temp1] + } + + if (((imm & 7) == 0) && (imm >= 0) && (imm < 32768)) { + cache_addd( LDR64_IMM(temp1, temp3, imm) ); // ldr temp1, [temp3, #imm] + } else if ((imm < 256) && (imm >= -256)) { + cache_addd( LDUR64_IMM(temp1, temp3, imm) ); // ldur temp1, [temp3, #imm] + } else { + gen_mov_qword_to_reg_imm(temp2, imm); + cache_addd( LDR64_REG_LSL_IMM(temp1, temp3, temp2, 0) ); // ldr temp1, [temp3, temp2] + } + + cache_addd( BR(temp1) ); // br temp1 +} + +// short conditional jump (+-127 bytes) if register is zero +// the destination is set by gen_fill_branch() later +static DRC_PTR_SIZE_IM gen_create_branch_on_zero(HostReg reg,bool dword) { + if (dword) { + cache_addd( CBZ_FWD(reg, 0) ); // cbz reg, j + } else { + cache_addd( UXTH(temp1, reg) ); // uxth temp1, reg + cache_addd( CBZ_FWD(temp1, 0) ); // cbz temp1, j + } + return ((DRC_PTR_SIZE_IM)cache.pos-4); +} + +// short conditional jump (+-127 bytes) if register is nonzero +// the destination is set by gen_fill_branch() later +static DRC_PTR_SIZE_IM gen_create_branch_on_nonzero(HostReg reg,bool dword) { + if (dword) { + cache_addd( CBNZ_FWD(reg, 0) ); // cbnz reg, j + } else { + cache_addd( UXTH(temp1, reg) ); // uxth temp1, reg + cache_addd( CBNZ_FWD(temp1, 0) ); // cbnz temp1, j + } + return ((DRC_PTR_SIZE_IM)cache.pos-4); +} + +// calculate relative offset and fill it into the location pointed to by data +static void INLINE gen_fill_branch(DRC_PTR_SIZE_IM data) { +#if C_DEBUG + Bits len=(Bit64u)cache.pos-data; + if (len<0) len=-len; + if (len>=0x00100000) LOG_MSG("Big jump %d",len); +#endif + *(Bit32u*)data=( (*(Bit32u*)data) & 0xff00001f ) | ( ( ((Bit64u)cache.pos - data) << 3 ) & 0x00ffffe0 ); +} + +// conditional jump if register is nonzero +// for isdword==true the 32bit of the register are tested +// for isdword==false the lowest 8bit of the register are tested +static DRC_PTR_SIZE_IM gen_create_branch_long_nonzero(HostReg reg,bool isdword) { + if (isdword) { + cache_addd( CBZ_FWD(reg, 8) ); // cbz reg, pc+8 // skip next instruction + } else { + cache_addd( UXTB(temp1, reg) ); // uxtb temp1, reg + cache_addd( CBZ_FWD(temp1, 8) ); // cbz temp1, pc+8 // skip next instruction + } + cache_addd( B_FWD(0) ); // b j + return ((DRC_PTR_SIZE_IM)cache.pos-4); +} + +// compare 32bit-register against zero and jump if value less/equal than zero +static DRC_PTR_SIZE_IM gen_create_branch_long_leqzero(HostReg reg) { + cache_addd( CMP_IMM(reg, 0, 0) ); // cmp reg, #0 + cache_addd( BGT_FWD(8) ); // bgt pc+8 // skip next instruction + cache_addd( B_FWD(0) ); // b j + return ((DRC_PTR_SIZE_IM)cache.pos-4); +} + +// calculate long relative offset and fill it into the location pointed to by data +static void INLINE gen_fill_branch_long(DRC_PTR_SIZE_IM data) { + // optimize for shorter branches ? + *(Bit32u*)data=( (*(Bit32u*)data) & 0xfc000000 ) | ( ( ((Bit64u)cache.pos - data) >> 2 ) & 0x03ffffff ); +} + +static void gen_run_code(void) { + Bit8u *pos1, *pos2, *pos3; + + cache_addd( 0xa9bd7bfd ); // stp fp, lr, [sp, #-48]! + cache_addd( 0x910003fd ); // mov fp, sp + cache_addd( STP64_IMM(FC_ADDR, FC_REGS_ADDR, HOST_sp, 16) ); // stp FC_ADDR, FC_REGS_ADDR, [sp, #16] + cache_addd( STP64_IMM(FC_SEGS_ADDR, readdata_addr, HOST_sp, 32) ); // stp FC_SEGS_ADDR, readdata_addr, [sp, #32] + + pos1 = cache.pos; + cache_addd( 0 ); + pos2 = cache.pos; + cache_addd( 0 ); + pos3 = cache.pos; + cache_addd( 0 ); + + cache_addd( BR(HOST_x0) ); // br x0 + + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } + + *(Bit32u *)pos1 = LDR64_PC(FC_SEGS_ADDR, cache.pos - pos1); // ldr FC_SEGS_ADDR, [pc, #(&Segs)] + cache_addq((Bit64u)&Segs); // address of "Segs" + + *(Bit32u *)pos2 = LDR64_PC(FC_REGS_ADDR, cache.pos - pos2); // ldr FC_REGS_ADDR, [pc, #(&cpu_regs)] + cache_addq((Bit64u)&cpu_regs); // address of "cpu_regs" + + *(Bit32u *)pos3 = LDR64_PC(readdata_addr, cache.pos - pos3); // ldr readdata_addr, [pc, #(&core_dynrec.readdata)] + cache_addq((Bit64u)&core_dynrec.readdata); // address of "core_dynrec.readdata" + + // align cache.pos to 32 bytes + if ((((Bitu)cache.pos) & 0x1f) != 0) { + cache.pos = cache.pos + (32 - (((Bitu)cache.pos) & 0x1f)); + } +} + +// return from a function +static void gen_return_function(void) { + cache_addd( LDP64_IMM(FC_ADDR, FC_REGS_ADDR, HOST_sp, 16) ); // ldp FC_ADDR, FC_REGS_ADDR, [sp, #16] + cache_addd( LDP64_IMM(FC_SEGS_ADDR, readdata_addr, HOST_sp, 32) ); // ldp FC_SEGS_ADDR, readdata_addr, [sp, #32] + cache_addd( 0xa8c37bfd ); // ldp fp, lr, [sp], #48 + cache_addd( RET ); // ret +} + +#ifdef DRC_FLAGS_INVALIDATION + +// called when a call to a function can be replaced by a +// call to a simpler function +static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) { +#ifdef DRC_FLAGS_INVALIDATION_DCODE + // try to avoid function calls but rather directly fill in code + switch (flags_type) { + case t_ADDb: + case t_ADDw: + case t_ADDd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=ADD_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // add FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_ORb: + case t_ORw: + case t_ORd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=ORR_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // orr FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_ANDb: + case t_ANDw: + case t_ANDd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=AND_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // and FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_SUBb: + case t_SUBw: + case t_SUBd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // sub FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_XORb: + case t_XORw: + case t_XORd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=EOR_REG_LSL_IMM(FC_RETOP, HOST_w0, HOST_w1, 0); // eor FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_CMPb: + case t_CMPw: + case t_CMPd: + case t_TESTb: + case t_TESTw: + case t_TESTd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_INCb: + case t_INCw: + case t_INCd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=ADD_IMM(FC_RETOP, HOST_w0, 1, 0); // add FC_RETOP, w0, #1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_DECb: + case t_DECw: + case t_DECd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=SUB_IMM(FC_RETOP, HOST_w0, 1, 0); // sub FC_RETOP, w0, #1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_SHLb: + case t_SHLw: + case t_SHLd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=LSLV(FC_RETOP, HOST_w0, HOST_w1); // lslv FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_SHRb: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=UXTB(FC_RETOP, HOST_w0); // uxtb FC_RETOP, w0 + *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+12)=LSRV(FC_RETOP, FC_RETOP, HOST_w1); // lsrv FC_RETOP, FC_RETOP, w1 + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_SHRw: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=UXTH(FC_RETOP, HOST_w0); // uxth FC_RETOP, w0 + *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+12)=LSRV(FC_RETOP, FC_RETOP, HOST_w1); // lsrv FC_RETOP, FC_RETOP, w1 + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_SHRd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=LSRV(FC_RETOP, HOST_w0, HOST_w1); // lsrv FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_SARb: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=SXTB(FC_RETOP, HOST_w0); // sxtb FC_RETOP, w0 + *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+12)=ASRV(FC_RETOP, FC_RETOP, HOST_w1); // asrv FC_RETOP, FC_RETOP, w1 + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_SARw: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=SXTH(FC_RETOP, HOST_w0); // sxth FC_RETOP, w0 + *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+12)=ASRV(FC_RETOP, FC_RETOP, HOST_w1); // asrv FC_RETOP, FC_RETOP, w1 + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_SARd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=ASRV(FC_RETOP, HOST_w0, HOST_w1); // asrv FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_RORb: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=BFI(HOST_w0, HOST_w0, 8, 8); // bfi w0, w0, 8, 8 + *(Bit32u*)(pos+8)=BFI(HOST_w0, HOST_w0, 16, 16); // bfi w0, w0, 16, 16 + *(Bit32u*)(pos+12)=RORV(FC_RETOP, HOST_w0, HOST_w1); // rorv FC_RETOP, w0, w1 + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_RORw: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=BFI(HOST_w0, HOST_w0, 16, 16); // bfi w0, w0, 16, 16 + *(Bit32u*)(pos+8)=NOP; // nop + *(Bit32u*)(pos+12)=RORV(FC_RETOP, HOST_w0, HOST_w1); // rorv FC_RETOP, w0, w1 + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_RORd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=RORV(FC_RETOP, HOST_w0, HOST_w1); // rorv FC_RETOP, w0, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_ROLb: + *(Bit32u*)pos=MOVZ(HOST_w2, 32, 0); // movz w2, #32 + *(Bit32u*)(pos+4)=BFI(HOST_w0, HOST_w0, 8, 8); // bfi w0, w0, 8, 8 + *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(HOST_w2, HOST_w2, HOST_w1, 0); // sub w2, w2, w1 + *(Bit32u*)(pos+12)=BFI(HOST_w0, HOST_w0, 16, 16); // bfi w0, w0, 16, 16 + *(Bit32u*)(pos+16)=RORV(FC_RETOP, HOST_w0, HOST_w2); // rorv FC_RETOP, w0, w2 + break; + case t_ROLw: + *(Bit32u*)pos=MOVZ(HOST_w2, 32, 0); // movz w2, #32 + *(Bit32u*)(pos+4)=BFI(HOST_w0, HOST_w0, 16, 16); // bfi w0, w0, 16, 16 + *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(HOST_w2, HOST_w2, HOST_w1, 0); // sub w2, w2, w1 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=RORV(FC_RETOP, HOST_w0, HOST_w2); // rorv FC_RETOP, w0, w2 + break; + case t_ROLd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=MOVZ(HOST_w2, 32, 0); // movz w2, #32 + *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(HOST_w2, HOST_w2, HOST_w1, 0); // sub w2, w2, w1 + *(Bit32u*)(pos+12)=RORV(FC_RETOP, HOST_w0, HOST_w2); // rorv FC_RETOP, w0, w2 + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_NEGb: + case t_NEGw: + case t_NEGd: + *(Bit32u*)pos=NOP; // nop + *(Bit32u*)(pos+4)=NOP; // nop + *(Bit32u*)(pos+8)=SUB_REG_LSL_IMM(FC_RETOP, HOST_wzr, HOST_w0, 0); // sub FC_RETOP, wzr, w0 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=NOP; // nop + break; + case t_DSHLd: + *(Bit32u*)pos=MOVZ64(HOST_x3, 0x1f, 0); // movz x3, #0x1f + *(Bit32u*)(pos+4)=BFI64(HOST_x1, HOST_x0, 32, 32); // bfi x1, x0, 32, 32 + *(Bit32u*)(pos+8)=AND64_REG_LSL_IMM(HOST_x2, HOST_x2, HOST_x3, 0); // and x2, x2, x3 + *(Bit32u*)(pos+12)=LSLV64(FC_RETOP, HOST_x1, HOST_x2); // lslv FC_RETOP, x1, x2 + *(Bit32u*)(pos+16)=LSR64_IMM(FC_RETOP, FC_RETOP, 32); // lsr FC_RETOP, FC_RETOP, #32 + break; + case t_DSHRd: + *(Bit32u*)pos=MOVZ64(HOST_x3, 0x1f, 0); // movz x3, #0x1f + *(Bit32u*)(pos+4)=BFI64(HOST_x0, HOST_x1, 32, 32); // bfi x0, x1, 32, 32 + *(Bit32u*)(pos+8)=AND64_REG_LSL_IMM(HOST_x2, HOST_x2, HOST_x3, 0); // and x2, x2, x3 + *(Bit32u*)(pos+12)=NOP; // nop + *(Bit32u*)(pos+16)=LSRV64(FC_RETOP, HOST_x0, HOST_x2); // lsrv FC_RETOP, x0, x2 + break; + default: + *(Bit32u*)pos=MOVZ64(temp1, ((Bit64u)fct_ptr) & 0xffff, 0); // movz temp1, #(fct_ptr & 0xffff) + *(Bit32u*)(pos+4)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 16) & 0xffff, 16); // movk temp1, #((fct_ptr >> 16) & 0xffff), lsl #16 + *(Bit32u*)(pos+8)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 32) & 0xffff, 32); // movk temp1, #((fct_ptr >> 32) & 0xffff), lsl #32 + *(Bit32u*)(pos+12)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 48) & 0xffff, 48); // movk temp1, #((fct_ptr >> 48) & 0xffff), lsl #48 + break; + + } +#else + *(Bit32u*)pos=MOVZ64(temp1, ((Bit64u)fct_ptr) & 0xffff, 0); // movz temp1, #(fct_ptr & 0xffff) + *(Bit32u*)(pos+4)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 16) & 0xffff, 16); // movk temp1, #((fct_ptr >> 16) & 0xffff), lsl #16 + *(Bit32u*)(pos+8)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 32) & 0xffff, 32); // movk temp1, #((fct_ptr >> 32) & 0xffff), lsl #32 + *(Bit32u*)(pos+12)=MOVK64(temp1, (((Bit64u)fct_ptr) >> 48) & 0xffff, 48); // movk temp1, #((fct_ptr >> 48) & 0xffff), lsl #48 +#endif +} +#endif + +static void cache_block_closing(Bit8u* block_start,Bitu block_size) { + //flush cache - GCC/LLVM builtin + __builtin___clear_cache((char *)block_start, (char *)(block_start+block_size)); +} + +static void cache_block_before_close(void) { } + +#ifdef DRC_USE_SEGS_ADDR + +// mov 16bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 2 must be zero) +// 16bit moves may destroy the upper 16bit of the destination register +static void gen_mov_seg16_to_reg(HostReg dest_reg,Bitu index) { + cache_addd( LDRH_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldrh dest_reg, [FC_SEGS_ADDR, #index] +} + +// mov 32bit value from Segs[index] into dest_reg using FC_SEGS_ADDR (index modulo 4 must be zero) +static void gen_mov_seg32_to_reg(HostReg dest_reg,Bitu index) { + cache_addd( LDR_IMM(dest_reg, FC_SEGS_ADDR, index) ); // ldr dest_reg, [FC_SEGS_ADDR, #index] +} + +// add a 32bit value from Segs[index] to a full register using FC_SEGS_ADDR (index modulo 4 must be zero) +static void gen_add_seg32_to_reg(HostReg reg,Bitu index) { + cache_addd( LDR_IMM(temp1, FC_SEGS_ADDR, index) ); // ldr temp1, [FC_SEGS_ADDR, #index] + cache_addd( ADD_REG_LSL_IMM(reg, reg, temp1, 0) ); // add reg, reg, temp1 +} + +#endif + +#ifdef DRC_USE_REGS_ADDR + +// mov 16bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 2 must be zero) +// 16bit moves may destroy the upper 16bit of the destination register +static void gen_mov_regval16_to_reg(HostReg dest_reg,Bitu index) { + cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index] +} + +// mov 32bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (index modulo 4 must be zero) +static void gen_mov_regval32_to_reg(HostReg dest_reg,Bitu index) { + cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index] +} + +// move a 32bit (dword==true) or 16bit (dword==false) value from cpu_regs[index] into dest_reg using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero) +// 16bit moves may destroy the upper 16bit of the destination register +static void gen_mov_regword_to_reg(HostReg dest_reg,Bitu index,bool dword) { + if (dword) { + cache_addd( LDR_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldr dest_reg, [FC_REGS_ADDR, #index] + } else { + cache_addd( LDRH_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrh dest_reg, [FC_REGS_ADDR, #index] + } +} + +// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR +// the upper 24bit of the destination register can be destroyed +// this function does not use FC_OP1/FC_OP2 as dest_reg as these +// registers might not be directly byte-accessible on some architectures +static void gen_mov_regbyte_to_reg_low(HostReg dest_reg,Bitu index) { + cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index] +} + +// move an 8bit value from cpu_regs[index] into dest_reg using FC_REGS_ADDR +// the upper 24bit of the destination register can be destroyed +// this function can use FC_OP1/FC_OP2 as dest_reg which are +// not directly byte-accessible on some architectures +static void gen_mov_regbyte_to_reg_low_canuseword(HostReg dest_reg,Bitu index) { + cache_addd( LDRB_IMM(dest_reg, FC_REGS_ADDR, index) ); // ldrb dest_reg, [FC_REGS_ADDR, #index] +} + + +// add a 32bit value from cpu_regs[index] to a full register using FC_REGS_ADDR (index modulo 4 must be zero) +static void gen_add_regval32_to_reg(HostReg reg,Bitu index) { + cache_addd( LDR_IMM(temp2, FC_REGS_ADDR, index) ); // ldr temp2, [FC_REGS_ADDR, #index] + cache_addd( ADD_REG_LSL_IMM(reg, reg, temp2, 0) ); // add reg, reg, temp2 +} + + +// move 16bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 2 must be zero) +static void gen_mov_regval16_from_reg(HostReg src_reg,Bitu index) { + cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index] +} + +// move 32bit of register into cpu_regs[index] using FC_REGS_ADDR (index modulo 4 must be zero) +static void gen_mov_regval32_from_reg(HostReg src_reg,Bitu index) { + cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index] +} + +// move 32bit (dword==true) or 16bit (dword==false) of a register into cpu_regs[index] using FC_REGS_ADDR (if dword==true index modulo 4 must be zero) (if dword==false index modulo 2 must be zero) +static void gen_mov_regword_from_reg(HostReg src_reg,Bitu index,bool dword) { + if (dword) { + cache_addd( STR_IMM(src_reg, FC_REGS_ADDR, index) ); // str src_reg, [FC_REGS_ADDR, #index] + } else { + cache_addd( STRH_IMM(src_reg, FC_REGS_ADDR, index) ); // strh src_reg, [FC_REGS_ADDR, #index] + } +} + +// move the lowest 8bit of a register into cpu_regs[index] using FC_REGS_ADDR +static void gen_mov_regbyte_from_reg_low(HostReg src_reg,Bitu index) { + cache_addd( STRB_IMM(src_reg, FC_REGS_ADDR, index) ); // strb src_reg, [FC_REGS_ADDR, #index] +} + +#endif diff --git a/src/cpu/core_dynrec/risc_mipsel32.h b/src/cpu/core_dynrec/risc_mipsel32.h index 717e5ff..a36e451 100644 --- a/src/cpu/core_dynrec/risc_mipsel32.h +++ b/src/cpu/core_dynrec/risc_mipsel32.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/cpu/core_dynrec/risc_x64.h b/src/cpu/core_dynrec/risc_x64.h index c710bc6..f00e56f 100644 --- a/src/cpu/core_dynrec/risc_x64.h +++ b/src/cpu/core_dynrec/risc_x64.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -56,11 +56,16 @@ typedef Bit8u HostReg; // then define DRC_PROTECT_ADDR_REG above #define FC_ADDR HOST_EBX +#if defined (_WIN64) +#define FC_OP1 HOST_ECX +#define FC_OP2 HOST_EDX +#else // register that holds the first parameter #define FC_OP1 HOST_EDI // register that holds the second parameter #define FC_OP2 HOST_ESI +#endif // special register that holds the third parameter for _R3 calls (byte accessible) #define FC_OP3 HOST_EAX @@ -78,55 +83,105 @@ typedef Bit8u HostReg; // move a full register from reg_src to reg_dst static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { + if (reg_dst==reg_src) return; cache_addb(0x8b); // mov reg_dst,reg_src cache_addb(0xc0+(reg_dst<<3)+reg_src); } +static void gen_mov_reg_qword(HostReg dest_reg,Bit64u imm); -static INLINE void gen_reg_memaddr(HostReg reg,void* data) { - Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+5); - if ((diff<0x80000000LL) && (diff>-0x80000000LL)) { +// This function generates an instruction with register addressing and a memory location +static INLINE void gen_reg_memaddr(HostReg reg,void* data,Bit8u op,Bit8u prefix=0) { + Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+(prefix?7:6)); +// if ((diff<0x80000000LL) && (diff>-0x80000000LL)) { //clang messes itself up on this... + if ( (diff>>63) == (diff>>31) ) { //signed bit extend, test to see if value fits in a Bit32s + // mov reg,[rip+diff] (or similar, depending on the op) to fetch *data + if(prefix) cache_addb(prefix); + cache_addb(op); cache_addb(0x05+(reg<<3)); // RIP-relative addressing is offset after the instruction cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL)); } else if ((Bit64u)data<0x100000000LL) { + // mov reg,[data] (or similar, depending on the op) when absolute address of data is <4GB + if(prefix) cache_addb(prefix); + cache_addb(op); cache_addw(0x2504+(reg<<3)); cache_addd((Bit32u)(((Bit64u)data)&0xffffffffLL)); } else { - E_Exit("DRC64:Unhandled memory reference"); + // load 64-bit data into tmp_reg and do mov reg,[tmp_reg] (or similar, depending on the op) + HostReg tmp_reg = HOST_EAX; + if(reg == HOST_EAX) tmp_reg = HOST_ECX; + + cache_addb(0x50+tmp_reg); // push rax/rcx + gen_mov_reg_qword(tmp_reg,(Bit64u)data); + + if(prefix) cache_addb(prefix); + cache_addb(op); + cache_addb(tmp_reg+(reg<<3)); + + cache_addb(0x58+tmp_reg); // pop rax/rcx } } -static INLINE void gen_memaddr(Bitu op,void* data,Bitu off) { - Bit64s diff; - diff = (Bit64s)data-((Bit64s)cache.pos+off+5); - if ((diff<0x80000000LL) && (diff>-0x80000000LL)) { +// Same as above, but with immediate addressing and a memory location +static INLINE void gen_memaddr(Bitu modreg,void* data,Bitu off,Bitu imm,Bit8u op,Bit8u prefix=0) { + Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+off+(prefix?7:6)); +// if ((diff<0x80000000LL) && (diff>-0x80000000LL)) { + if ( (diff>>63) == (diff>>31) ) { // RIP-relative addressing is offset after the instruction - cache_addb(op+1); - cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL)); + if(prefix) cache_addb(prefix); + cache_addw(op+((modreg+1)<<8)); + cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL)); + + switch(off) { + case 1: cache_addb(((Bit8u)imm&0xff)); break; + case 2: cache_addw(((Bit16u)imm&0xffff)); break; + case 4: cache_addd(((Bit32u)imm&0xffffffff)); break; + } + } else if ((Bit64u)data<0x100000000LL) { - cache_addb(op); + if(prefix) cache_addb(prefix); + cache_addw(op+(modreg<<8)); cache_addb(0x25); cache_addd((Bit32u)(((Bit64u)data)&0xffffffffLL)); + + switch(off) { + case 1: cache_addb(((Bit8u)imm&0xff)); break; + case 2: cache_addw(((Bit16u)imm&0xffff)); break; + case 4: cache_addd(((Bit32u)imm&0xffffffff)); break; + } + } else { - E_Exit("DRC64:Unhandled memory reference"); + HostReg tmp_reg = HOST_EAX; + + cache_addb(0x50+tmp_reg); // push rax + gen_mov_reg_qword(tmp_reg,(Bit64u)data); + + if(prefix) cache_addb(prefix); + cache_addw(op+((modreg-4+tmp_reg)<<8)); + + switch(off) { + case 1: cache_addb(((Bit8u)imm&0xff)); break; + case 2: cache_addw(((Bit16u)imm&0xffff)); break; + case 4: cache_addd(((Bit32u)imm&0xffffffff)); break; + } + + cache_addb(0x58+tmp_reg); // pop rax } } // move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg // 16bit moves may destroy the upper 16bit of the destination register -static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { - if (!dword) cache_addb(0x66); - cache_addb(0x8b); // mov reg,[data] - gen_reg_memaddr(dest_reg,data); +static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword,Bit8u prefix=0) { + if (!dword) gen_reg_memaddr(dest_reg,data,0xb7,0x0f); // movzx reg,[data] - zero extend data, fixes LLVM compile where the called function does not extend the parameters + else gen_reg_memaddr(dest_reg,data,0x8b,prefix); // mov reg,[data] } // move a 16bit constant value into dest_reg // the upper 16bit of the destination register may be destroyed static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { - cache_addb(0x66); cache_addb(0xb8+dest_reg); // mov reg,imm - cache_addw(imm); + cache_addd((Bit32u)imm); } // move a 32bit constant value into dest_reg @@ -135,11 +190,20 @@ static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { cache_addd(imm); } +// move a 64bit constant value into a full register +static void gen_mov_reg_qword(HostReg dest_reg,Bit64u imm) { + if (imm==(Bit32u)imm) { + gen_mov_dword_to_reg_imm(dest_reg, (Bit32u)imm); + return; + } + cache_addb(0x48); + cache_addb(0xb8+dest_reg); // mov dest_reg,imm + cache_addq(imm); +} + // move 32bit (dword==true) or 16bit (dword==false) of a register into memory -static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { - if (!dword) cache_addb(0x66); - cache_addb(0x89); // mov [data],reg - gen_reg_memaddr(src_reg,dest); +static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword,Bit8u prefix=0) { + gen_reg_memaddr(src_reg,dest,0x89,(dword?prefix:0x66)); // mov [data],reg } // move an 8bit value from memory into dest_reg @@ -147,8 +211,7 @@ static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { // this function does not use FC_OP1/FC_OP2 as dest_reg as these // registers might not be directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { - cache_addb(0x8a); // mov reg,[data] - gen_reg_memaddr(dest_reg,data); + gen_reg_memaddr(dest_reg,data,0xb6,0x0f); // movzx reg,[data] } // move an 8bit value from memory into dest_reg @@ -156,9 +219,7 @@ static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { // this function can use FC_OP1/FC_OP2 as dest_reg which are // not directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) { - cache_addb(0x66); - cache_addb(0x8b); // mov reg,[data] - gen_reg_memaddr(dest_reg,data); + gen_reg_memaddr(dest_reg,data,0xb6,0x0f); // movzx reg,[data] } // move an 8bit constant value into dest_reg @@ -166,8 +227,8 @@ static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) { // this function does not use FC_OP1/FC_OP2 as dest_reg as these // registers might not be directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) { - cache_addb(0xb0+dest_reg); // mov reg,imm - cache_addb(imm); + cache_addb(0xb8+dest_reg); // mov reg,imm + cache_addd((Bit32u)imm); } // move an 8bit constant value into dest_reg @@ -175,15 +236,13 @@ static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) { // this function can use FC_OP1/FC_OP2 as dest_reg which are // not directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) { - cache_addb(0x66); cache_addb(0xb8+dest_reg); // mov reg,imm - cache_addw(imm); + cache_addd((Bit32u)imm); } // move the lowest 8bit of a register into memory static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { - cache_addb(0x88); // mov [data],reg - gen_reg_memaddr(src_reg,dest); + gen_reg_memaddr(src_reg,dest,0x88); // mov byte [data],reg } @@ -206,12 +265,12 @@ static void gen_extend_word(bool sign,HostReg reg) { // add a 32bit value from memory to a full register static void gen_add(HostReg reg,void* op) { - cache_addb(0x03); // add reg,[data] - gen_reg_memaddr(reg,op); + gen_reg_memaddr(reg,op,0x03); // add reg,[data] } // add a 32bit constant value to a full register static void gen_add_imm(HostReg reg,Bit32u imm) { + if (!imm) return; cache_addw(0xc081+(reg<<8)); // add reg,imm cache_addd(imm); } @@ -226,72 +285,47 @@ static void gen_and_imm(HostReg reg,Bit32u imm) { // move a 32bit constant value into memory static void gen_mov_direct_dword(void* dest,Bit32u imm) { - cache_addb(0xc7); // mov [data],imm - gen_memaddr(0x04,dest,4); - cache_addd(imm); + gen_memaddr(0x4,dest,4,imm,0xc7); // mov [data],imm } -// move a 64bit constant value into a full register -static void gen_mov_reg_qword(HostReg dest_reg,Bit64u imm) { - cache_addb(0x48); - cache_addb(0xb8+dest_reg); // mov dest_reg,imm - cache_addq(imm); -} // move an address into memory static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { gen_mov_reg_qword(HOST_EAX,imm); - cache_addb(0x48); - gen_mov_word_from_reg(HOST_EAX,dest,true); + gen_mov_word_from_reg(HOST_EAX,dest,true,0x48); // 0x48 prefixes full 64-bit mov } // add an 8bit constant value to a memory value static void gen_add_direct_byte(void* dest,Bit8s imm) { - cache_addb(0x83); // add [data],imm - gen_memaddr(0x4,dest,1); - cache_addb(imm); + if (!imm) return; + gen_memaddr(0x4,dest,1,imm,0x83); // add [data],imm } // add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { + if (!imm) return; if ((imm<128) && dword) { gen_add_direct_byte(dest,(Bit8s)imm); return; } - if (!dword) cache_addb(0x66); - cache_addb(0x81); // add [data],imm - if (dword) { - gen_memaddr(0x4,dest,4); // size of following immediate value - cache_addd((Bit32u)imm); - } else { - gen_memaddr(0x4,dest,2); // size of following immediate value - cache_addw((Bit16u)imm); - } + gen_memaddr(0x4,dest,(dword?4:2),imm,0x81,(dword?0:0x66)); // add [data],imm } // subtract an 8bit constant value from a memory value static void gen_sub_direct_byte(void* dest,Bit8s imm) { - cache_addb(0x83); // sub [data],imm - gen_memaddr(0x2c,dest,1); - cache_addb(imm); + if (!imm) return; + gen_memaddr(0x2c,dest,1,imm,0x83); } // subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { + if (!imm) return; if ((imm<128) && dword) { gen_sub_direct_byte(dest,(Bit8s)imm); return; } - if (!dword) cache_addb(0x66); - cache_addw(0x81); // sub [data],imm - if (dword) { - gen_memaddr(0x2c,dest,4); // size of following immediate value - cache_addd((Bit32u)imm); - } else { - gen_memaddr(0x2c,dest,2); // size of following immediate value - cache_addw((Bit16u)imm); - } + gen_memaddr(0x2c,dest,(dword?4:2),imm,0x81,(dword?0:0x66)); // sub [data],imm } @@ -341,58 +375,17 @@ static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) { // generate a call to a parameterless function static void INLINE gen_call_function_raw(void * func) { - cache_addb(0x48); - cache_addw(0xec83); - cache_addb(0x08); // sub rsp,0x08 (align stack to 16 byte boundary) - - cache_addb(0x48); - cache_addb(0xb8); // mov reg,imm64 + cache_addw(0xb848); cache_addq((Bit64u)func); cache_addw(0xd0ff); - - cache_addb(0x48); - cache_addw(0xc483); - cache_addb(0x08); // add rsp,0x08 (reset alignment) } // generate a call to a function with paramcount parameters // note: the parameters are loaded in the architecture specific way // using the gen_load_param_ functions below static Bit64u INLINE gen_call_function_setup(void * func,Bitu paramcount,bool fastcall=false) { - // align the stack - cache_addb(0x48); - cache_addw(0xc48b); // mov rax,rsp - - cache_addb(0x48); - cache_addw(0xec83); // sub rsp,0x08 - cache_addb(0x08); // 0x08==return address pushed onto stack by call - - cache_addb(0x48); - cache_addw(0xe483); // and esp,0xfffffffffffffff0 - cache_addb(0xf0); - - cache_addb(0x48); - cache_addw(0xc483); // add rsp,0x08 - cache_addb(0x08); - - // stack is 16 byte aligned now - - - cache_addb(0x50); // push rax (==old rsp) - - // returned address relates to where the address is stored in gen_call_function_raw - Bit64u proc_addr=(Bit64u)cache.pos-4; - - // Do the actual call to the procedure - cache_addb(0x48); - cache_addb(0xb8); // mov reg,imm64 - cache_addq((Bit64u)func); - - cache_addw(0xd0ff); - - // restore stack - cache_addb(0x5c); // pop rsp - + Bit64u proc_addr = (Bit64u)cache.pos; + gen_call_function_raw(func); return proc_addr; } @@ -407,14 +400,14 @@ static void INLINE gen_load_param_imm(Bitu imm,Bitu param) { case 1: // mov param2,imm32 gen_mov_dword_to_reg_imm(FC_OP2,(Bit32u)imm); break; -#if defined (_MSC_VER) - case 2: // mov r8,imm32 - cache_addw(0xb849); - cache_addq((Bit32u)imm); +#if defined (_WIN64) + case 2: // mov r8d,imm32 + cache_addw(0xb841); + cache_addd((Bit32u)imm); break; - case 3: // mov r9,imm32 - cache_addw(0xb949); - cache_addq((Bit32u)imm); + case 3: // mov r9d,imm32 + cache_addw(0xb941); + cache_addd((Bit32u)imm); break; #else case 2: // mov rdx,imm32 @@ -440,7 +433,7 @@ static void INLINE gen_load_param_addr(DRC_PTR_SIZE_IM addr,Bitu param) { case 1: // mov param2,addr64 gen_mov_reg_qword(FC_OP2,addr); break; -#if defined (_MSC_VER) +#if defined (_WIN64) case 2: // mov r8,addr64 cache_addw(0xb849); cache_addq(addr); @@ -473,14 +466,14 @@ static void INLINE gen_load_param_reg(Bitu reg,Bitu param) { case 1: // mov param2,reg&7 gen_mov_regs(FC_OP2,reg&7); break; -#if defined (_MSC_VER) +#if defined (_WIN64) case 2: // mov r8,reg&7 - cache_addb(0x49); - gen_mov_regs(0,reg&7); + cache_addw(0x8949); + cache_addb(0xc0 + ((reg & 7) << 3)); break; case 3: // mov r9,reg&7 - cache_addb(0x49); - gen_mov_regs(1,reg&7); + cache_addw(0x8949); + cache_addb(0xc1 + ((reg & 7) << 3)); break; #else case 2: // mov rdx,reg&7 @@ -506,20 +499,18 @@ static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { case 1: // mov param2,[mem] gen_mov_word_to_reg(FC_OP2,(void*)mem,true); break; -#if defined (_MSC_VER) - case 2: // mov r8,[mem] - cache_addb(0x49); - gen_mov_word_to_reg(0,(void*)mem,true); +#if defined (_WIN64) + case 2: // mov r8d,[mem] + gen_mov_word_to_reg(0,(void*)mem,true,0x44); // 0x44, use x64 rXd regs break; - case 3: // mov r9,[mem] - cache_addb(0x49); - gen_mov_word_to_reg(1,(void*)mem,true); + case 3: // mov r9d,[mem] + gen_mov_word_to_reg(1,(void*)mem,true,0x44); // 0x44, use x64 rXd regs break; #else - case 2: // mov rdx,[mem] + case 2: // mov edx,[mem] gen_mov_word_to_reg(HOST_EDX,(void*)mem,true); break; - case 3: // mov rcx,[mem] + case 3: // mov ecx,[mem] gen_mov_word_to_reg(HOST_ECX,(void*)mem,true); break; #endif @@ -610,16 +601,19 @@ static void gen_fill_branch_long(Bit64u data) { *(Bit32u*)data=(Bit32u)((Bit64u)cache.pos-data-4); } - static void gen_run_code(void) { - cache_addb(0x53); // push rbx - cache_addw(0xd0ff+(FC_OP1<<8)); // call rdi - cache_addb(0x5b); // pop rbx + cache_addw(0x5355); // push rbp,rbx + cache_addb(0x56); // push rsi + cache_addd(0x20EC8348); // sub rsp, 32 + cache_addb(0x48);cache_addw(0x2D8D);cache_addd(2); // lea rbp, [rip+2] + cache_addw(0xE0FF+(FC_OP1<<8)); // jmp FC_OP1 + cache_addd(0x20C48348); // add rsp, 32 + cache_addd(0xC35D5B5E); // pop rsi,rbx,rbp;ret } // return from a function static void gen_return_function(void) { - cache_addb(0xc3); // ret + cache_addw(0xE5FF); // jmp rbp } #ifdef DRC_FLAGS_INVALIDATION @@ -634,94 +628,77 @@ static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) { case t_ADDb: case t_ADDw: case t_ADDd: - *(Bit32u*)(pos+0)=0xf001f889; // mov eax,edi; add eax,esi - *(Bit32u*)(pos+4)=0x90900eeb; // skip + // mov eax,FC_OP1; add eax,FC_OP2 + *(Bit32u*)(pos+0)=0xc001c089+(FC_OP1<<11)+(FC_OP2<<27); + *(Bit32u*)(pos+4)=0x909006eb; // skip *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; + return; case t_ORb: case t_ORw: case t_ORd: - *(Bit32u*)(pos+0)=0xf009f889; // mov eax,edi; or eax,esi - *(Bit32u*)(pos+4)=0x90900eeb; // skip + // mov eax,FC_OP1; or eax,FC_OP2 + *(Bit32u*)(pos+0)=0xc009c089+(FC_OP1<<11)+(FC_OP2<<27); + *(Bit32u*)(pos+4)=0x909006eb; // skip *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; + return; case t_ANDb: case t_ANDw: case t_ANDd: - *(Bit32u*)(pos+0)=0xf021f889; // mov eax,edi; and eax,esi - *(Bit32u*)(pos+4)=0x90900eeb; // skip + // mov eax,FC_OP1; and eax,FC_OP2 + *(Bit32u*)(pos+0)=0xc021c089+(FC_OP1<<11)+(FC_OP2<<27); + *(Bit32u*)(pos+4)=0x909006eb; // skip *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; + return; case t_SUBb: case t_SUBw: case t_SUBd: - *(Bit32u*)(pos+0)=0xf029f889; // mov eax,edi; sub eax,esi - *(Bit32u*)(pos+4)=0x90900eeb; // skip + // mov eax,FC_OP1; sub eax,FC_OP2 + *(Bit32u*)(pos+0)=0xc029c089+(FC_OP1<<11)+(FC_OP2<<27); + *(Bit32u*)(pos+4)=0x909006eb; // skip *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; + return; case t_XORb: case t_XORw: case t_XORd: - *(Bit32u*)(pos+0)=0xf031f889; // mov eax,edi; xor eax,esi - *(Bit32u*)(pos+4)=0x90900eeb; // skip + // mov eax,FC_OP1; xor eax,FC_OP2 + *(Bit32u*)(pos+0)=0xc031c089+(FC_OP1<<11)+(FC_OP2<<27); + *(Bit32u*)(pos+4)=0x909006eb; // skip *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; + return; case t_CMPb: case t_CMPw: case t_CMPd: case t_TESTb: case t_TESTw: case t_TESTd: - *(Bit32u*)(pos+0)=0x909012eb; // skip + *(Bit32u*)(pos+0)=0x90900aeb; // skip *(Bit32u*)(pos+4)=0x90909090; *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; + return; case t_INCb: case t_INCw: case t_INCd: - *(Bit32u*)(pos+0)=0xc0fff889; // mov eax,edi; inc eax - *(Bit32u*)(pos+4)=0x90900eeb; // skip + *(Bit32u*)(pos+0)=0xc0ffc089+(FC_OP1<<11); // mov eax,ecx; inc eax + *(Bit32u*)(pos+4)=0x909006eb; // skip *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; + return; case t_DECb: case t_DECw: case t_DECd: - *(Bit32u*)(pos+0)=0xc8fff889; // mov eax,edi; dec eax - *(Bit32u*)(pos+4)=0x90900eeb; // skip + *(Bit32u*)(pos+0)=0xc8ffc089+(FC_OP1<<11); // mov eax, FC_OP1; dec eax + *(Bit32u*)(pos+4)=0x909006eb; // skip *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; + return; case t_NEGb: case t_NEGw: case t_NEGd: - *(Bit32u*)(pos+0)=0xd8f7f889; // mov eax,edi; neg eax - *(Bit32u*)(pos+4)=0x90900eeb; // skip + *(Bit32u*)(pos+0)=0xd8f7c089+(FC_OP1<<11); // mov eax, FC_OP1; neg eax + *(Bit32u*)(pos+4)=0x909006eb; // skip *(Bit32u*)(pos+8)=0x90909090; - *(Bit32u*)(pos+12)=0x90909090; - *(Bit32u*)(pos+16)=0x90909090; - break; - default: - *(Bit64u*)(pos+6)=(Bit64u)fct_ptr; // fill function pointer - break; + return; } -#else - *(Bit64u*)(pos+6)=(Bit64u)fct_ptr; // fill function pointer #endif + *(Bit64u*)(pos+2)=(Bit64u)fct_ptr; // fill function pointer } #endif diff --git a/src/cpu/core_dynrec/risc_x86.h b/src/cpu/core_dynrec/risc_x86.h index 99063d2..82b0013 100644 --- a/src/cpu/core_dynrec/risc_x86.h +++ b/src/cpu/core_dynrec/risc_x86.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/cpu/core_full.cpp b/src/cpu/core_full.cpp index f70f807..20a62d1 100644 --- a/src/cpu/core_full.cpp +++ b/src/cpu/core_full.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dosbox.h" diff --git a/src/cpu/core_full/ea_lookup.h b/src/cpu/core_full/ea_lookup.h index f9647af..3d3b25d 100644 --- a/src/cpu/core_full/ea_lookup.h +++ b/src/cpu/core_full/ea_lookup.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ { diff --git a/src/cpu/core_full/load.h b/src/cpu/core_full/load.h index 0a3f1ae..d029def 100644 --- a/src/cpu/core_full/load.h +++ b/src/cpu/core_full/load.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ switch (inst.code.load) { @@ -186,15 +186,19 @@ l_M_Ed: case M_GRP: inst.code=Groups[inst.code.op][inst.rm_index]; goto l_MODRMswitch; - case M_GRP_Ib: - inst_op2_d=Fetchb(); + case M_SHIFT_Ib: + inst_op2_d=Fetchb() & 0x1f; + if (!inst_op2_d) + break; inst.code=Groups[inst.code.op][inst.rm_index]; goto l_MODRMswitch; - case M_GRP_CL: - inst_op2_d=reg_cl; + case M_SHIFT_CL: + inst_op2_d=reg_cl & 0x1f; + if (!inst_op2_d) + break; inst.code=Groups[inst.code.op][inst.rm_index]; goto l_MODRMswitch; - case M_GRP_1: + case M_SHIFT_1: inst_op2_d=1; inst.code=Groups[inst.code.op][inst.rm_index]; goto l_MODRMswitch; diff --git a/src/cpu/core_full/loadwrite.h b/src/cpu/core_full/loadwrite.h index 2289151..e432a1f 100644 --- a/src/cpu/core_full/loadwrite.h +++ b/src/cpu/core_full/loadwrite.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define SaveIP() reg_eip=(Bit32u)(inst.cseip-SegBase(cs)); diff --git a/src/cpu/core_full/op.h b/src/cpu/core_full/op.h index ea5f517..cbbf19a 100644 --- a/src/cpu/core_full/op.h +++ b/src/cpu/core_full/op.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Do the actual opcode */ diff --git a/src/cpu/core_full/optable.h b/src/cpu/core_full/optable.h index 81eb94a..1ea95db 100644 --- a/src/cpu/core_full/optable.h +++ b/src/cpu/core_full/optable.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Big ass opcode table normal,double, 66 normal, 66 double */ @@ -155,7 +155,7 @@ static OpCode OpCodeTable[1024]={ {L_Iw ,0 ,S_REGw ,REGI_SI},{L_Iw ,0 ,S_REGw ,REGI_DI}, /* 0xc0 - 0xc7 */ -{L_MODRM ,5 ,0 ,M_GRP_Ib },{L_MODRM ,6 ,0 ,M_GRP_Ib }, +{L_MODRM ,5 ,0 ,M_SHIFT_Ib },{L_MODRM ,6 ,0 ,M_SHIFT_Ib }, {L_POPw ,0 ,S_IPIw ,0 },{L_POPw ,0 ,S_IP ,0 }, {L_MODRM ,O_SEGES ,S_SEGGw,M_Efw },{L_MODRM ,O_SEGDS ,S_SEGGw,M_Efw }, {L_MODRM ,0 ,S_Eb ,M_Ib },{L_MODRM ,0 ,S_Ew ,M_Iw }, @@ -166,8 +166,8 @@ static OpCode OpCodeTable[1024]={ {L_INTO ,O_INT ,0 ,0 },{D_IRETw ,0 ,0 ,0 }, /* 0xd0 - 0xd7 */ -{L_MODRM ,5 ,0 ,M_GRP_1 },{L_MODRM ,6 ,0 ,M_GRP_1 }, -{L_MODRM ,5 ,0 ,M_GRP_CL },{L_MODRM ,6 ,0 ,M_GRP_CL }, +{L_MODRM ,5 ,0 ,M_SHIFT_1 },{L_MODRM ,6 ,0 ,M_SHIFT_1 }, +{L_MODRM ,5 ,0 ,M_SHIFT_CL },{L_MODRM ,6 ,0 ,M_SHIFT_CL }, {L_Ib ,O_AAM ,0 ,0 },{L_Ib ,O_AAD ,0 ,0 }, {D_SETALC ,0 ,0 ,0 },{D_XLAT ,0 ,0 ,0 }, //TODO FPU @@ -511,7 +511,7 @@ static OpCode OpCodeTable[1024]={ {L_Id ,0 ,S_REGd ,REGI_SI},{L_Id ,0 ,S_REGd ,REGI_DI}, /* 0x2c0 - 0x2c7 */ -{L_MODRM ,5 ,0 ,M_GRP_Ib },{L_MODRM ,7 ,0 ,M_GRP_Ib }, +{L_MODRM ,5 ,0 ,M_SHIFT_Ib },{L_MODRM ,7 ,0 ,M_SHIFT_Ib }, {L_POPd ,0 ,S_IPIw ,0 },{L_POPd ,0 ,S_IP ,0 }, {L_MODRM ,O_SEGES ,S_SEGGd,M_Efd },{L_MODRM ,O_SEGDS ,S_SEGGd,M_Efd }, {L_MODRM ,0 ,S_Eb ,M_Ib },{L_MODRM ,0 ,S_Ed ,M_Id }, @@ -522,8 +522,8 @@ static OpCode OpCodeTable[1024]={ {L_INTO ,O_INT ,0 ,0 },{D_IRETd ,0 ,0 ,0 }, /* 0x2d0 - 0x2d7 */ -{L_MODRM ,5 ,0 ,M_GRP_1 },{L_MODRM ,7 ,0 ,M_GRP_1 }, -{L_MODRM ,5 ,0 ,M_GRP_CL },{L_MODRM ,7 ,0 ,M_GRP_CL }, +{L_MODRM ,5 ,0 ,M_SHIFT_1 },{L_MODRM ,7 ,0 ,M_SHIFT_1 }, +{L_MODRM ,5 ,0 ,M_SHIFT_CL },{L_MODRM ,7 ,0 ,M_SHIFT_CL }, {L_Ib ,O_AAM ,0 ,0 },{L_Ib ,O_AAD ,0 ,0 }, {D_SETALC ,0 ,0 ,0 },{D_XLAT ,0 ,0 ,0 }, /* 0x2d8 - 0x2df */ diff --git a/src/cpu/core_full/save.h b/src/cpu/core_full/save.h index 142ec5c..c494f4b 100644 --- a/src/cpu/core_full/save.h +++ b/src/cpu/core_full/save.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Write the data from the opcode */ diff --git a/src/cpu/core_full/string.h b/src/cpu/core_full/string.h index ee99dea..44ddc0d 100644 --- a/src/cpu/core_full/string.h +++ b/src/cpu/core_full/string.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ { @@ -88,6 +88,13 @@ di_index=(di_index+add_index) & add_mask; } break; + case R_INSD: + add_index<<=2; + for (;count>0;count--) { + SaveMd(di_base+di_index,IO_ReadD(reg_dx)); + di_index=(di_index+add_index) & add_mask; + } + break; case R_STOSB: for (;count>0;count--) { SaveMb(di_base+di_index,reg_al); diff --git a/src/cpu/core_full/support.h b/src/cpu/core_full/support.h index a6b1207..f8d8b33 100644 --- a/src/cpu/core_full/support.h +++ b/src/cpu/core_full/support.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ enum { @@ -167,7 +167,8 @@ enum { M_SEG,M_EA, M_GRP, - M_GRP_Ib,M_GRP_CL,M_GRP_1, + //Special shift groups + M_SHIFT_1, M_SHIFT_Ib,M_SHIFT_CL, M_POPw,M_POPd }; diff --git a/src/cpu/core_normal.cpp b/src/cpu/core_normal.cpp index 530e0c6..0d9cfc3 100644 --- a/src/cpu/core_normal.cpp +++ b/src/cpu/core_normal.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include @@ -58,6 +58,8 @@ extern Bitu cycle_count; #define CPU_PIC_CHECK 1 #define CPU_TRAP_CHECK 1 +#define CPU_TRAP_DECODER CPU_Core_Normal_Trap_Run + #define OPCODE_NONE 0x000 #define OPCODE_0F 0x100 #define OPCODE_SIZE 0x200 diff --git a/src/cpu/core_normal/helpers.h b/src/cpu/core_normal/helpers.h index 2011494..bd4d72e 100644 --- a/src/cpu/core_normal/helpers.h +++ b/src/cpu/core_normal/helpers.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/cpu/core_normal/prefix_0f.h b/src/cpu/core_normal/prefix_0f.h index 38004f1..c19ac75 100644 --- a/src/cpu/core_normal/prefix_0f.h +++ b/src/cpu/core_normal/prefix_0f.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ CASE_0F_W(0x00) /* GRP 6 Exxx */ diff --git a/src/cpu/core_normal/prefix_66.h b/src/cpu/core_normal/prefix_66.h index a40bc23..0c9218c 100644 --- a/src/cpu/core_normal/prefix_66.h +++ b/src/cpu/core_normal/prefix_66.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ CASE_D(0x01) /* ADD Ed,Gd */ @@ -390,7 +390,7 @@ CPU_CALL(true,newcs,newip,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif @@ -403,7 +403,7 @@ if (CPU_POPF(true)) RUNEXCEPTION(); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; goto decode_end; } #endif @@ -515,7 +515,7 @@ CPU_IRET(true,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif @@ -589,7 +589,7 @@ CPU_JMP(true,newcs,newip,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif @@ -677,7 +677,7 @@ CPU_CALL(true,newcs,newip,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif @@ -697,7 +697,7 @@ CPU_JMP(true,newcs,newip,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif diff --git a/src/cpu/core_normal/prefix_66_0f.h b/src/cpu/core_normal/prefix_66_0f.h index 832e8f4..38966aa 100644 --- a/src/cpu/core_normal/prefix_66_0f.h +++ b/src/cpu/core_normal/prefix_66_0f.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ CASE_0F_D(0x00) /* GRP 6 Exxx */ diff --git a/src/cpu/core_normal/prefix_none.h b/src/cpu/core_normal/prefix_none.h index c732d1a..5d59e80 100644 --- a/src/cpu/core_normal/prefix_none.h +++ b/src/cpu/core_normal/prefix_none.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ CASE_B(0x00) /* ADD Eb,Gb */ @@ -564,7 +564,7 @@ CPU_CALL(false,newcs,newip,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif @@ -579,7 +579,7 @@ if (CPU_POPF(false)) RUNEXCEPTION(); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; goto decode_end; } #endif @@ -780,7 +780,7 @@ CPU_IRET(false,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif @@ -919,7 +919,7 @@ CPU_JMP(false,newcs,newip,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif @@ -1132,7 +1132,7 @@ CPU_CALL(false,newcs,newip,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif @@ -1153,7 +1153,7 @@ CPU_JMP(false,newcs,newip,GETIP); #if CPU_TRAP_CHECK if (GETFLAG(TF)) { - cpudecoder=CPU_Core_Normal_Trap_Run; + cpudecoder=CPU_TRAP_DECODER; return CBRET_NONE; } #endif diff --git a/src/cpu/core_normal/string.h b/src/cpu/core_normal/string.h index fb9758a..97d8035 100644 --- a/src/cpu/core_normal/string.h +++ b/src/cpu/core_normal/string.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ enum STRING_OP { @@ -93,6 +93,13 @@ static void DoString(STRING_OP type) { di_index=(di_index+add_index) & add_mask; } break; + case R_INSD: + add_index<<=2; + for (;count>0;count--) { + SaveMd(di_base+di_index,IO_ReadD(reg_dx)); + di_index=(di_index+add_index) & add_mask; + } + break; case R_STOSB: for (;count>0;count--) { SaveMb(di_base+di_index,reg_al); diff --git a/src/cpu/core_normal/support.h b/src/cpu/core_normal/support.h index 5752d2a..9b94b78 100644 --- a/src/cpu/core_normal/support.h +++ b/src/cpu/core_normal/support.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/cpu/core_normal/table_ea.h b/src/cpu/core_normal/table_ea.h index e6ac3f2..f4a764a 100644 --- a/src/cpu/core_normal/table_ea.h +++ b/src/cpu/core_normal/table_ea.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ typedef PhysPt (*EA_LookupHandler)(void); diff --git a/src/cpu/core_prefetch.cpp b/src/cpu/core_prefetch.cpp index e378968..f718cd5 100644 --- a/src/cpu/core_prefetch.cpp +++ b/src/cpu/core_prefetch.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -59,6 +59,8 @@ extern Bitu cycle_count; #define CPU_PIC_CHECK 1 #define CPU_TRAP_CHECK 1 +#define CPU_TRAP_DECODER CPU_Core_Prefetch_Trap_Run + #define OPCODE_NONE 0x000 #define OPCODE_0F 0x100 #define OPCODE_SIZE 0x200 diff --git a/src/cpu/core_simple.cpp b/src/cpu/core_simple.cpp index e751227..9f18c3b 100644 --- a/src/cpu/core_simple.cpp +++ b/src/cpu/core_simple.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include @@ -50,6 +50,8 @@ extern Bitu cycle_count; #define CPU_PIC_CHECK 1 #define CPU_TRAP_CHECK 1 +#define CPU_TRAP_DECODER CPU_Core_Simple_Trap_Run + #define OPCODE_NONE 0x000 #define OPCODE_0F 0x100 #define OPCODE_SIZE 0x200 @@ -185,13 +187,12 @@ decode_end: return CBRET_NONE; } -// not really used Bits CPU_Core_Simple_Trap_Run(void) { Bits oldCycles = CPU_Cycles; CPU_Cycles = 1; cpu.trap_skip = false; - Bits ret=CPU_Core_Normal_Run(); + Bits ret=CPU_Core_Simple_Run(); if (!cpu.trap_skip) CPU_HW_Interrupt(1); CPU_Cycles = oldCycles-1; cpudecoder = &CPU_Core_Simple_Run; diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index 535919e..c4d8c38 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -36,7 +36,7 @@ #include "support.h" Bitu DEBUG_EnableDebugger(void); -extern void GFX_SetTitle(Bit32s cycles ,Bits frameskip,bool paused); +extern void GFX_SetTitle(Bit32s cycles ,int frameskip,bool paused); #if 1 #undef LOG @@ -232,44 +232,48 @@ bool CPU_PUSHF(Bitu use32) { } void CPU_CheckSegments(void) { - bool needs_invalidation=false; + bool needs_invalidation = false; Descriptor desc; - if (!cpu.gdt.GetDescriptor(SegValue(es),desc)) needs_invalidation=true; + if (!cpu.gdt.GetDescriptor(SegValue(es),desc)) needs_invalidation = true; else switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: - case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: - if (cpu.cpl>desc.DPL()) needs_invalidation=true; break; + case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: + if (cpu.cpl > desc.DPL()) needs_invalidation = true; + break; default: break; } if (needs_invalidation) CPU_SetSegGeneral(es,0); - needs_invalidation=false; - if (!cpu.gdt.GetDescriptor(SegValue(ds),desc)) needs_invalidation=true; + needs_invalidation = false; + if (!cpu.gdt.GetDescriptor(SegValue(ds),desc)) needs_invalidation = true; else switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: - case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: - if (cpu.cpl>desc.DPL()) needs_invalidation=true; break; + case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: + if (cpu.cpl > desc.DPL()) needs_invalidation = true; + break; default: break; } if (needs_invalidation) CPU_SetSegGeneral(ds,0); - needs_invalidation=false; - if (!cpu.gdt.GetDescriptor(SegValue(fs),desc)) needs_invalidation=true; + needs_invalidation = false; + if (!cpu.gdt.GetDescriptor(SegValue(fs),desc)) needs_invalidation = true; else switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: - case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: - if (cpu.cpl>desc.DPL()) needs_invalidation=true; break; + case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: + if (cpu.cpl > desc.DPL()) needs_invalidation = true; + break; default: break; } if (needs_invalidation) CPU_SetSegGeneral(fs,0); - needs_invalidation=false; - if (!cpu.gdt.GetDescriptor(SegValue(gs),desc)) needs_invalidation=true; + needs_invalidation = false; + if (!cpu.gdt.GetDescriptor(SegValue(gs),desc)) needs_invalidation = true; else switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: - case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: - if (cpu.cpl>desc.DPL()) needs_invalidation=true; break; + case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: + if (cpu.cpl > desc.DPL()) needs_invalidation = true; + break; default: break; } if (needs_invalidation) CPU_SetSegGeneral(gs,0); } @@ -349,7 +353,7 @@ bool CPU_SwitchTask(Bitu new_tss_selector,TSwitchType tstype,Bitu old_eip) { FillFlags(); TaskStateSegment new_tss; if (!new_tss.SetSelector(new_tss_selector)) - E_Exit("Illegal TSS for switch, selector=%x, switchtype=%x",new_tss_selector,tstype); + E_Exit("Illegal TSS for switch, selector=%" sBitfs(x) ", switchtype=%x",new_tss_selector,tstype); if (tstype==TSwitch_IRET) { if (!new_tss.desc.IsBusy()) E_Exit("TSS not busy for IRET"); @@ -503,7 +507,7 @@ doconforming: Segs.val[cs]=new_cs; break; default: - E_Exit("Task switch CS Type %d",cs_desc.Type()); + E_Exit("Task switch CS Type %" sBitfs(u),cs_desc.Type()); } } CPU_SetSegGeneral(es,new_es); @@ -555,8 +559,8 @@ void CPU_Interrupt(Bitu num,Bitu type,Bitu oldeip) { case 0xcd: #if C_HEAVY_DEBUG LOG(LOG_CPU,LOG_ERROR)("Call to interrupt 0xCD this is BAD"); - DEBUG_HeavyWriteLogInstruction(); - E_Exit("Call to interrupt 0xCD this is BAD"); +// DEBUG_HeavyWriteLogInstruction(); +// E_Exit("Call to interrupt 0xCD this is BAD"); #endif break; case 0x03: @@ -725,7 +729,7 @@ do_interrupt: } break; default: - E_Exit("INT:Gate Selector points to illegal descriptor with type %x",cs_desc.Type()); + E_Exit("INT:Gate Selector points to illegal descriptor with type %" sBitfs(x),cs_desc.Type()); } Segs.val[cs]=(gate_sel&0xfffc) | cpu.cpl; @@ -755,7 +759,7 @@ do_interrupt: } return; default: - E_Exit("Illegal descriptor type %X for int %X",gate.Type(),num); + E_Exit("Illegal descriptor type %" sBitfs(X) " for int %" sBitfs(X),gate.Type(),num); } } assert(1); @@ -902,7 +906,7 @@ void CPU_IRET(bool use32,Bitu oldeip) { EXCEPTION_GP,n_cs_sel & 0xfffc) break; default: - E_Exit("IRET:Illegal descriptor type %X",n_cs_desc.Type()); + E_Exit("IRET:Illegal descriptor type %" sBitfs(X), n_cs_desc.Type()); } CPU_CHECK_COND(!n_cs_desc.saved.seg.p, "IRET with nonpresent code segment", @@ -1059,7 +1063,7 @@ CODE_jmp: CPU_SwitchTask(selector,TSwitch_JMP,oldeip); break; default: - E_Exit("JMP Illegal descriptor type %X",desc.Type()); + E_Exit("JMP Illegal descriptor type %" sBitfs(X),desc.Type()); } } assert(1); @@ -1295,7 +1299,7 @@ call_code: CPU_Exception(EXCEPTION_GP,selector & 0xfffc); return; default: - E_Exit("CALL:Descriptor type %x unsupported",call.Type()); + E_Exit("CALL:Descriptor type %" sBitfs(x) " unsupported",call.Type()); } } assert(1); @@ -1353,7 +1357,7 @@ void CPU_RET(bool use32,Bitu bytes,Bitu oldeip) { EXCEPTION_GP,selector & 0xfffc) break; default: - E_Exit("RET from illegal descriptor type %X",desc.Type()); + E_Exit("RET from illegal descriptor type %" sBitfs(X),desc.Type()); } RET_same_level: if (!desc.saved.seg.p) { @@ -1398,7 +1402,7 @@ RET_same_level: EXCEPTION_GP,selector & 0xfffc) break; default: - E_Exit("RET from illegal descriptor type %X",desc.Type()); // or #GP(selector) + E_Exit("RET from illegal descriptor type %" sBitfs(X),desc.Type()); // or #GP(selector) } CPU_CHECK_COND(!desc.saved.seg.p, @@ -1509,7 +1513,7 @@ bool CPU_LTR(Bitu selector) { LOG(LOG_CPU,LOG_ERROR)("LTR failed, selector=%X (not present)",selector); return CPU_PrepareException(EXCEPTION_NP,selector); } - if (!cpu_tss.SetSelector(selector)) E_Exit("LTR failed, selector=%X",selector); + if (!cpu_tss.SetSelector(selector)) E_Exit("LTR failed, selector=%" sBitfs(X),selector); cpu_tss.desc.SetBusy(true); cpu_tss.SaveSelector(); } else { @@ -1570,7 +1574,7 @@ void CPU_SET_CRX(Bitu cr,Bitu value) { GFX_SetTitle(CPU_CyclePercUsed,-1,false); if(!printed_cycles_auto_info) { printed_cycles_auto_info = true; - LOG_MSG("DOSBox switched to max cycles, because of the setting: cycles=auto. If the game runs too fast try a fixed cycles amount in DOSBox's options."); + LOG_MSG("DOSBox has switched to max cycles, because of the setting: cycles=auto.\nIf the game runs too fast, try a fixed cycles amount in DOSBox's options."); } } else { GFX_SetTitle(-1,-1,false); @@ -1925,17 +1929,17 @@ bool CPU_SetSegGeneral(SegNames seg,Bitu value) { if (seg==ss) { // Stack needs to be non-zero if ((value & 0xfffc)==0) { - E_Exit("CPU_SetSegGeneral: Stack segment zero"); -// return CPU_PrepareException(EXCEPTION_GP,0); +// E_Exit("CPU_SetSegGeneral: Stack segment zero"); + return CPU_PrepareException(EXCEPTION_GP,0); } Descriptor desc; if (!cpu.gdt.GetDescriptor(value,desc)) { - E_Exit("CPU_SetSegGeneral: Stack segment beyond limits"); -// return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc); +// E_Exit("CPU_SetSegGeneral: Stack segment beyond limits"); + return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc); } if (((value & 3)!=cpu.cpl) || (desc.DPL()!=cpu.cpl)) { - E_Exit("CPU_SetSegGeneral: Stack segment with invalid privileges"); -// return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc); +// E_Exit("CPU_SetSegGeneral: Stack segment with invalid privileges"); + return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc); } switch (desc.Type()) { @@ -2006,9 +2010,11 @@ bool CPU_SetSegGeneral(SegNames seg,Bitu value) { bool CPU_PopSeg(SegNames seg,bool use32) { Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask)); + Bitu addsp = use32 ? 0x04 : 0x02; + //Calcullate this beforehande since the stack mask might change + Bit32u new_esp = (reg_esp&cpu.stack.notmask) | ((reg_esp + addsp)&cpu.stack.mask); if (CPU_SetSegGeneral(seg,val)) return true; - Bitu addsp=use32?0x04:0x02; - reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+addsp)&cpu.stack.mask); + reg_esp = new_esp; return false; } diff --git a/src/cpu/flags.cpp b/src/cpu/flags.cpp index cd47f42..55c6fa8 100644 --- a/src/cpu/flags.cpp +++ b/src/cpu/flags.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* diff --git a/src/cpu/instructions.h b/src/cpu/instructions.h index 620d170..404ab68 100644 --- a/src/cpu/instructions.h +++ b/src/cpu/instructions.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Jumps */ @@ -228,14 +228,6 @@ #define ROLB(op1,op2,load,save) \ - if (!(op2&0x7)) { \ - if (op2&0x18) { \ - FillFlagsNoCFOF(); \ - SETFLAGBIT(CF,op1 & 1); \ - SETFLAGBIT(OF,(op1 & 1) ^ (op1 >> 7)); \ - } \ - break; \ - } \ FillFlagsNoCFOF(); \ lf_var1b=load(op1); \ lf_var2b=op2&0x07; \ @@ -246,14 +238,6 @@ SETFLAGBIT(OF,(lf_resb & 1) ^ (lf_resb >> 7)); #define ROLW(op1,op2,load,save) \ - if (!(op2&0xf)) { \ - if (op2&0x10) { \ - FillFlagsNoCFOF(); \ - SETFLAGBIT(CF,op1 & 1); \ - SETFLAGBIT(OF,(op1 & 1) ^ (op1 >> 15)); \ - } \ - break; \ - } \ FillFlagsNoCFOF(); \ lf_var1w=load(op1); \ lf_var2b=op2&0xf; \ @@ -264,7 +248,6 @@ SETFLAGBIT(OF,(lf_resw & 1) ^ (lf_resw >> 15)); #define ROLD(op1,op2,load,save) \ - if (!op2) break; \ FillFlagsNoCFOF(); \ lf_var1d=load(op1); \ lf_var2b=op2; \ @@ -276,14 +259,6 @@ #define RORB(op1,op2,load,save) \ - if (!(op2&0x7)) { \ - if (op2&0x18) { \ - FillFlagsNoCFOF(); \ - SETFLAGBIT(CF,op1>>7); \ - SETFLAGBIT(OF,(op1>>7) ^ ((op1>>6) & 1)); \ - } \ - break; \ - } \ FillFlagsNoCFOF(); \ lf_var1b=load(op1); \ lf_var2b=op2&0x07; \ @@ -294,14 +269,6 @@ SETFLAGBIT(OF,(lf_resb ^ (lf_resb<<1)) & 0x80); #define RORW(op1,op2,load,save) \ - if (!(op2&0xf)) { \ - if (op2&0x10) { \ - FillFlagsNoCFOF(); \ - SETFLAGBIT(CF,op1>>15); \ - SETFLAGBIT(OF,(op1>>15) ^ ((op1>>14) & 1)); \ - } \ - break; \ - } \ FillFlagsNoCFOF(); \ lf_var1w=load(op1); \ lf_var2b=op2&0xf; \ @@ -312,7 +279,6 @@ SETFLAGBIT(OF,(lf_resw ^ (lf_resw<<1)) & 0x8000); #define RORD(op1,op2,load,save) \ - if (!op2) break; \ FillFlagsNoCFOF(); \ lf_var1d=load(op1); \ lf_var2b=op2; \ @@ -366,8 +332,6 @@ SETFLAGBIT(OF,(reg_flags & 1) ^ (lf_resd >> 31)); \ } - - #define RCRB(op1,op2,load,save) \ if (op2%9) { \ Bit8u cf=(Bit8u)FillFlags()&0x1; \ @@ -413,21 +377,18 @@ #define SHLB(op1,op2,load,save) \ - if (!op2) break; \ lf_var1b=load(op1);lf_var2b=op2; \ lf_resb=lf_var1b << lf_var2b; \ save(op1,lf_resb); \ lflags.type=t_SHLb; #define SHLW(op1,op2,load,save) \ - if (!op2) break; \ lf_var1w=load(op1);lf_var2b=op2 ; \ lf_resw=lf_var1w << lf_var2b; \ save(op1,lf_resw); \ lflags.type=t_SHLw; #define SHLD(op1,op2,load,save) \ - if (!op2) break; \ lf_var1d=load(op1);lf_var2b=op2; \ lf_resd=lf_var1d << lf_var2b; \ save(op1,lf_resd); \ @@ -435,21 +396,18 @@ #define SHRB(op1,op2,load,save) \ - if (!op2) break; \ lf_var1b=load(op1);lf_var2b=op2; \ lf_resb=lf_var1b >> lf_var2b; \ save(op1,lf_resb); \ lflags.type=t_SHRb; #define SHRW(op1,op2,load,save) \ - if (!op2) break; \ lf_var1w=load(op1);lf_var2b=op2; \ lf_resw=lf_var1w >> lf_var2b; \ save(op1,lf_resw); \ lflags.type=t_SHRw; #define SHRD(op1,op2,load,save) \ - if (!op2) break; \ lf_var1d=load(op1);lf_var2b=op2; \ lf_resd=lf_var1d >> lf_var2b; \ save(op1,lf_resd); \ @@ -457,7 +415,6 @@ #define SARB(op1,op2,load,save) \ - if (!op2) break; \ lf_var1b=load(op1);lf_var2b=op2; \ if (lf_var2b>8) lf_var2b=8; \ if (lf_var1b & 0x80) { \ @@ -470,7 +427,6 @@ lflags.type=t_SARb; #define SARW(op1,op2,load,save) \ - if (!op2) break; \ lf_var1w=load(op1);lf_var2b=op2; \ if (lf_var2b>16) lf_var2b=16; \ if (lf_var1w & 0x8000) { \ @@ -483,7 +439,6 @@ lflags.type=t_SARw; #define SARD(op1,op2,load,save) \ - if (!op2) break; \ lf_var2b=op2;lf_var1d=load(op1); \ if (lf_var1d & 0x80000000) { \ lf_resd=(lf_var1d >> lf_var2b)| \ @@ -679,6 +634,7 @@ if (quo>0xff) EXCEPTION(0); \ reg_ah=rem; \ reg_al=quo8; \ + SETFLAGBIT(OF,false); \ } @@ -693,6 +649,7 @@ if (quo!=(Bit32u)quo16) EXCEPTION(0); \ reg_dx=rem; \ reg_ax=quo16; \ + SETFLAGBIT(OF,false); \ } #define DIVD(op1,load,save) \ @@ -706,6 +663,7 @@ if (quo!=(Bit64u)quo32) EXCEPTION(0); \ reg_edx=rem; \ reg_eax=quo32; \ + SETFLAGBIT(OF,false); \ } @@ -719,6 +677,7 @@ if (quo!=(Bit16s)quo8s) EXCEPTION(0); \ reg_ah=rem; \ reg_al=quo8s; \ + SETFLAGBIT(OF,false); \ } @@ -733,6 +692,7 @@ if (quo!=(Bit32s)quo16s) EXCEPTION(0); \ reg_dx=rem; \ reg_ax=quo16s; \ + SETFLAGBIT(OF,false); \ } #define IDIVD(op1,load,save) \ @@ -746,12 +706,15 @@ if (quo!=(Bit64s)quo32s) EXCEPTION(0); \ reg_edx=rem; \ reg_eax=quo32s; \ + SETFLAGBIT(OF,false); \ } #define IMULB(op1,load,save) \ { \ reg_ax=((Bit8s)reg_al) * ((Bit8s)(load(op1))); \ FillFlagsNoCFOF(); \ + SETFLAGBIT(ZF,reg_al == 0); \ + SETFLAGBIT(SF,reg_al & 0x80); \ if ((reg_ax & 0xff80)==0xff80 || \ (reg_ax & 0xff80)==0x0000) { \ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \ @@ -767,6 +730,8 @@ reg_ax=(Bit16s)(temps); \ reg_dx=(Bit16s)(temps >> 16); \ FillFlagsNoCFOF(); \ + SETFLAGBIT(ZF,reg_ax == 0); \ + SETFLAGBIT(SF,reg_ax & 0x8000); \ if (((temps & 0xffff8000)==0xffff8000 || \ (temps & 0xffff8000)==0x0000)) { \ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \ @@ -782,6 +747,8 @@ reg_eax=(Bit32u)(temps); \ reg_edx=(Bit32u)(temps >> 32); \ FillFlagsNoCFOF(); \ + SETFLAGBIT(ZF,reg_eax == 0); \ + SETFLAGBIT(SF,reg_eax & 0x80000000); \ if ((reg_edx==0xffffffff) && \ (reg_eax & 0x80000000) ) { \ SETFLAGBIT(CF,false);SETFLAGBIT(OF,false); \ @@ -824,6 +791,7 @@ if (rm >= 0xc0) { \ GetEArb; \ Bit8u val=blah & 0x1f; \ + if (!val) break; \ switch (which) { \ case 0x00:ROLB(*earb,val,LoadRb,SaveRb);break; \ case 0x01:RORB(*earb,val,LoadRb,SaveRb);break; \ @@ -837,6 +805,7 @@ } else { \ GetEAa; \ Bit8u val=blah & 0x1f; \ + if (!val) break; \ switch (which) { \ case 0x00:ROLB(eaa,val,LoadMb,SaveMb);break; \ case 0x01:RORB(eaa,val,LoadMb,SaveMb);break; \ @@ -858,6 +827,7 @@ if (rm >= 0xc0) { \ GetEArw; \ Bit8u val=blah & 0x1f; \ + if (!val) break; \ switch (which) { \ case 0x00:ROLW(*earw,val,LoadRw,SaveRw);break; \ case 0x01:RORW(*earw,val,LoadRw,SaveRw);break; \ @@ -871,6 +841,7 @@ } else { \ GetEAa; \ Bit8u val=blah & 0x1f; \ + if (!val) break; \ switch (which) { \ case 0x00:ROLW(eaa,val,LoadMw,SaveMw);break; \ case 0x01:RORW(eaa,val,LoadMw,SaveMw);break; \ @@ -891,6 +862,7 @@ if (rm >= 0xc0) { \ GetEArd; \ Bit8u val=blah & 0x1f; \ + if (!val) break; \ switch (which) { \ case 0x00:ROLD(*eard,val,LoadRd,SaveRd);break; \ case 0x01:RORD(*eard,val,LoadRd,SaveRd);break; \ @@ -904,6 +876,7 @@ } else { \ GetEAa; \ Bit8u val=blah & 0x1f; \ + if (!val) break; \ switch (which) { \ case 0x00:ROLD(eaa,val,LoadMd,SaveMd);break; \ case 0x01:RORD(eaa,val,LoadMd,SaveMd);break; \ diff --git a/src/cpu/lazyflags.h b/src/cpu/lazyflags.h index 49855f7..c110681 100644 --- a/src/cpu/lazyflags.h +++ b/src/cpu/lazyflags.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,13 +11,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#if !defined __LAZYFLAGS_H -#define __LAZYFLAG_H +#ifndef DOSBOX_LAZYFLAGS_H +#define DOSBOX_LAZYFLAGS_H //Flag Handling Bit32u get_CF(void); @@ -31,7 +31,9 @@ Bitu FillFlags(void); void FillFlagsNoCFOF(void); void DestroyConditionFlags(void); +#ifndef DOSBOX_REGS_H #include "regs.h" +#endif struct LazyFlags { GenReg32 var1,var2,res; diff --git a/src/cpu/modrm.cpp b/src/cpu/modrm.cpp index c5e4e92..79538bc 100644 --- a/src/cpu/modrm.cpp +++ b/src/cpu/modrm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "cpu.h" diff --git a/src/cpu/modrm.h b/src/cpu/modrm.h index 1f4d6d5..f6d1a2a 100644 --- a/src/cpu/modrm.h +++ b/src/cpu/modrm.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern Bit8u * lookupRMregb[]; diff --git a/src/cpu/paging.cpp b/src/cpu/paging.cpp index 6ac9b66..b91cdfa 100644 --- a/src/cpu/paging.cpp +++ b/src/cpu/paging.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/debug/debug.cpp b/src/debug/debug.cpp index 7b76d57..217710d 100644 --- a/src/debug/debug.cpp +++ b/src/debug/debug.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -61,8 +62,8 @@ int old_cursor_state; // Forwards static void DrawCode(void); static void DEBUG_RaiseTimerIrq(void); -static void SaveMemory(Bitu seg, Bitu ofs1, Bit32u num); -static void SaveMemoryBin(Bitu seg, Bitu ofs1, Bit32u num); +static void SaveMemory(Bit16u seg, Bit32u ofs1, Bit32u num); +static void SaveMemoryBin(Bit16u seg, Bit32u ofs1, Bit32u num); static void LogMCBS(void); static void LogGDT(void); static void LogLDT(void); @@ -244,26 +245,31 @@ bool GetDescriptorInfo(char* selname, char* out1, char* out2) class CDebugVar { public: - CDebugVar(char* _name, PhysPt _adr) { adr=_adr; safe_strncpy(name,_name,16); }; + CDebugVar(char* _name, PhysPt _adr) { adr=_adr; safe_strncpy(name,_name,16); hasvalue = false; value = 0; }; - char* GetName(void) { return name; }; - PhysPt GetAdr (void) { return adr; }; + char* GetName (void) { return name; }; + PhysPt GetAdr (void) { return adr; }; + void SetValue(bool has, Bit16u val) { hasvalue = has; value=val; }; + Bit16u GetValue(void) { return value; }; + bool HasValue(void) { return hasvalue; }; private: PhysPt adr; - char name[16]; + char name[16]; + bool hasvalue; + Bit16u value; public: - static void InsertVariable (char* name, PhysPt adr); - static CDebugVar* FindVar (PhysPt adr); - static void DeleteAll (); - static bool SaveVars (char* name); - static bool LoadVars (char* name); + static void InsertVariable(char* name, PhysPt adr); + static CDebugVar* FindVar (PhysPt adr); + static void DeleteAll (); + static bool SaveVars (char* name); + static bool LoadVars (char* name); - static std::list varList; + static std::vector varList; }; -std::list CDebugVar::varList; +std::vector CDebugVar::varList; /********************/ @@ -281,35 +287,40 @@ class CBreakpoint public: CBreakpoint(void); - void SetAddress (Bit16u seg, Bit32u off) { location = GetAddress(seg,off); type = BKPNT_PHYSICAL; segment = seg; offset = off; }; - void SetAddress (PhysPt adr) { location = adr; type = BKPNT_PHYSICAL; }; - void SetInt (Bit8u _intNr, Bit16u ah) { intNr = _intNr, ahValue = ah; type = BKPNT_INTERRUPT; }; + void SetAddress (Bit16u seg, Bit32u off) { location = GetAddress(seg,off); type = BKPNT_PHYSICAL; segment = seg; offset = off; }; + void SetAddress (PhysPt adr) { location = adr; type = BKPNT_PHYSICAL; }; + void SetInt (Bit8u _intNr, Bit16u ah, Bit16u al) { intNr = _intNr, ahValue = ah; alValue = al; type = BKPNT_INTERRUPT; }; void SetOnce (bool _once) { once = _once; }; void SetType (EBreakpoint _type) { type = _type; }; void SetValue (Bit8u value) { ahValue = value; }; + void SetOther (Bit8u other) { alValue = other; }; bool IsActive (void) { return active; }; void Activate (bool _active); EBreakpoint GetType (void) { return type; }; bool GetOnce (void) { return once; }; - PhysPt GetLocation (void) { if (GetType()!=BKPNT_INTERRUPT) return location; else return 0; }; + PhysPt GetLocation (void) { return location; }; Bit16u GetSegment (void) { return segment; }; Bit32u GetOffset (void) { return offset; }; - Bit8u GetIntNr (void) { if (GetType()==BKPNT_INTERRUPT) return intNr; else return 0; }; - Bit16u GetValue (void) { if (GetType()!=BKPNT_PHYSICAL) return ahValue; else return 0; }; + Bit8u GetIntNr (void) { return intNr; }; + Bit16u GetValue (void) { return ahValue; }; + Bit16u GetOther (void) { return alValue; }; // statics static CBreakpoint* AddBreakpoint (Bit16u seg, Bit32u off, bool once); - static CBreakpoint* AddIntBreakpoint (Bit8u intNum, Bit16u ah, bool once); + static CBreakpoint* AddIntBreakpoint (Bit8u intNum, Bit16u ah, Bit16u al, bool once); static CBreakpoint* AddMemBreakpoint (Bit16u seg, Bit32u off); - static void ActivateBreakpoints (PhysPt adr, bool activate); + static void DeactivateBreakpoints(); + static void ActivateBreakpoints (); + static void ActivateBreakpointsExceptAt(PhysPt adr); static bool CheckBreakpoint (PhysPt adr); static bool CheckBreakpoint (Bitu seg, Bitu off); - static bool CheckIntBreakpoint (PhysPt adr, Bit8u intNr, Bit16u ahValue); - static bool IsBreakpoint (PhysPt where); - static bool IsBreakpointDrawn (PhysPt where); - static bool DeleteBreakpoint (PhysPt where); + static bool CheckIntBreakpoint (PhysPt adr, Bit8u intNr, Bit16u ahValue, Bit16u alValue); + static CBreakpoint* FindPhysBreakpoint (Bit16u seg, Bit32u off, bool once); + static CBreakpoint* FindOtherActiveBreakpoint(PhysPt adr, CBreakpoint* skip); + static bool IsBreakpoint (Bit16u seg, Bit32u off); + static bool DeleteBreakpoint (Bit16u seg, Bit32u off); static bool DeleteByIndex (Bit16u index); static void DeleteAll (void); static void ShowList (void); @@ -325,36 +336,53 @@ private: // Int Bit8u intNr; Bit16u ahValue; + Bit16u alValue; // Shared bool active; bool once; static std::list BPoints; -public: - static CBreakpoint* ignoreOnce; }; CBreakpoint::CBreakpoint(void): -location(0), +location(0),oldData(0xCC), active(false),once(false), -segment(0),offset(0),intNr(0),ahValue(0), +segment(0),offset(0),intNr(0),ahValue(0),alValue(0), type(BKPNT_UNKNOWN) { }; void CBreakpoint::Activate(bool _active) { #if !C_HEAVY_DEBUG - if (GetType()==BKPNT_PHYSICAL) { + if (GetType() == BKPNT_PHYSICAL) { if (_active) { // Set 0xCC and save old value Bit8u data = mem_readb(location); - if (data!=0xCC) { + if (data != 0xCC) { oldData = data; mem_writeb(location,0xCC); + } else if (!active) { + // Another activate breakpoint is already here. + // Find it, and copy its oldData value + CBreakpoint *bp = FindOtherActiveBreakpoint(location, this); + + if (!bp || bp->oldData == 0xCC) { + // This might also happen if there is a real 0xCC instruction here + DEBUG_ShowMsg("DEBUG: Internal error while activating breakpoint.\n"); + oldData = 0xCC; + } else + oldData = bp->oldData; }; } else { - // Remove 0xCC and set old value - if (mem_readb (location)==0xCC) { - mem_writeb(location,oldData); + if (mem_readb(location) == 0xCC) { + if (oldData == 0xCC) + DEBUG_ShowMsg("DEBUG: Internal error while deactivating breakpoint.\n"); + + // Check if we are the last active breakpoint at this location + bool otherActive = (FindOtherActiveBreakpoint(location, this) != 0); + + // If so, remove 0xCC and set old value + if (!otherActive) + mem_writeb(location, oldData); }; } } @@ -364,8 +392,6 @@ void CBreakpoint::Activate(bool _active) // Statics std::list CBreakpoint::BPoints; -CBreakpoint* CBreakpoint::ignoreOnce = 0; -Bitu ignoreAddressOnce = 0; CBreakpoint* CBreakpoint::AddBreakpoint(Bit16u seg, Bit32u off, bool once) { @@ -376,10 +402,10 @@ CBreakpoint* CBreakpoint::AddBreakpoint(Bit16u seg, Bit32u off, bool once) return bp; }; -CBreakpoint* CBreakpoint::AddIntBreakpoint(Bit8u intNum, Bit16u ah, bool once) +CBreakpoint* CBreakpoint::AddIntBreakpoint(Bit8u intNum, Bit16u ah, Bit16u al, bool once) { CBreakpoint* bp = new CBreakpoint(); - bp->SetInt (intNum,ah); + bp->SetInt (intNum,ah,al); bp->SetOnce (once); BPoints.push_front (bp); return bp; @@ -395,43 +421,47 @@ CBreakpoint* CBreakpoint::AddMemBreakpoint(Bit16u seg, Bit32u off) return bp; }; -void CBreakpoint::ActivateBreakpoints(PhysPt adr, bool activate) +void CBreakpoint::ActivateBreakpoints() { // activate all breakpoints std::list::iterator i; - CBreakpoint* bp; - for(i=BPoints.begin(); i != BPoints.end(); i++) { - bp = (*i); - // Do not activate, when bp is an actual address - if (activate && (bp->GetType()==BKPNT_PHYSICAL) && (bp->GetLocation()==adr)) { - // Do not activate :) + for (i = BPoints.begin(); i != BPoints.end(); ++i) + (*i)->Activate(true); +} + +void CBreakpoint::DeactivateBreakpoints() +{ + // deactivate all breakpoints + std::list::iterator i; + for (i = BPoints.begin(); i != BPoints.end(); ++i) + (*i)->Activate(false); +} + +void CBreakpoint::ActivateBreakpointsExceptAt(PhysPt adr) +{ + // activate all breakpoints, except those at adr + std::list::iterator i; + for (i = BPoints.begin(); i != BPoints.end(); ++i) { + CBreakpoint* bp = (*i); + // Do not activate breakpoints at adr + if (bp->GetType() == BKPNT_PHYSICAL && bp->GetLocation() == adr) continue; - } - bp->Activate(activate); + bp->Activate(true); }; }; bool CBreakpoint::CheckBreakpoint(Bitu seg, Bitu off) // Checks if breakpoint is valid and should stop execution { - if ((ignoreAddressOnce!=0) && (GetAddress(seg,off)==ignoreAddressOnce)) { - ignoreAddressOnce = 0; - return false; - } else - ignoreAddressOnce = 0; + // Quick exit if there are no breakpoints + if (BPoints.empty()) return false; // Search matching breakpoint std::list::iterator i; CBreakpoint* bp; - for(i=BPoints.begin(); i != BPoints.end(); i++) { + for(i=BPoints.begin(); i != BPoints.end(); ++i) { bp = (*i); if ((bp->GetType()==BKPNT_PHYSICAL) && bp->IsActive() && (bp->GetSegment()==seg) && (bp->GetOffset()==off)) { - // Ignore Once ? - if (ignoreOnce==bp) { - ignoreOnce=0; - bp->Activate(true); - return false; - }; // Found, if (bp->GetOnce()) { // delete it, if it should only be used once @@ -439,8 +469,14 @@ bool CBreakpoint::CheckBreakpoint(Bitu seg, Bitu off) bp->Activate(false); delete bp; } else { - ignoreOnce = bp; - }; + // Also look for once-only breakpoints at this address + bp = FindPhysBreakpoint(seg, off, true); + if (bp) { + BPoints.remove(bp); + bp->Activate(false); + delete bp; + } + } return true; } #if C_HEAVY_DEBUG @@ -475,36 +511,25 @@ bool CBreakpoint::CheckBreakpoint(Bitu seg, Bitu off) return false; }; -bool CBreakpoint::CheckIntBreakpoint(PhysPt adr, Bit8u intNr, Bit16u ahValue) +bool CBreakpoint::CheckIntBreakpoint(PhysPt adr, Bit8u intNr, Bit16u ahValue, Bit16u alValue) // Checks if interrupt breakpoint is valid and should stop execution { - if ((ignoreAddressOnce!=0) && (adr==ignoreAddressOnce)) { - ignoreAddressOnce = 0; - return false; - } else - ignoreAddressOnce = 0; + if (BPoints.empty()) return false; // Search matching breakpoint std::list::iterator i; CBreakpoint* bp; - for(i=BPoints.begin(); i != BPoints.end(); i++) { + for(i=BPoints.begin(); i != BPoints.end(); ++i) { bp = (*i); if ((bp->GetType()==BKPNT_INTERRUPT) && bp->IsActive() && (bp->GetIntNr()==intNr)) { - if ((bp->GetValue()==BPINT_ALL) || (bp->GetValue()==ahValue)) { + if (((bp->GetValue()==BPINT_ALL) || (bp->GetValue()==ahValue)) && ((bp->GetOther()==BPINT_ALL) || (bp->GetOther()==alValue))) { // Ignore it once ? - if (ignoreOnce==bp) { - ignoreOnce=0; - bp->Activate(true); - return false; - }; // Found if (bp->GetOnce()) { // delete it, if it should only be used once (BPoints.erase)(i); bp->Activate(false); delete bp; - } else { - ignoreOnce = bp; } return true; } @@ -517,7 +542,7 @@ void CBreakpoint::DeleteAll() { std::list::iterator i; CBreakpoint* bp; - for(i=BPoints.begin(); i != BPoints.end(); i++) { + for(i=BPoints.begin(); i != BPoints.end(); ++i) { bp = (*i); bp->Activate(false); delete bp; @@ -532,7 +557,7 @@ bool CBreakpoint::DeleteByIndex(Bit16u index) int nr = 0; std::list::iterator i; CBreakpoint* bp; - for(i=BPoints.begin(); i != BPoints.end(); i++) { + for(i=BPoints.begin(); i != BPoints.end(); ++i) { if (nr==index) { bp = (*i); (BPoints.erase)(i); @@ -545,69 +570,75 @@ bool CBreakpoint::DeleteByIndex(Bit16u index) return false; }; -bool CBreakpoint::DeleteBreakpoint(PhysPt where) +CBreakpoint* CBreakpoint::FindPhysBreakpoint(Bit16u seg, Bit32u off, bool once) { - // Search matching breakpoint + if (BPoints.empty()) return 0; +#if !C_HEAVY_DEBUG + PhysPt adr = GetAddress(seg, off); +#endif + // Search for matching breakpoint std::list::iterator i; CBreakpoint* bp; - for(i=BPoints.begin(); i != BPoints.end(); i++) { + for(i=BPoints.begin(); i != BPoints.end(); ++i) { bp = (*i); - if ((bp->GetType()==BKPNT_PHYSICAL) && (bp->GetLocation()==where)) { - (BPoints.erase)(i); - bp->Activate(false); - delete bp; - return true; - } - }; - return false; -}; +#if C_HEAVY_DEBUG + // Heavy debugging breakpoints are triggered by matching seg:off + bool atLocation = bp->GetSegment() == seg && bp->GetOffset() == off; +#else + // Normal debugging breakpoints are triggered at an address + bool atLocation = bp->GetLocation() == adr; +#endif -bool CBreakpoint::IsBreakpoint(PhysPt adr) -// is there a breakpoint at address ? -{ - // Search matching breakpoint - std::list::iterator i; - CBreakpoint* bp; - for(i=BPoints.begin(); i != BPoints.end(); i++) { - bp = (*i); - if ((bp->GetType()==BKPNT_PHYSICAL) && (bp->GetSegment()==adr)) { - return true; - }; - if ((bp->GetType()==BKPNT_PHYSICAL) && (bp->GetLocation()==adr)) { - return true; - }; - }; - return false; -}; + if (bp->GetType() == BKPNT_PHYSICAL && atLocation && bp->GetOnce() == once) + return bp; + } -bool CBreakpoint::IsBreakpointDrawn(PhysPt adr) -// valid breakpoint, that should be drawn ? + return 0; +} + +CBreakpoint* CBreakpoint::FindOtherActiveBreakpoint(PhysPt adr, CBreakpoint* skip) { - // Search matching breakpoint std::list::iterator i; - CBreakpoint* bp; - for(i=BPoints.begin(); i != BPoints.end(); i++) { - bp = (*i); - if ((bp->GetType()==BKPNT_PHYSICAL) && (bp->GetLocation()==adr)) { - // Only draw, if breakpoint is not only once, - return !bp->GetOnce(); - }; - }; + for (i = BPoints.begin(); i != BPoints.end(); ++i) { + CBreakpoint* bp = (*i); + if (bp != skip && bp->GetType() == BKPNT_PHYSICAL && bp->GetLocation() == adr && bp->IsActive()) + return bp; + } + return 0; +} + +// is there a permanent breakpoint at address ? +bool CBreakpoint::IsBreakpoint(Bit16u seg, Bit32u off) +{ + return FindPhysBreakpoint(seg, off, false) != 0; +} + +bool CBreakpoint::DeleteBreakpoint(Bit16u seg, Bit32u off) +{ + CBreakpoint* bp = FindPhysBreakpoint(seg, off, false); + if (bp) { + BPoints.remove(bp); + delete bp; + return true; + } + return false; -}; +} + void CBreakpoint::ShowList(void) { // iterate list int nr = 0; std::list::iterator i; - for(i=BPoints.begin(); i != BPoints.end(); i++) { + for(i=BPoints.begin(); i != BPoints.end(); ++i) { CBreakpoint* bp = (*i); if (bp->GetType()==BKPNT_PHYSICAL) { DEBUG_ShowMsg("%02X. BP %04X:%04X\n",nr,bp->GetSegment(),bp->GetOffset()); } else if (bp->GetType()==BKPNT_INTERRUPT) { - if (bp->GetValue()==BPINT_ALL) DEBUG_ShowMsg("%02X. BPINT %02X\n",nr,bp->GetIntNr()); - else DEBUG_ShowMsg("%02X. BPINT %02X AH=%02X\n",nr,bp->GetIntNr(),bp->GetValue()); + if (bp->GetValue()==BPINT_ALL) DEBUG_ShowMsg("%02X. BPINT %02X\n",nr,bp->GetIntNr()); + else if (bp->GetOther()==BPINT_ALL) DEBUG_ShowMsg("%02X. BPINT %02X AH=%02X\n",nr,bp->GetIntNr(),bp->GetValue()); + else DEBUG_ShowMsg("%02X. BPINT %02X AH=%02X AL=%02X\n",nr,bp->GetIntNr(),bp->GetValue(),bp->GetOther()); } else if (bp->GetType()==BKPNT_MEMORY) { DEBUG_ShowMsg("%02X. BPMEM %04X:%04X (%02X)\n",nr,bp->GetSegment(),bp->GetOffset(),bp->GetValue()); } else if (bp->GetType()==BKPNT_MEMORY_PROT) { @@ -621,21 +652,21 @@ void CBreakpoint::ShowList(void) bool DEBUG_Breakpoint(void) { - /* First get the phyiscal address and check for a set Breakpoint */ + /* First get the physical address and check for a set Breakpoint */ if (!CBreakpoint::CheckBreakpoint(SegValue(cs),reg_eip)) return false; // Found. Breakpoint is valid PhysPt where=GetAddress(SegValue(cs),reg_eip); - CBreakpoint::ActivateBreakpoints(where,false); // Deactivate all breakpoints + CBreakpoint::DeactivateBreakpoints(); // Deactivate all breakpoints return true; }; bool DEBUG_IntBreakpoint(Bit8u intNum) { - /* First get the phyiscal address and check for a set Breakpoint */ + /* First get the physical address and check for a set Breakpoint */ PhysPt where=GetAddress(SegValue(cs),reg_eip); - if (!CBreakpoint::CheckIntBreakpoint(where,intNum,reg_ah)) return false; + if (!CBreakpoint::CheckIntBreakpoint(where,intNum,reg_ah,reg_al)) return false; // Found. Breakpoint is valid - CBreakpoint::ActivateBreakpoints(where,false); // Deactivate all breakpoints + CBreakpoint::DeactivateBreakpoints(); // Deactivate all breakpoints return true; }; @@ -647,11 +678,11 @@ static bool StepOver() size=DasmI386(dline, start, reg_eip, cpu.code.big); if (strstr(dline,"call") || strstr(dline,"int") || strstr(dline,"loop") || strstr(dline,"rep")) { - CBreakpoint::AddBreakpoint (SegValue(cs),reg_eip+size, true); - CBreakpoint::ActivateBreakpoints(start, true); + // Don't add a temporary breakpoint if there's already one here + if (!CBreakpoint::FindPhysBreakpoint(SegValue(cs), reg_eip+size, true)) + CBreakpoint::AddBreakpoint(SegValue(cs),reg_eip+size, true); debugging=false; DrawCode(); - DOSBOX_SetNormalLoop(); return true; } return false; @@ -682,14 +713,14 @@ static void DrawData(void) { /* Data win */ for (int y=0; y<8; y++) { // Address - if (add<0x10000) mvwprintw (dbg.win_data,1+y,0,"%04X:%04X ",dataSeg,add); - else mvwprintw (dbg.win_data,1+y,0,"%04X:%08X ",dataSeg,add); + if (add<0x10000) mvwprintw (dbg.win_data,y,0,"%04X:%04X ",dataSeg,add); + else mvwprintw (dbg.win_data,y,0,"%04X:%08X ",dataSeg,add); for (int x=0; x<16; x++) { address = GetAddress(dataSeg,add); if (mem_readb_checked(address,&ch)) ch=0; - mvwprintw (dbg.win_data,1+y,14+3*x,"%02X",ch); + mvwprintw (dbg.win_data,y,14+3*x,"%02X",ch); if (ch<32 || !isprint(*reinterpret_cast(&ch))) ch='.'; - mvwprintw (dbg.win_data,1+y,63+x,"%c",ch); + mvwprintw (dbg.win_data,y,63+x,"%c",ch); add++; }; } @@ -794,7 +825,7 @@ static void DrawCode(void) { codeViewData.cursorSeg = codeViewData.useCS; codeViewData.cursorOfs = disEIP; saveSel = true; - } else if (CBreakpoint::IsBreakpointDrawn(start)) { + } else if (CBreakpoint::IsBreakpoint(codeViewData.useCS, disEIP)) { wattrset(dbg.win_code,COLOR_PAIR(PAIR_GREY_RED)); } else { wattrset(dbg.win_code,0); @@ -845,8 +876,9 @@ static void DrawCode(void) { codeViewData.useEIPlast = disEIP; - wattrset(dbg.win_code,0); + wattrset(dbg.win_code,0); if (!debugging) { + if (has_colors()) wattrset(dbg.win_code,COLOR_PAIR(PAIR_GREEN_BLACK)); mvwprintw(dbg.win_code,10,0,"%s","(Running)"); wclrtoeol(dbg.win_code); } else { @@ -856,11 +888,13 @@ static void DrawCode(void) { mvwprintw(dbg.win_code,10,0,"%c-> %s%c", (codeViewData.ovrMode?'O':'I'),dispPtr,(*curPtr?' ':'_')); wclrtoeol(dbg.win_code); // not correct in pdcurses if full line + mvwchgat(dbg.win_code,10,0,3,0,(PAIR_BLACK_GREY),NULL); if (*curPtr) { mvwchgat(dbg.win_code,10,(curPtr-dispPtr+4),1,0,(PAIR_BLACK_GREY),NULL); } } + wattrset(dbg.win_code,0); wrefresh(dbg.win_code); } @@ -887,39 +921,39 @@ Bit32u GetHexValue(char* str, char*& hex) Bit32u value = 0; Bit32u regval = 0; hex = str; - while (*hex==' ') hex++; - if (strstr(hex,"EAX")==hex) { hex+=3; regval = reg_eax; }; - if (strstr(hex,"EBX")==hex) { hex+=3; regval = reg_ebx; }; - if (strstr(hex,"ECX")==hex) { hex+=3; regval = reg_ecx; }; - if (strstr(hex,"EDX")==hex) { hex+=3; regval = reg_edx; }; - if (strstr(hex,"ESI")==hex) { hex+=3; regval = reg_esi; }; - if (strstr(hex,"EDI")==hex) { hex+=3; regval = reg_edi; }; - if (strstr(hex,"EBP")==hex) { hex+=3; regval = reg_ebp; }; - if (strstr(hex,"ESP")==hex) { hex+=3; regval = reg_esp; }; - if (strstr(hex,"EIP")==hex) { hex+=3; regval = reg_eip; }; - if (strstr(hex,"AX")==hex) { hex+=2; regval = reg_ax; }; - if (strstr(hex,"BX")==hex) { hex+=2; regval = reg_bx; }; - if (strstr(hex,"CX")==hex) { hex+=2; regval = reg_cx; }; - if (strstr(hex,"DX")==hex) { hex+=2; regval = reg_dx; }; - if (strstr(hex,"SI")==hex) { hex+=2; regval = reg_si; }; - if (strstr(hex,"DI")==hex) { hex+=2; regval = reg_di; }; - if (strstr(hex,"BP")==hex) { hex+=2; regval = reg_bp; }; - if (strstr(hex,"SP")==hex) { hex+=2; regval = reg_sp; }; - if (strstr(hex,"IP")==hex) { hex+=2; regval = reg_ip; }; - if (strstr(hex,"CS")==hex) { hex+=2; regval = SegValue(cs); }; - if (strstr(hex,"DS")==hex) { hex+=2; regval = SegValue(ds); }; - if (strstr(hex,"ES")==hex) { hex+=2; regval = SegValue(es); }; - if (strstr(hex,"FS")==hex) { hex+=2; regval = SegValue(fs); }; - if (strstr(hex,"GS")==hex) { hex+=2; regval = SegValue(gs); }; - if (strstr(hex,"SS")==hex) { hex+=2; regval = SegValue(ss); }; + while (*hex == ' ') hex++; + if (strncmp(hex,"EAX",3) == 0) { hex+=3; regval = reg_eax; } else + if (strncmp(hex,"EBX",3) == 0) { hex+=3; regval = reg_ebx; } else + if (strncmp(hex,"ECX",3) == 0) { hex+=3; regval = reg_ecx; } else + if (strncmp(hex,"EDX",3) == 0) { hex+=3; regval = reg_edx; } else + if (strncmp(hex,"ESI",3) == 0) { hex+=3; regval = reg_esi; } else + if (strncmp(hex,"EDI",3) == 0) { hex+=3; regval = reg_edi; } else + if (strncmp(hex,"EBP",3) == 0) { hex+=3; regval = reg_ebp; } else + if (strncmp(hex,"ESP",3) == 0) { hex+=3; regval = reg_esp; } else + if (strncmp(hex,"EIP",3) == 0) { hex+=3; regval = reg_eip; } else + if (strncmp(hex,"AX",2) == 0) { hex+=2; regval = reg_ax; } else + if (strncmp(hex,"BX",2) == 0) { hex+=2; regval = reg_bx; } else + if (strncmp(hex,"CX",2) == 0) { hex+=2; regval = reg_cx; } else + if (strncmp(hex,"DX",2) == 0) { hex+=2; regval = reg_dx; } else + if (strncmp(hex,"SI",2) == 0) { hex+=2; regval = reg_si; } else + if (strncmp(hex,"DI",2) == 0) { hex+=2; regval = reg_di; } else + if (strncmp(hex,"BP",2) == 0) { hex+=2; regval = reg_bp; } else + if (strncmp(hex,"SP",2) == 0) { hex+=2; regval = reg_sp; } else + if (strncmp(hex,"IP",2) == 0) { hex+=2; regval = reg_ip; } else + if (strncmp(hex,"CS",2) == 0) { hex+=2; regval = SegValue(cs); } else + if (strncmp(hex,"DS",2) == 0) { hex+=2; regval = SegValue(ds); } else + if (strncmp(hex,"ES",2) == 0) { hex+=2; regval = SegValue(es); } else + if (strncmp(hex,"FS",2) == 0) { hex+=2; regval = SegValue(fs); } else + if (strncmp(hex,"GS",2) == 0) { hex+=2; regval = SegValue(gs); } else + if (strncmp(hex,"SS",2) == 0) { hex+=2; regval = SegValue(ss); }; while (*hex) { - if ((*hex>='0') && (*hex<='9')) value = (value<<4)+*hex-'0'; - else if ((*hex>='A') && (*hex<='F')) value = (value<<4)+*hex-'A'+10; + if ((*hex >= '0') && (*hex <= '9')) value = (value<<4) + *hex - '0'; + else if ((*hex >= 'A') && (*hex <= 'F')) value = (value<<4) + *hex - 'A' + 10; else { - if(*hex == '+') {hex++;return regval + value + GetHexValue(hex,hex); }; - if(*hex == '-') {hex++;return regval + value - GetHexValue(hex,hex); }; - break; // No valid char + if (*hex == '+') {hex++;return regval + value + GetHexValue(hex,hex); } else + if (*hex == '-') {hex++;return regval + value - GetHexValue(hex,hex); } + else break; // No valid char } hex++; }; @@ -930,38 +964,38 @@ bool ChangeRegister(char* str) { char* hex = str; while (*hex==' ') hex++; - if (strstr(hex,"EAX")==hex) { hex+=3; reg_eax = GetHexValue(hex,hex); } else - if (strstr(hex,"EBX")==hex) { hex+=3; reg_ebx = GetHexValue(hex,hex); } else - if (strstr(hex,"ECX")==hex) { hex+=3; reg_ecx = GetHexValue(hex,hex); } else - if (strstr(hex,"EDX")==hex) { hex+=3; reg_edx = GetHexValue(hex,hex); } else - if (strstr(hex,"ESI")==hex) { hex+=3; reg_esi = GetHexValue(hex,hex); } else - if (strstr(hex,"EDI")==hex) { hex+=3; reg_edi = GetHexValue(hex,hex); } else - if (strstr(hex,"EBP")==hex) { hex+=3; reg_ebp = GetHexValue(hex,hex); } else - if (strstr(hex,"ESP")==hex) { hex+=3; reg_esp = GetHexValue(hex,hex); } else - if (strstr(hex,"EIP")==hex) { hex+=3; reg_eip = GetHexValue(hex,hex); } else - if (strstr(hex,"AX")==hex) { hex+=2; reg_ax = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"BX")==hex) { hex+=2; reg_bx = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"CX")==hex) { hex+=2; reg_cx = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"DX")==hex) { hex+=2; reg_dx = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"SI")==hex) { hex+=2; reg_si = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"DI")==hex) { hex+=2; reg_di = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"BP")==hex) { hex+=2; reg_bp = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"SP")==hex) { hex+=2; reg_sp = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"IP")==hex) { hex+=2; reg_ip = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"CS")==hex) { hex+=2; SegSet16(cs,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"DS")==hex) { hex+=2; SegSet16(ds,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"ES")==hex) { hex+=2; SegSet16(es,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"FS")==hex) { hex+=2; SegSet16(fs,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"GS")==hex) { hex+=2; SegSet16(gs,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"SS")==hex) { hex+=2; SegSet16(ss,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"AF")==hex) { hex+=2; SETFLAGBIT(AF,GetHexValue(hex,hex)); } else - if (strstr(hex,"CF")==hex) { hex+=2; SETFLAGBIT(CF,GetHexValue(hex,hex)); } else - if (strstr(hex,"DF")==hex) { hex+=2; SETFLAGBIT(DF,GetHexValue(hex,hex)); } else - if (strstr(hex,"IF")==hex) { hex+=2; SETFLAGBIT(IF,GetHexValue(hex,hex)); } else - if (strstr(hex,"OF")==hex) { hex+=2; SETFLAGBIT(OF,GetHexValue(hex,hex)); } else - if (strstr(hex,"ZF")==hex) { hex+=2; SETFLAGBIT(ZF,GetHexValue(hex,hex)); } else - if (strstr(hex,"PF")==hex) { hex+=2; SETFLAGBIT(PF,GetHexValue(hex,hex)); } else - if (strstr(hex,"SF")==hex) { hex+=2; SETFLAGBIT(SF,GetHexValue(hex,hex)); } else + if (strncmp(hex,"EAX",3) == 0) { hex+=3; reg_eax = GetHexValue(hex,hex); } else + if (strncmp(hex,"EBX",3) == 0) { hex+=3; reg_ebx = GetHexValue(hex,hex); } else + if (strncmp(hex,"ECX",3) == 0) { hex+=3; reg_ecx = GetHexValue(hex,hex); } else + if (strncmp(hex,"EDX",3) == 0) { hex+=3; reg_edx = GetHexValue(hex,hex); } else + if (strncmp(hex,"ESI",3) == 0) { hex+=3; reg_esi = GetHexValue(hex,hex); } else + if (strncmp(hex,"EDI",3) == 0) { hex+=3; reg_edi = GetHexValue(hex,hex); } else + if (strncmp(hex,"EBP",3) == 0) { hex+=3; reg_ebp = GetHexValue(hex,hex); } else + if (strncmp(hex,"ESP",3) == 0) { hex+=3; reg_esp = GetHexValue(hex,hex); } else + if (strncmp(hex,"EIP",3) == 0) { hex+=3; reg_eip = GetHexValue(hex,hex); } else + if (strncmp(hex,"AX",2) == 0) { hex+=2; reg_ax = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"BX",2) == 0) { hex+=2; reg_bx = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"CX",2) == 0) { hex+=2; reg_cx = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"DX",2) == 0) { hex+=2; reg_dx = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"SI",2) == 0) { hex+=2; reg_si = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"DI",2) == 0) { hex+=2; reg_di = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"BP",2) == 0) { hex+=2; reg_bp = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"SP",2) == 0) { hex+=2; reg_sp = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"IP",2) == 0) { hex+=2; reg_ip = (Bit16u)GetHexValue(hex,hex); } else + if (strncmp(hex,"CS",2) == 0) { hex+=2; SegSet16(cs,(Bit16u)GetHexValue(hex,hex)); } else + if (strncmp(hex,"DS",2) == 0) { hex+=2; SegSet16(ds,(Bit16u)GetHexValue(hex,hex)); } else + if (strncmp(hex,"ES",2) == 0) { hex+=2; SegSet16(es,(Bit16u)GetHexValue(hex,hex)); } else + if (strncmp(hex,"FS",2) == 0) { hex+=2; SegSet16(fs,(Bit16u)GetHexValue(hex,hex)); } else + if (strncmp(hex,"GS",2) == 0) { hex+=2; SegSet16(gs,(Bit16u)GetHexValue(hex,hex)); } else + if (strncmp(hex,"SS",2) == 0) { hex+=2; SegSet16(ss,(Bit16u)GetHexValue(hex,hex)); } else + if (strncmp(hex,"AF",2) == 0) { hex+=2; SETFLAGBIT(AF,GetHexValue(hex,hex)); } else + if (strncmp(hex,"CF",2) == 0) { hex+=2; SETFLAGBIT(CF,GetHexValue(hex,hex)); } else + if (strncmp(hex,"DF",2) == 0) { hex+=2; SETFLAGBIT(DF,GetHexValue(hex,hex)); } else + if (strncmp(hex,"IF",2) == 0) { hex+=2; SETFLAGBIT(IF,GetHexValue(hex,hex)); } else + if (strncmp(hex,"OF",2) == 0) { hex+=2; SETFLAGBIT(OF,GetHexValue(hex,hex)); } else + if (strncmp(hex,"ZF",2) == 0) { hex+=2; SETFLAGBIT(ZF,GetHexValue(hex,hex)); } else + if (strncmp(hex,"PF",2) == 0) { hex+=2; SETFLAGBIT(PF,GetHexValue(hex,hex)); } else + if (strncmp(hex,"SF",2) == 0) { hex+=2; SETFLAGBIT(SF,GetHexValue(hex,hex)); } else { return false; }; return true; }; @@ -989,7 +1023,7 @@ bool ParseCommand(char* str) { return true; }; - if (command == "MEMDUMPBIN") { // Dump memory to file bineary + if (command == "MEMDUMPBIN") { // Dump memory to file binary Bit16u seg = (Bit16u)GetHexValue(found,found); found++; Bit32u ofs = GetHexValue(found,found); found++; Bit32u num = GetHexValue(found,found); found++; @@ -1037,6 +1071,11 @@ bool ParseCommand(char* str) { return true; }; + if (command == "ADDLOG") { + if(found && *found) DEBUG_ShowMsg("NOTICE: %s\n",found); + return true; + }; + if (command == "SR") { // Set register value DEBUG_ShowMsg("DEBUG: Set Register %s.\n",(ChangeRegister(found)?"success":"failure")); return true; @@ -1100,14 +1139,21 @@ bool ParseCommand(char* str) { if (command == "BPINT") { // Add Interrupt Breakpoint Bit8u intNr = (Bit8u)GetHexValue(found,found); - bool all = !(*found);found++; - Bit8u valAH = (Bit8u)GetHexValue(found,found); + bool all = !(*found); + Bit8u valAH = (Bit8u)GetHexValue(found,found); if ((valAH==0x00) && (*found=='*' || all)) { - CBreakpoint::AddIntBreakpoint(intNr,BPINT_ALL,false); + CBreakpoint::AddIntBreakpoint(intNr,BPINT_ALL,BPINT_ALL,false); DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X\n",intNr); } else { - CBreakpoint::AddIntBreakpoint(intNr,valAH,false); - DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X AH=%02X\n",intNr,valAH); + all = !(*found); + Bit8u valAL = (Bit8u)GetHexValue(found,found); + if ((valAL==0x00) && (*found=='*' || all)) { + CBreakpoint::AddIntBreakpoint(intNr,valAH,BPINT_ALL,false); + DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X AH=%02X\n",intNr,valAH); + } else { + CBreakpoint::AddIntBreakpoint(intNr,valAH,valAL,false); + DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X AH=%02X AL=%02X\n",intNr,valAH,valAL); + } } return true; }; @@ -1178,8 +1224,7 @@ bool ParseCommand(char* str) { cpuLogCounter = GetHexValue(found,found); debugging = false; - CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); - ignoreAddressOnce = SegPhys(cs)+reg_eip; + CBreakpoint::ActivateBreakpointsExceptAt(SegPhys(cs)+reg_eip); DOSBOX_SetNormalLoop(); return true; }; @@ -1198,7 +1243,7 @@ bool ParseCommand(char* str) { Bit8u intNr = (Bit8u)GetHexValue(found,found); DEBUG_ShowMsg("DEBUG: Starting INT %02X\n",intNr); CBreakpoint::AddBreakpoint(SegValue(cs),reg_eip, true); - CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip-1,true); + CBreakpoint::ActivateBreakpointsExceptAt(SegPhys(cs)+reg_eip-1); debugging = false; DrawCode(); DOSBOX_SetNormalLoop(); @@ -1276,20 +1321,11 @@ bool ParseCommand(char* str) { #endif if (command == "HELP" || command == "?") { DEBUG_ShowMsg("Debugger commands (enter all values in hex or as register):\n"); - DEBUG_ShowMsg("--------------------------------------------------------------------------\n"); - DEBUG_ShowMsg("F3/F6 - Previous command in history.\n"); - DEBUG_ShowMsg("F4/F7 - Next command in history.\n"); - DEBUG_ShowMsg("F5 - Run.\n"); - DEBUG_ShowMsg("F9 - Set/Remove breakpoint.\n"); - DEBUG_ShowMsg("F10/F11 - Step over / trace into instruction.\n"); - DEBUG_ShowMsg("ALT + D/E/S/X/B - Set data view to DS:SI/ES:DI/SS:SP/DS:DX/ES:BX.\n"); - DEBUG_ShowMsg("Escape - Clear input line."); - DEBUG_ShowMsg("Up/Down - Move code view cursor.\n"); - DEBUG_ShowMsg("Page Up/Down - Scroll data view.\n"); - DEBUG_ShowMsg("Home/End - Scroll log messages.\n"); + DEBUG_ShowMsg("Commands------------------------------------------------\n"); DEBUG_ShowMsg("BP [segment]:[offset] - Set breakpoint.\n"); DEBUG_ShowMsg("BPINT [intNr] * - Set interrupt breakpoint.\n"); - DEBUG_ShowMsg("BPINT [intNr] [ah] - Set interrupt breakpoint with ah.\n"); + DEBUG_ShowMsg("BPINT [intNr] [ah] * - Set interrupt breakpoint with ah.\n"); + DEBUG_ShowMsg("BPINT [intNr] [ah] [al] - Set interrupt breakpoint with ah and al.\n"); #if C_HEAVY_DEBUG DEBUG_ShowMsg("BPM [segment]:[offset] - Set memory breakpoint (memory change).\n"); DEBUG_ShowMsg("BPPM [selector]:[offset]- Set pmode-memory breakpoint (memory change).\n"); @@ -1304,7 +1340,7 @@ bool ParseCommand(char* str) { DEBUG_ShowMsg("LOG [num] - Write cpu log file.\n"); DEBUG_ShowMsg("LOGS/LOGL [num] - Write short/long cpu log file.\n"); DEBUG_ShowMsg("HEAVYLOG - Enable/Disable automatic cpu log when dosbox exits.\n"); - DEBUG_ShowMsg("ZEROPROTECT - Enable/Disable zero code execution detecion.\n"); + DEBUG_ShowMsg("ZEROPROTECT - Enable/Disable zero code execution detection.\n"); #endif DEBUG_ShowMsg("SR [reg] [value] - Set register value.\n"); DEBUG_ShowMsg("SM [seg]:[off] [val] [.]..- Set memory with following values.\n"); @@ -1313,6 +1349,8 @@ bool ParseCommand(char* str) { DEBUG_ShowMsg("SV [filename] - Save var list in file.\n"); DEBUG_ShowMsg("LV [filename] - Load var list from file.\n"); + DEBUG_ShowMsg("ADDLOG [message] - Add message to the log file.\n"); + DEBUG_ShowMsg("MEMDUMP [seg]:[off] [len] - Write memory to file memdump.txt.\n"); DEBUG_ShowMsg("MEMDUMPBIN [s]:[o] [len] - Write memory to file memdump.bin.\n"); DEBUG_ShowMsg("SELINFO [segName] - Show selector info.\n"); @@ -1329,6 +1367,17 @@ bool ParseCommand(char* str) { DEBUG_ShowMsg("TIMERIRQ - Run the system timer.\n"); DEBUG_ShowMsg("HELP - Help\n"); + DEBUG_ShowMsg("Keys------------------------------------------------\n"); + DEBUG_ShowMsg("F3/F6 - Previous command in history.\n"); + DEBUG_ShowMsg("F4/F7 - Next command in history.\n"); + DEBUG_ShowMsg("F5 - Run.\n"); + DEBUG_ShowMsg("F9 - Set/Remove breakpoint.\n"); + DEBUG_ShowMsg("F10/F11 - Step over / trace into instruction.\n"); + DEBUG_ShowMsg("ALT + D/E/S/X/B - Set data view to DS:SI/ES:DI/SS:SP/DS:DX/ES:BX.\n"); + DEBUG_ShowMsg("Escape - Clear input line."); + DEBUG_ShowMsg("Up/Down - Move code view cursor.\n"); + DEBUG_ShowMsg("Page Up/Down - Scroll data view.\n"); + DEBUG_ShowMsg("Home/End - Scroll log messages.\n"); return true; }; @@ -1402,7 +1451,7 @@ char* AnalyzeInstruction(char* inst, bool saveSelector) { // Variable found ? CDebugVar* var = CDebugVar::FindVar(address); if (var) { - // Replace occurence + // Replace occurrence char* pos1 = strchr(inst,'['); char* pos2 = strchr(inst,']'); if (pos1 && pos2) { @@ -1512,8 +1561,30 @@ char* AnalyzeInstruction(char* inst, bool saveSelector) { Bit32u DEBUG_CheckKeys(void) { Bits ret=0; + bool numberrun = false; + bool skipDraw = false; int key=getch(); - if (key>0) { + + if (key >='1' && key <='5' && strlen(codeViewData.inputStr) == 0) { + const Bit32s v[] ={5,500,1000,5000,10000}; + CPU_Cycles= v[key - '1']; + + skipFirstInstruction = true; + + ret = (*cpudecoder)(); + SetCodeWinStart(); + + /* Setup variables so we end up at the proper ret processing */ + numberrun = true; + + // Don't redraw the screen if it's going to get redrawn immediately + // afterwards, to avoid resetting oldregs. + if (ret == debugCallback) + skipDraw = true; + key = -1; + } + + if (key>0 || numberrun) { #if defined(WIN32) && defined(__PDCURSES__) switch (key) { case PADENTER: key=0x0A; break; @@ -1640,40 +1711,52 @@ Bit32u DEBUG_CheckKeys(void) { break; case KEY_F(5): // Run Program debugging=false; - CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); - ignoreAddressOnce = SegPhys(cs)+reg_eip; - DOSBOX_SetNormalLoop(); + DrawCode(); // update code window to show "running" status + + skipFirstInstruction = true; // for heavy debugger + CPU_Cycles = 1; + ret=(*cpudecoder)(); + + // ensure all breakpoints are activated + CBreakpoint::ActivateBreakpoints(); + + skipDraw = true; // don't update screen after this instruction + + DOSBOX_SetNormalLoop(); break; case KEY_F(9): // Set/Remove Breakpoint - { PhysPt ptr = GetAddress(codeViewData.cursorSeg,codeViewData.cursorOfs); - if (CBreakpoint::IsBreakpoint(ptr)) { - CBreakpoint::DeleteBreakpoint(ptr); + if (CBreakpoint::IsBreakpoint(codeViewData.cursorSeg, codeViewData.cursorOfs)) { + if (CBreakpoint::DeleteBreakpoint(codeViewData.cursorSeg, codeViewData.cursorOfs)) DEBUG_ShowMsg("DEBUG: Breakpoint deletion success.\n"); - } - else { - CBreakpoint::AddBreakpoint(codeViewData.cursorSeg, codeViewData.cursorOfs, false); - DEBUG_ShowMsg("DEBUG: Set breakpoint at %04X:%04X\n",codeViewData.cursorSeg,codeViewData.cursorOfs); - } + else + DEBUG_ShowMsg("DEBUG: Failed to delete breakpoint.\n"); + } + else { + CBreakpoint::AddBreakpoint(codeViewData.cursorSeg, codeViewData.cursorOfs, false); + DEBUG_ShowMsg("DEBUG: Set breakpoint at %04X:%04X\n",codeViewData.cursorSeg,codeViewData.cursorOfs); } break; case KEY_F(10): // Step over inst - if (StepOver()) return 0; - else { - exitLoop = false; + if (StepOver()) { skipFirstInstruction = true; // for heavy debugger CPU_Cycles = 1; ret=(*cpudecoder)(); - SetCodeWinStart(); - CBreakpoint::ignoreOnce = 0; + + DOSBOX_SetNormalLoop(); + + // ensure all breakpoints are activated + CBreakpoint::ActivateBreakpoints(); + skipDraw = true; + break; } - break; + // If we aren't stepping over something, do a normal step. + // NB: Fall-through case KEY_F(11): // trace into exitLoop = false; skipFirstInstruction = true; // for heavy debugger CPU_Cycles = 1; ret = (*cpudecoder)(); SetCodeWinStart(); - CBreakpoint::ignoreOnce = 0; break; case 0x0A: //Parse typed Command codeViewData.inputStr[MAXCMDLEN] = '\0'; @@ -1736,7 +1819,8 @@ Bit32u DEBUG_CheckKeys(void) { } } ret=0; - DEBUG_DrawScreen(); + if (!skipDraw) + DEBUG_DrawScreen(); } return ret; }; @@ -1751,7 +1835,7 @@ Bitu DEBUG_Loop(void) { SDL_Delay(1); if ((oldCS!=SegValue(cs)) || (oldEIP!=reg_eip)) { CBreakpoint::AddBreakpoint(oldCS,oldEIP,true); - CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); + CBreakpoint::ActivateBreakpointsExceptAt(SegPhys(cs)+reg_eip); debugging=false; DOSBOX_SetNormalLoop(); return 0; @@ -1790,7 +1874,8 @@ static void LogMCBChain(Bit16u mcb_segment) { DOS_MCB mcb(mcb_segment); char filename[9]; // 8 characters plus a terminating NUL const char *psp_seg_note; - PhysPt dataAddr = PhysMake(dataSeg,dataOfs);// location being viewed in the "Data Overview" + Bit16u DOS_dataOfs = static_cast(dataOfs); //Realmode addressing only + PhysPt dataAddr = PhysMake(dataSeg,DOS_dataOfs);// location being viewed in the "Data Overview" // loop forever, breaking out of the loop once we've processed the last MCB while (true) { @@ -1820,7 +1905,7 @@ static void LogMCBChain(Bit16u mcb_segment) { PhysPt mcbStartAddr = PhysMake(mcb_segment+1,0); PhysPt mcbEndAddr = PhysMake(mcb_segment+1+mcb.GetSize(),0); if (dataAddr >= mcbStartAddr && dataAddr < mcbEndAddr) { - LOG(LOG_MISC,LOG_ERROR)(" (data addr %04hX:%04X is %u bytes past this MCB)",dataSeg,dataOfs,dataAddr - mcbStartAddr); + LOG(LOG_MISC,LOG_ERROR)(" (data addr %04hX:%04X is %u bytes past this MCB)",dataSeg,DOS_dataOfs,dataAddr - mcbStartAddr); } // if we've just processed the last MCB in the chain, break out of the loop @@ -1856,9 +1941,9 @@ static void LogGDT(void) while (address %04Xxxx flags [uw] %x:%x::%x:%x [d=%x|a=%x]", i,entry.block.base,entry.block.us,table.block.us, entry.block.wr,table.block.wr,entry.block.d,entry.block.a); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); } } } @@ -1926,10 +2011,10 @@ void LogPages(char* selname) { Bitu entry_addr=(table.block.base<<12)+(sel & 0x3ff)*4; entry.load=phys_readd(entry_addr); sprintf(out1,"page %05Xxxx -> %04Xxxx flags [puw] %x:%x::%x:%x::%x:%x",sel,entry.block.base,entry.block.p,table.block.p,entry.block.us,table.block.us,entry.block.wr,table.block.wr); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); } else { sprintf(out1,"pagetable %03X not present, flags [puw] %x::%x::%x",(sel >> 10),table.block.p,table.block.us,table.block.wr); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); } } } @@ -1938,24 +2023,24 @@ void LogPages(char* selname) { static void LogCPUInfo(void) { char out1[512]; sprintf(out1,"cr0:%08X cr2:%08X cr3:%08X cpl=%x",cpu.cr0,paging.cr2,paging.cr3,cpu.cpl); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); sprintf(out1,"eflags:%08X [vm=%x iopl=%x nt=%x]",reg_flags,GETFLAG(VM)>>17,GETFLAG(IOPL)>>12,GETFLAG(NT)>>14); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); sprintf(out1,"GDT base=%08X limit=%08X",cpu.gdt.GetBase(),cpu.gdt.GetLimit()); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); sprintf(out1,"IDT base=%08X limit=%08X",cpu.idt.GetBase(),cpu.idt.GetLimit()); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); Bitu sel=CPU_STR(); Descriptor desc; if (cpu.gdt.GetDescriptor(sel,desc)) { sprintf(out1,"TR selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); } sel=CPU_SLDT(); if (cpu.gdt.GetDescriptor(sel,desc)) { sprintf(out1,"LDT selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1); - LOG(LOG_MISC,LOG_ERROR)(out1); + LOG(LOG_MISC,LOG_ERROR)("%s",out1); } }; @@ -2036,7 +2121,7 @@ public: } char filename[128]; - char args[256]; + char args[256+1]; cmd->FindCommand(1,temp_line); safe_strncpy(filename,temp_line.c_str(),128); @@ -2055,17 +2140,10 @@ public: Bit16u oldss = SegValue(ss); Bit32u oldesp = reg_esp; - // Workaround : Allocate Stack Space - Bit16u segment; - Bit16u size = 0x200 / 0x10; - if (DOS_AllocateMemory(&segment,&size)) { - SegSet16(ss,segment); - reg_sp = 0x200; - // Start shell - DOS_Shell shell; - shell.Execute(filename,args); - DOS_FreeMemory(segment); - } + // Start shell + DOS_Shell shell; + shell.Execute(filename,args); + // set old reg values SegSet16(ss,oldss); reg_esp = oldesp; @@ -2081,7 +2159,7 @@ void DEBUG_CheckExecuteBreakpoint(Bit16u seg, Bit32u off) { if (pDebugcom && pDebugcom->IsActive()) { CBreakpoint::AddBreakpoint(seg,off,true); - CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); + CBreakpoint::ActivateBreakpointsExceptAt(SegPhys(cs)+reg_eip); pDebugcom = 0; }; }; @@ -2105,8 +2183,9 @@ void DEBUG_SetupConsole(void) { WIN32_Console(); #else tcgetattr(0,&consolesettings); - printf("\e[8;50;80t"); //resize terminal - fflush(NULL); + //curses must be inited first in order to catch the resize (is an event) +// printf("\e[8;50;80t"); //resize terminal +// fflush(NULL); #endif memset((void *)&dbg,0,sizeof(dbg)); debugging=false; @@ -2123,8 +2202,8 @@ void DEBUG_ShutDown(Section * /*sec*/) { #ifndef WIN32 tcsetattr(0, TCSANOW,&consolesettings); // printf("\e[0m\e[2J"); //Seems to destroy scrolling - printf("\ec"); - fflush(NULL); +// printf("\ec"); //Doesn't seem to be needed anymore +// fflush(NULL); #endif } @@ -2156,7 +2235,7 @@ void CDebugVar::InsertVariable(char* name, PhysPt adr) void CDebugVar::DeleteAll(void) { - std::list::iterator i; + std::vector::iterator i; CDebugVar* bp; for(i=varList.begin(); i != varList.end(); i++) { bp = static_cast(*i); @@ -2167,17 +2246,19 @@ void CDebugVar::DeleteAll(void) CDebugVar* CDebugVar::FindVar(PhysPt pt) { - std::list::iterator i; + if (varList.empty()) return 0; + + std::vector::size_type s = varList.size(); CDebugVar* bp; - for(i=varList.begin(); i != varList.end(); i++) { - bp = static_cast(*i); - if (bp->GetAdr()==pt) return bp; + for(std::vector::size_type i = 0; i != s; i++) { + bp = static_cast(varList[i]); + if (bp->GetAdr() == pt) return bp; }; return 0; }; bool CDebugVar::SaveVars(char* name) { - if (varList.size()>65535) return false; + if (varList.size() > 65535) return false; FILE* f = fopen(name,"wb+"); if (!f) return false; @@ -2186,7 +2267,7 @@ bool CDebugVar::SaveVars(char* name) { Bit16u num = (Bit16u)varList.size(); fwrite(&num,1,sizeof(num),f); - std::list::iterator i; + std::vector::iterator i; CDebugVar* bp; for(i=varList.begin(); i != varList.end(); i++) { bp = static_cast(*i); @@ -2207,15 +2288,17 @@ bool CDebugVar::LoadVars(char* name) // read number of vars Bit16u num; - fread(&num,1,sizeof(num),f); - + if (fread(&num,sizeof(num),1,f) != 1) { + fclose(f); + return false; + } for (Bit16u i=0; i::iterator i; CDebugVar *dv; char buffer[DEBUG_VAR_BUF_LEN]; + std::vector::size_type s = CDebugVar::varList.size(); + bool windowchanges = false; - int idx = 0; - for(i=CDebugVar::varList.begin(); i != CDebugVar::varList.end(); i++, idx++) { + for(std::vector::size_type i = 0; i != s; i++) { - if (idx == 4*3) { + if (i == 4*3) { /* too many variables */ break; } - dv = static_cast(*i); - + dv = static_cast(CDebugVar::varList[i]); Bit16u value; - if (mem_readw_checked(dv->GetAdr(),&value)) + bool varchanges = false; + bool has_no_value = mem_readw_checked(dv->GetAdr(),&value); + if (has_no_value) { snprintf(buffer,DEBUG_VAR_BUF_LEN, "%s", "??????"); - else - snprintf(buffer,DEBUG_VAR_BUF_LEN, "0x%04x", value); + dv->SetValue(false,0); + varchanges = true; + } else { + if ( dv->HasValue() && dv->GetValue() == value) { + ; //It already had a value and it didn't change (most likely case) + } else { + dv->SetValue(true,value); + snprintf(buffer,DEBUG_VAR_BUF_LEN, "0x%04x", value); + varchanges = true; + } + } - int y = idx / 3; - int x = (idx % 3) * 26; - mvwprintw(dbg.win_var, y, x, dv->GetName()); - mvwprintw(dbg.win_var, y, (x + DEBUG_VAR_BUF_LEN + 1) , buffer); + if (varchanges) { + int y = i / 3; + int x = (i % 3) * 26; + mvwprintw(dbg.win_var, y, x, dv->GetName()); + mvwprintw(dbg.win_var, y, (x + DEBUG_VAR_BUF_LEN + 1) , buffer); + windowchanges = true; //Something has changed in this window + } } - wrefresh(dbg.win_var); + if (windowchanges) wrefresh(dbg.win_var); }; #undef DEBUG_VAR_BUF_LEN // HEAVY DEBUGGING STUFF @@ -2457,6 +2553,7 @@ bool DEBUG_HeavyIsBreakpoint(void) { cpuLogCounter--; } if (cpuLogCounter<=0) { + cpuLogFile.flush(); cpuLogFile.close(); DEBUG_ShowMsg("DEBUG: cpu log LOGCPU.TXT created\n"); cpuLog = false; diff --git a/src/debug/debug_disasm.cpp b/src/debug/debug_disasm.cpp index 6d8d5bc..9b905f8 100644 --- a/src/debug/debug_disasm.cpp +++ b/src/debug/debug_disasm.cpp @@ -246,7 +246,7 @@ static char const * op386map1[256] = { "int 03", "int %Ib", "into", "iret", /* d */ "%g1 %Eb,1", "%g1 %Ev,1", "%g1 %Eb,cl", "%g1 %Ev,cl", - "aam ; %Ib", "aad ; %Ib", "setalc", "xlat", + "aam ; %Ib", "aad ; %Ib", "setalc", "%P xlat", #if 0 "esc 0,%Ib", "esc 1,%Ib", "esc 2,%Ib", "esc 3,%Ib", "esc 4,%Ib", "esc 5,%Ib", "esc 6,%Ib", "esc 7,%Ib", @@ -490,6 +490,7 @@ static void uprintf(char const *s, ...) va_list arg_ptr; va_start (arg_ptr, s); vsprintf(ubufp, s, arg_ptr); + va_end(arg_ptr); while (*ubufp) ubufp++; } diff --git a/src/debug/debug_gui.cpp b/src/debug/debug_gui.cpp index 60166d9..8905144 100644 --- a/src/debug/debug_gui.cpp +++ b/src/debug/debug_gui.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -27,6 +27,7 @@ #include #include +#include "cross.h" #include "support.h" #include "regs.h" #include "debug.h" @@ -56,16 +57,18 @@ void DEBUG_ShowMsg(char const* format,...) { char buf[512]; va_list msg; va_start(msg,format); - vsprintf(buf,format,msg); + vsnprintf(buf,sizeof(buf),format,msg); va_end(msg); + buf[sizeof(buf) - 1] = '\0'; + /* Add newline if not present */ - Bitu len=strlen(buf); - if(buf[len-1]!='\n') strcat(buf,"\n"); + size_t len = strlen(buf); + if(buf[len - 1] != '\n' && len + 1 < sizeof(buf) ) strcat(buf,"\n"); if(debuglog) fprintf(debuglog,"%s",buf); - if (logBuffPos!=logBuff.end()) { + if (logBuffPos != logBuff.end()) { logBuffPos=logBuff.end(); DEBUG_RefreshPage(0); // mvwprintw(dbg.win_out,dbg.win_out->_maxy-1, 0, ""); @@ -86,6 +89,7 @@ void DEBUG_RefreshPage(char scroll) { list::iterator i = logBuffPos; int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx); int rem_lines = maxy; + if(rem_lines == -1) return; wclear(dbg.win_out); @@ -109,7 +113,7 @@ void LOG::operator() (char const* format, ...){ if (d_type>=LOG_MAX) return; if ((d_severity!=LOG_ERROR) && (!loggrp[d_type].enabled)) return; - DEBUG_ShowMsg("%10u: %s:%s\n",cycle_count,loggrp[d_type].front,buf); + DEBUG_ShowMsg("%10u: %s:%s\n",static_cast(cycle_count),loggrp[d_type].front,buf); } @@ -145,15 +149,15 @@ static void DrawBars(void) { attrset(COLOR_PAIR(PAIR_BLACK_BLUE)); } /* Show the Register bar */ - mvaddstr(1-1,0, "---(Register Overview )---"); + mvaddstr(1-1,0, "-----(Register Overview )----- "); /* Show the Data Overview bar perhaps with more special stuff in the end */ - mvaddstr(6-1,0,"---(Data Overview Scroll: page up/down)---"); - /* Show the Code Overview perhaps with special stuff in bar too */ - mvaddstr(17-1,0,"---(Code Overview Scroll: up/down )---"); + mvaddstr(6-1,0, "-----(Data Overview Scroll: page up/down)----- "); + /* Show the Code Overview perhaps with special stuff in bar too */ + mvaddstr(15-1,0,"-----(Code Overview Scroll: up/down )----- "); /* Show the Variable Overview bar */ - mvaddstr(29-1,0, "---(Variable Overview )---"); + mvaddstr(27-1,0,"-----(Variable Overview )----- "); /* Show the Output OverView */ - mvaddstr(34-1,0, "---(Output Scroll: home/end )---"); + mvaddstr(32-1,0,"-----(Output Scroll: home/end )----- "); attrset(0); //Match values with below. So we don't need to touch the internal window structures } @@ -169,16 +173,17 @@ static void MakeSubWindows(void) { dbg.win_reg=subwin(dbg.win_main,4,win_main_maxx,outy,0); outy+=5; // 6 /* The Data Window */ - dbg.win_data=subwin(dbg.win_main,10,win_main_maxx,outy,0); - outy+=11; // 17 + dbg.win_data=subwin(dbg.win_main,8,win_main_maxx,outy,0); + outy+=9; // 15 /* The Code Window */ dbg.win_code=subwin(dbg.win_main,11,win_main_maxx,outy,0); - outy+=12; // 29 + outy+=12; // 27 /* The Variable Window */ dbg.win_var=subwin(dbg.win_main,4,win_main_maxx,outy,0); - outy+=5; // 34 + outy+=5; // 32 /* The Output Window */ dbg.win_out=subwin(dbg.win_main,win_main_maxy-outy,win_main_maxx,outy,0); + if(!dbg.win_reg ||!dbg.win_data || !dbg.win_code || !dbg.win_var || !dbg.win_out) E_Exit("Setting up windows failed"); // dbg.input_y=win_main_maxy-1; scrollok(dbg.win_out,TRUE); DrawBars(); @@ -195,20 +200,21 @@ static void MakePairs(void) { } static void LOG_Destroy(Section*) { if(debuglog) fclose(debuglog); + debuglog = 0; } static void LOG_Init(Section * sec) { - Section_prop * sect=static_cast(sec); - const char * blah=sect->Get_string("logfile"); - if(blah && blah[0] &&(debuglog = fopen(blah,"wt+"))){ - }else{ - debuglog=0; + Section_prop * sect = static_cast(sec); + const char * blah = sect->Get_string("logfile"); + if(blah && blah[0] && (debuglog = fopen(blah,"wt+"))){ + ; + } else { + debuglog = 0; } sect->AddDestroyFunction(&LOG_Destroy); - char buf[1024]; - for (Bitu i=1;iGet_bool(buf); } @@ -251,9 +257,9 @@ void LOG_StartUp(void) { Section_prop * sect=control->AddSection_prop("log",LOG_Init); Prop_string* Pstring = sect->Add_string("logfile",Property::Changeable::Always,""); Pstring->Set_help("file where the log messages will be saved to"); - char buf[1024]; - for (Bitu i=1;iAdd_bool(buf,Property::Changeable::Always,true); Pbool->Set_help("Enable/Disable logging of this type."); @@ -272,6 +278,8 @@ void DBGUI_StartUp(void) { nodelay(dbg.win_main,true); keypad(dbg.win_main,true); #ifndef WIN32 + printf("\e[8;50;80t"); + fflush(NULL); resizeterm(50,80); touchwin(dbg.win_main); #endif diff --git a/src/debug/debug_inc.h b/src/debug/debug_inc.h index 873cde2..0c8f638 100644 --- a/src/debug/debug_inc.h +++ b/src/debug/debug_inc.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Local Debug Function */ diff --git a/src/debug/debug_win32.cpp b/src/debug/debug_win32.cpp index 9fd80ea..090ab69 100644 --- a/src/debug/debug_win32.cpp +++ b/src/debug/debug_win32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,11 +11,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "dosbox.h" +#if C_DEBUG #ifdef WIN32 #include @@ -77,3 +79,4 @@ void WIN32_Console() { ResizeConsole(GetStdHandle(STD_OUTPUT_HANDLE),80,50); } #endif +#endif diff --git a/src/debug/disasm_tables.h b/src/debug/disasm_tables.h index 55a6bae..25453a3 100644 --- a/src/debug/disasm_tables.h +++ b/src/debug/disasm_tables.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ char * RegTable_16[8]= {"ax","cx","dx","bx","sp","bp","si","di"}; diff --git a/src/dos/Makefile.am b/src/dos/Makefile.am index 3bdfbf5..36a2d99 100644 --- a/src/dos/Makefile.am +++ b/src/dos/Makefile.am @@ -7,4 +7,4 @@ libdos_a_SOURCES = dos.cpp dos_devices.cpp dos_execute.cpp dos_files.cpp dos_ioc drives.cpp drives.h drive_virtual.cpp drive_local.cpp drive_cache.cpp drive_fat.cpp \ drive_iso.cpp dev_con.h dos_mscdex.cpp dos_keyboard_layout.cpp \ cdrom.h cdrom.cpp cdrom_ioctl_win32.cpp cdrom_aspi_win32.cpp cdrom_ioctl_linux.cpp cdrom_image.cpp \ - cdrom_ioctl_os2.cpp + cdrom_ioctl_os2.cpp drive_overlay.cpp diff --git a/src/dos/cdrom.cpp b/src/dos/cdrom.cpp index 0e7d38e..06d47de 100644 --- a/src/dos/cdrom.cpp +++ b/src/dos/cdrom.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/dos/cdrom.h b/src/dos/cdrom.h index 7199f46..c1219c7 100644 --- a/src/dos/cdrom.h +++ b/src/dos/cdrom.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/dos/cdrom_aspi_win32.cpp b/src/dos/cdrom_aspi_win32.cpp index 5ae8daf..aeed5ea 100644 --- a/src/dos/cdrom_aspi_win32.cpp +++ b/src/dos/cdrom_aspi_win32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -32,7 +32,11 @@ #include // Ioctl stuff #include #include // Ioctl stuff -#else +#elif (defined __MINGW64_VERSION_MAJOR) +#include // Ioctl stuff +#include // Ioctl stuff +#include +#else #include "ddk/ntddcdrm.h" // Ioctl stuff #include "ddk/ntddscsi.h" #endif diff --git a/src/dos/cdrom_image.cpp b/src/dos/cdrom_image.cpp index b96546d..d669462 100644 --- a/src/dos/cdrom_image.cpp +++ b/src/dos/cdrom_image.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -52,6 +52,7 @@ CDROM_Interface_Image::BinaryFile::BinaryFile(const char *filename, bool &error) CDROM_Interface_Image::BinaryFile::~BinaryFile() { delete file; + file = NULL; } bool CDROM_Interface_Image::BinaryFile::read(Bit8u *buffer, int seek, int count) @@ -128,12 +129,13 @@ int CDROM_Interface_Image::AudioFile::getLength() // initialize static members int CDROM_Interface_Image::refCount = 0; -CDROM_Interface_Image* CDROM_Interface_Image::images[26]; +CDROM_Interface_Image* CDROM_Interface_Image::images[26] = {}; CDROM_Interface_Image::imagePlayer CDROM_Interface_Image::player = { NULL, NULL, NULL, {0}, 0, 0, 0, false, false, false, {0} }; CDROM_Interface_Image::CDROM_Interface_Image(Bit8u subUnit) + :subUnit(subUnit) { images[subUnit] = this; if (refCount == 0) { @@ -168,7 +170,7 @@ bool CDROM_Interface_Image::SetDevice(char* path, int forceCD) // print error message on dosbox console char buf[MAX_LINE_LENGTH]; - snprintf(buf, MAX_LINE_LENGTH, "Could not load image file: %s\n", path); + snprintf(buf, MAX_LINE_LENGTH, "Could not load image file: %s\r\n", path); Bit16u size = (Bit16u)strlen(buf); DOS_WriteFile(STDOUT, (Bit8u*)buf, &size); return false; @@ -205,7 +207,7 @@ bool CDROM_Interface_Image::GetAudioSub(unsigned char& attr, unsigned char& trac attr = tracks[track - 1].attr; index = 1; FRAMES_TO_MSF(player.currFrame + 150, &absPos.min, &absPos.sec, &absPos.fr); - FRAMES_TO_MSF(player.currFrame - tracks[track - 1].start + 150, &relPos.min, &relPos.sec, &relPos.fr); + FRAMES_TO_MSF(player.currFrame - tracks[track - 1].start, &relPos.min, &relPos.sec, &relPos.fr); return true; } @@ -375,6 +377,7 @@ bool CDROM_Interface_Image::LoadIsoFile(char* filename) track.file = new BinaryFile(filename, error); if (error) { delete track.file; + track.file = NULL; return false; } track.number = 1; @@ -416,8 +419,9 @@ bool CDROM_Interface_Image::CanReadPVD(TrackFile *file, int sectorSize, bool mod if (sectorSize == RAW_SECTOR_SIZE && !mode2) seek += 16; if (mode2) seek += 24; file->read(pvd, seek, COOKED_SECTOR_SIZE); - // pvd[0] = descriptor type, pvd[1..5] = standard identifier, pvd[6] = iso version - return (pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1); + // pvd[0] = descriptor type, pvd[1..5] = standard identifier, pvd[6] = iso version (+8 for High Sierra) + return ((pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1) || + (pvd[8] == 1 && !strncmp((char*)(&pvd[9]), "CDROM", 5) && pvd[14] == 1)); } #if defined(WIN32) @@ -543,6 +547,7 @@ bool CDROM_Interface_Image::LoadCueSheet(char *cuefile) #endif if (error) { delete track.file; + track.file = NULL; success = false; } } @@ -658,7 +663,30 @@ bool CDROM_Interface_Image::GetRealFileName(string &filename, string &pathname) return true; } } - +#if defined (WIN32) || defined(OS2) + //Nothing +#else + //Consider the possibility that the filename has a windows directory seperator (inside the CUE file) + //which is common for some commercial rereleases of DOS games using DOSBox + + string copy = filename; + size_t l = copy.size(); + for (size_t i = 0; i < l;i++) { + if(copy[i] == '\\') copy[i] = '/'; + } + + if (stat(copy.c_str(), &test) == 0) { + filename = copy; + return true; + } + + tmpstr = pathname + "/" + copy; + if (stat(tmpstr.c_str(), &test) == 0) { + filename = tmpstr; + return true; + } + +#endif return false; } diff --git a/src/dos/cdrom_ioctl_linux.cpp b/src/dos/cdrom_ioctl_linux.cpp index 4612217..846db8b 100644 --- a/src/dos/cdrom_ioctl_linux.cpp +++ b/src/dos/cdrom_ioctl_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/dos/cdrom_ioctl_os2.cpp b/src/dos/cdrom_ioctl_os2.cpp index 6c34b17..926a85a 100644 --- a/src/dos/cdrom_ioctl_os2.cpp +++ b/src/dos/cdrom_ioctl_os2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/dos/cdrom_ioctl_win32.cpp b/src/dos/cdrom_ioctl_win32.cpp index 994c582..74b568b 100644 --- a/src/dos/cdrom_ioctl_win32.cpp +++ b/src/dos/cdrom_ioctl_win32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -26,9 +26,9 @@ #include #include -#if defined (_MSC_VER) -#include // Ioctl stuff +#if (defined (_MSC_VER)) || (defined __MINGW64_VERSION_MAJOR) #include // Ioctl stuff +#include // Ioctl stuff #else #include "ddk/ntddcdrm.h" // Ioctl stuff #endif diff --git a/src/dos/dev_con.h b/src/dos/dev_con.h index 79a6e42..330b56f 100644 --- a/src/dos/dev_con.h +++ b/src/dos/dev_con.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -30,13 +30,13 @@ public: bool Write(Bit8u * data,Bit16u * size); bool Seek(Bit32u * pos,Bit32u type); bool Close(); - void ClearAnsi(void); Bit16u GetInformation(void); bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode){return false;} bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode){return false;} private: + void ClearAnsi(void); + void Output(Bit8u chr); Bit8u readcache; - Bit8u lastwrite; struct ansi { /* should create a constructor, which would fill them with the appropriate values */ bool esc; bool sci; @@ -44,8 +44,6 @@ private: Bit8u attr; Bit8u data[NUMBER_ANSI_DATA]; Bit8u numberofarg; - Bit16u nrows; - Bit16u ncols; Bit8s savecol; Bit8s saverow; bool warned; @@ -55,6 +53,7 @@ private: bool device_CON::Read(Bit8u * data,Bit16u * size) { Bit16u oldax=reg_ax; Bit16u count=0; + INT10_SetCurMode(); if ((readcache) && (*size)) { data[count++]=readcache; if(dos.echo) INT10_TeletypeOutput(readcache,7); @@ -116,8 +115,10 @@ bool device_CON::Read(Bit8u * data,Bit16u * size) { bool device_CON::Write(Bit8u * data,Bit16u * size) { Bit16u count=0; Bitu i; - Bit8u col,row; + Bit8u col,row,page; + Bit16u ncols,nrows; Bit8u tempdata; + INT10_SetCurMode(); while (*size>count) { if (!ansi.esc){ if(data[count]=='\033') { @@ -127,12 +128,18 @@ bool device_CON::Write(Bit8u * data,Bit16u * size) { ansi.esc=true; count++; continue; + } else if(data[count] == '\t' && !dos.direct_output) { + /* expand tab if not direct output */ + page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + do { + Output(' '); + col=CURSOR_POS_COL(page); + } while(col%8); + count++; + continue; } else { - /* Some sort of "hack" now that '\n' doesn't set col to 0 (int10_char.cpp old chessgame) */ - if((data[count] == '\n') && (lastwrite != '\r')) INT10_TeletypeOutputAttr('\r',ansi.attr,ansi.enabled); - /* pass attribute only if ansi is enabled */ - INT10_TeletypeOutputAttr(data[count],ansi.attr,ansi.enabled); - lastwrite = data[count++]; + Output(data[count]); + count++; continue; } } @@ -156,7 +163,8 @@ bool device_CON::Write(Bit8u * data,Bit16u * size) { continue; } /*ansi.esc and ansi.sci are true */ - Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + if (!dos.internal_output) ansi.enabled=true; + page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); switch(data[count]){ case '0': case '1': @@ -175,11 +183,9 @@ bool device_CON::Write(Bit8u * data,Bit16u * size) { break; case 'm': /* SGR */ for(i=0;i<=ansi.numberofarg;i++){ - ansi.enabled=true; switch(ansi.data[i]){ case 0: /* normal */ ansi.attr=0x07;//Real ansi does this as well. (should do current defaults) - ansi.enabled=false; break; case 1: /* bold mode on*/ ansi.attr|=0x08; @@ -269,11 +275,13 @@ bool device_CON::Write(Bit8u * data,Bit16u * size) { ansi.warned = true; LOG(LOG_IOCTL,LOG_WARN)("ANSI SEQUENCES USED"); } + ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); + nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1; /* Turn them into positions that are on the screen */ if(ansi.data[0] == 0) ansi.data[0] = 1; if(ansi.data[1] == 0) ansi.data[1] = 1; - if(ansi.data[0] > ansi.nrows) ansi.data[0] = (Bit8u)ansi.nrows; - if(ansi.data[1] > ansi.ncols) ansi.data[1] = (Bit8u)ansi.ncols; + if(ansi.data[0] > nrows) ansi.data[0] = (Bit8u)nrows; + if(ansi.data[1] > ncols) ansi.data[1] = (Bit8u)ncols; INT10_SetCursorPos(--(ansi.data[0]),--(ansi.data[1]),page); /*ansi=1 based, int10 is 0 based */ ClearAnsi(); break; @@ -290,9 +298,10 @@ bool device_CON::Write(Bit8u * data,Bit16u * size) { case 'B': /*cursor Down */ col=CURSOR_POS_COL(page) ; row=CURSOR_POS_ROW(page) ; + nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1; tempdata = (ansi.data[0]? ansi.data[0] : 1); - if(tempdata + static_cast(row) >= ansi.nrows) - { row = ansi.nrows - 1;} + if(tempdata + static_cast(row) >= nrows) + { row = nrows - 1;} else { row += tempdata; } INT10_SetCursorPos(row,col,page); ClearAnsi(); @@ -300,9 +309,10 @@ bool device_CON::Write(Bit8u * data,Bit16u * size) { case 'C': /*cursor forward */ col=CURSOR_POS_COL(page); row=CURSOR_POS_ROW(page); + ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); tempdata=(ansi.data[0]? ansi.data[0] : 1); - if(tempdata + static_cast(col) >= ansi.ncols) - { col = ansi.ncols - 1;} + if(tempdata + static_cast(col) >= ncols) + { col = ncols - 1;} else { col += tempdata;} INT10_SetCursorPos(row,col,page); ClearAnsi(); @@ -342,15 +352,17 @@ bool device_CON::Write(Bit8u * data,Bit16u * size) { case 'K': /* erase till end of line (don't touch cursor) */ col = CURSOR_POS_COL(page); row = CURSOR_POS_ROW(page); - INT10_WriteChar(' ',ansi.attr,page,ansi.ncols-col,true); //Use this one to prevent scrolling when end of screen is reached - //for(i = col;i<(Bitu) ansi.ncols; i++) INT10_TeletypeOutputAttr(' ',ansi.attr,true); + ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); + INT10_WriteChar(' ',ansi.attr,page,ncols-col,true); //Use this one to prevent scrolling when end of screen is reached + //for(i = col;i<(Bitu) ncols; i++) INT10_TeletypeOutputAttr(' ',ansi.attr,true); INT10_SetCursorPos(row,col,page); ClearAnsi(); break; case 'M': /* delete line (NANSI) */ - col = CURSOR_POS_COL(page); row = CURSOR_POS_ROW(page); - INT10_ScrollWindow(row,0,ansi.nrows-1,ansi.ncols-1,ansi.data[0]? -ansi.data[0] : -1,ansi.attr,0xFF); + ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); + nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1; + INT10_ScrollWindow(row,0,nrows-1,ncols-1,ansi.data[0]? -ansi.data[0] : -1,ansi.attr,0xFF); ClearAnsi(); break; case 'l':/* (if code =7) disable linewrap */ @@ -396,11 +408,8 @@ Bit16u device_CON::GetInformation(void) { device_CON::device_CON() { SetName("CON"); readcache=0; - lastwrite=0; ansi.enabled=false; ansi.attr=0x7; - ansi.ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); //should be updated once set/reset mode is implemented - ansi.nrows=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1; ansi.saverow=0; ansi.savecol=0; ansi.warned=false; @@ -413,3 +422,19 @@ void device_CON::ClearAnsi(void){ ansi.sci=false; ansi.numberofarg=0; } + +void device_CON::Output(Bit8u chr) { + if (dos.internal_output || ansi.enabled) { + if (CurMode->type==M_TEXT) { + Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + Bit8u col=CURSOR_POS_COL(page); + Bit8u row=CURSOR_POS_ROW(page); + BIOS_NCOLS;BIOS_NROWS; + if (nrows==row+1 && (chr=='\n' || (ncols==col+1 && chr!='\r' && chr!=8 && chr!=7))) { + INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,ansi.attr,page); + INT10_SetCursorPos(row-1,col,page); + } + } + INT10_TeletypeOutputAttr(chr,ansi.attr,true); + } else INT10_TeletypeOutput(chr,7); + } diff --git a/src/dos/dos.cpp b/src/dos/dos.cpp index baba3c3..07e39f1 100644 --- a/src/dos/dos.cpp +++ b/src/dos/dos.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -133,7 +133,7 @@ static Bitu DOS_21Handler(void) { Bit8u c=reg_dl;Bit16u n=1; DOS_WriteFile(STDOUT,&c,&n); //Not in the official specs, but happens nonetheless. (last written character) - reg_al = c;// reg_al=(c==9)?0x20:c; //Officially: tab to spaces + reg_al=(c==9)?0x20:c; //strangely, tab conversion to spaces is reflected here } break; case 0x03: /* Read character from STDAUX */ @@ -184,8 +184,10 @@ static Bitu DOS_21Handler(void) { default: { Bit8u c = reg_dl;Bit16u n = 1; + dos.direct_output=true; DOS_WriteFile(STDOUT,&c,&n); - reg_al = reg_dl; + dos.direct_output=false; + reg_al=c; } break; }; @@ -211,6 +213,7 @@ static Bitu DOS_21Handler(void) { while ((c=mem_readb(buf++))!='$') { DOS_WriteFile(STDOUT,&c,&n); } + reg_al=c; } break; case 0x0a: /* Buffered Input */ @@ -223,6 +226,10 @@ static Bitu DOS_21Handler(void) { free--; for(;;) { DOS_ReadFile(STDIN,&c,&n); + if (n == 0) // End of file + E_Exit("DOS:0x0a:Redirected input reached EOF"); + if (c == 10) // Line feed + continue; if (c == 8) { // Backspace if (read) { //Something to backspace. // STDOUT treats backspace as non-destructive. @@ -487,10 +494,10 @@ static Bitu DOS_21Handler(void) { Bit8u drive=reg_dl; if (!drive || reg_ah==0x1f) drive = DOS_GetDefaultDrive(); else drive--; - if (Drives[drive]) { + if (drive < DOS_DRIVES && Drives[drive] && !Drives[drive]->isRemovable()) { reg_al = 0x00; SegSet16(ds,dos.tables.dpb); - reg_bx = drive;//Faking only the first entry (that is the driveletter) + reg_bx = drive*9; LOG(LOG_DOSMISC,LOG_ERROR)("Get drive parameter block."); } else { reg_al=0xff; @@ -833,6 +840,9 @@ static Bitu DOS_21Handler(void) { reg_bx=dos.psp(); break; case 0x52: { /* Get list of lists */ + Bit8u count=2; // floppy drives always counted + while (countisRemovable()) count++; + dos_infoblock.SetBlockDevices(count); RealPt addr=dos_infoblock.GetPointer(); SegSet16(es,RealSeg(addr)); reg_bx=RealOff(addr); @@ -1096,16 +1106,23 @@ static Bitu DOS_21Handler(void) { break; case 0x69: /* Get/Set disk serial number */ { + Bit16u old_cx=reg_cx; switch(reg_al) { case 0x00: /* Get */ - LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Get Disk serial number"); - CALLBACK_SCF(true); + LOG(LOG_DOSMISC,LOG_WARN)("DOS:Get Disk serial number"); + reg_cl=0x66;// IOCTL function + break; + case 0x01: /* Set */ + LOG(LOG_DOSMISC,LOG_WARN)("DOS:Set Disk serial number"); + reg_cl=0x46;// IOCTL function break; - case 0x01: - LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Set Disk serial number"); default: E_Exit("DOS:Illegal Get Serial Number call %2X",reg_al); } + reg_ch=0x08; // IOCTL category: disk drive + reg_ax=0x440d; // Generic block device request + DOS_21Handler(); + reg_cx=old_cx; break; } case 0x6c: /* Extended Open/Create */ @@ -1157,26 +1174,32 @@ static Bitu DOS_27Handler(void) { } static Bitu DOS_25Handler(void) { - if(Drives[reg_al]==0){ - reg_ax=0x8002; + if (reg_al >= DOS_DRIVES || !Drives[reg_al] || Drives[reg_al]->isRemovable()) { + reg_ax = 0x8002; SETFLAGBIT(CF,true); - }else{ + } else { + if (reg_cx == 1 && reg_dx == 0) { + if (reg_al >= 2) { + PhysPt ptr = PhysMake(SegValue(ds),reg_bx); + // write some BPB data into buffer for MicroProse installers + mem_writew(ptr+0x1c,0x3f); // hidden sectors + } + } else { + LOG(LOG_DOSMISC,LOG_NORMAL)("int 25 called but not as disk detection drive %u",reg_al); + } SETFLAGBIT(CF,false); - if((reg_cx != 1) ||(reg_dx != 1)) - LOG(LOG_DOSMISC,LOG_NORMAL)("int 25 called but not as diskdetection drive %X",reg_al); - - reg_ax=0; + reg_ax = 0; } return CBRET_NONE; } static Bitu DOS_26Handler(void) { LOG(LOG_DOSMISC,LOG_NORMAL)("int 26 called: hope for the best!"); - if(Drives[reg_al]==0){ - reg_ax=0x8002; + if (reg_al >= DOS_DRIVES || !Drives[reg_al] || Drives[reg_al]->isRemovable()) { + reg_ax = 0x8002; SETFLAGBIT(CF,true); - }else{ + } else { SETFLAGBIT(CF,false); - reg_ax=0; + reg_ax = 0; } return CBRET_NONE; } @@ -1198,10 +1221,10 @@ public: // iret // retf <- int 21 4c jumps here to mimic a retf Cyber - callback[2].Install(DOS_25Handler,CB_RETF,"DOS Int 25"); + callback[2].Install(DOS_25Handler,CB_RETF_STI,"DOS Int 25"); callback[2].Set_RealVec(0x25); - callback[3].Install(DOS_26Handler,CB_RETF,"DOS Int 26"); + callback[3].Install(DOS_26Handler,CB_RETF_STI,"DOS Int 26"); callback[3].Set_RealVec(0x26); callback[4].Install(DOS_27Handler,CB_IRET,"DOS Int 27"); @@ -1230,6 +1253,8 @@ public: dos.version.major=5; dos.version.minor=0; + dos.direct_output=false; + dos.internal_output=false; } ~DOS(){ for (Bit16u i=0;i20) { // Allocate needed paragraphs fileNum+=2; // Add a few more files for safety @@ -337,7 +344,7 @@ void DOS_DTA::SetupSearch(Bit8u _sdrive,Bit8u _sattr,char * pattern) { } void DOS_DTA::SetResult(const char * _name,Bit32u _size,Bit16u _date,Bit16u _time,Bit8u _attr) { - MEM_BlockWrite(pt+offsetof(sDTA,name),(void *)_name,DOS_NAMELENGTH_ASCII); + MEM_BlockWrite(pt+offsetof(sDTA,name),(void *)_name,strlen(_name)+1); sSave(sDTA,size,_size); sSave(sDTA,date,_date); sSave(sDTA,time,_time); @@ -386,8 +393,8 @@ bool DOS_FCB::Extended(void) { void DOS_FCB::Create(bool _extended) { Bitu fill; - if (_extended) fill=36+7; - else fill=36; + if (_extended) fill=33+7; + else fill=33; Bitu i; for (i=0;iSeek(&size,DOS_SEEK_END); + Files[_fhandle]->Seek(&size,DOS_SEEK_END); sSave(sFCB,filesize,size); size = 0; - Files[temp]->Seek(&size,DOS_SEEK_SET); - sSave(sFCB,time,Files[temp]->time); - sSave(sFCB,date,Files[temp]->date); + Files[_fhandle]->Seek(&size,DOS_SEEK_SET); + sSave(sFCB,time,Files[_fhandle]->time); + sSave(sFCB,date,Files[_fhandle]->date); } bool DOS_FCB::Valid() { @@ -491,8 +505,11 @@ void DOS_FCB::SetAttr(Bit8u attr) { if(extended) mem_writeb(pt - 1,attr); } -void DOS_FCB::SetResultAttr(Bit8u attr) { - mem_writeb(pt + 12,attr); +void DOS_FCB::SetResult(Bit32u size,Bit16u date,Bit16u time,Bit8u attr) { + mem_writed(pt + 0x1d,size); + mem_writew(pt + 0x19,date); + mem_writew(pt + 0x17,time); + mem_writeb(pt + 0x0c,attr); } void DOS_SDA::Init() { diff --git a/src/dos/dos_codepages.h b/src/dos/dos_codepages.h index 9b69f61..d5b1bbe 100644 --- a/src/dos/dos_codepages.h +++ b/src/dos/dos_codepages.h @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/dos/dos_devices.cpp b/src/dos/dos_devices.cpp index 9c4263a..9beb430 100644 --- a/src/dos/dos_devices.cpp +++ b/src/dos/dos_devices.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -37,8 +37,7 @@ class device_NUL : public DOS_Device { public: device_NUL() { SetName("NUL"); }; virtual bool Read(Bit8u * data,Bit16u * size) { - for(Bitu i = 0; i < *size;i++) - data[i]=0; + *size = 0; //Return success and no data read. LOG(LOG_IOCTL,LOG_NORMAL)("%s:READ",GetName()); return true; } @@ -60,6 +59,10 @@ class device_LPT1 : public device_NUL { public: device_LPT1() { SetName("LPT1");} Bit16u GetInformation(void) { return 0x80A0; } + bool Read(Bit8u* data,Bit16u * size){ + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } }; bool DOS_Device::Read(Bit8u * data,Bit16u * size) { diff --git a/src/dos/dos_execute.cpp b/src/dos/dos_execute.cpp index da43a40..b2a9986 100644 --- a/src/dos/dos_execute.cpp +++ b/src/dos/dos_execute.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -88,7 +88,7 @@ static void RestoreRegisters(void) { reg_sp+=18; } -extern void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused); +extern void GFX_SetTitle(Bit32s cycles,int frameskip,bool paused); void DOS_UpdatePSPName(void) { DOS_MCB mcb(dos.psp()-1); static char name[9]; @@ -225,6 +225,10 @@ bool DOS_ChildPSP(Bit16u segment, Bit16u size) { psp.SetFCB2(RealMake(parent_psp_seg,0x6c)); psp.SetEnvironment(psp_parent.GetEnvironment()); psp.SetSize(size); + // push registers in case child PSP is terminated + SaveRegisters(); + psp.SetStack(RealMakeSeg(ss,reg_sp)); + reg_sp+=18; return true; } @@ -311,6 +315,7 @@ bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { envseg=block.exec.envseg; if (!MakeEnv(name,&envseg)) { DOS_CloseFile(fhandle); + delete [] loadbuf; return false; } /* Get Memory */ @@ -352,14 +357,6 @@ bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { maxsize=0xffff; /* resize to full extent of memory block */ DOS_ResizeMemory(pspseg,&maxsize); - /* now try to lock out memory above segment 0x2000 */ - if ((real_readb(0x2000,0)==0x5a) && (real_readw(0x2000,1)==0) && (real_readw(0x2000,3)==0x7ffe)) { - /* MCB after PCJr graphics memory region is still free */ - if (pspseg+maxsize==0x17ff) { - DOS_MCB cmcb((Bit16u)(pspseg-1)); - cmcb.SetType(0x5a); // last block - } - } } loadseg=pspseg+16; if (!iscom) { @@ -415,34 +412,18 @@ bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { RealPt csip,sssp; if (iscom) { csip=RealMake(pspseg,0x100); - sssp=RealMake(pspseg,0xfffe); - mem_writew(PhysMake(pspseg,0xfffe),0); + if (memsize<0x1000) { + LOG(LOG_EXEC,LOG_WARN)("COM format with only %X paragraphs available",memsize); + sssp=RealMake(pspseg,(memsize<<4)-2); + } else sssp=RealMake(pspseg,0xfffe); + mem_writew(Real2Phys(sssp),0); } else { csip=RealMake(loadseg+head.initCS,head.initIP); sssp=RealMake(loadseg+head.initSS,head.initSP); if (head.initSP<4) LOG(LOG_EXEC,LOG_ERROR)("stack underflow/wrap at EXEC"); } - if (flags==LOAD) { - SaveRegisters(); - DOS_PSP callpsp(dos.psp()); - /* Save the SS:SP on the PSP of calling program */ - callpsp.SetStack(RealMakeSeg(ss,reg_sp)); - reg_sp+=18; - /* Switch the psp's */ - dos.psp(pspseg); - DOS_PSP newpsp(dos.psp()); - dos.dta(RealMake(newpsp.GetSegment(),0x80)); - /* First word on the stack is the value ax should contain on startup */ - real_writew(RealSeg(sssp-2),RealOff(sssp-2),0xffff); - block.exec.initsssp = sssp-2; - block.exec.initcsip = csip; - block.SaveData(); - return true; - } - - if (flags==LOADNGO) { - if ((reg_sp>0xfffe) || (reg_sp<18)) LOG(LOG_EXEC,LOG_ERROR)("stack underflow/wrap at EXEC"); + if ((flags==LOAD) || (flags==LOADNGO)) { /* Get Caller's program CS:IP of the stack and set termination address to that */ RealSetVec(0x22,RealMake(mem_readw(SegPhys(ss)+reg_sp+2),mem_readw(SegPhys(ss)+reg_sp))); SaveRegisters(); @@ -458,29 +439,18 @@ bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { /* copy fcbs */ newpsp.SetFCB1(block.exec.fcb1); newpsp.SetFCB2(block.exec.fcb2); - /* Set the stack for new program */ - SegSet16(ss,RealSeg(sssp));reg_sp=RealOff(sssp); - /* Add some flags and CS:IP on the stack for the IRET */ - CPU_Push16(RealSeg(csip)); - CPU_Push16(RealOff(csip)); - /* DOS starts programs with a RETF, so critical flags - * should not be modified (IOPL in v86 mode); - * interrupt flag is set explicitly, test flags cleared */ - reg_flags=(reg_flags&(~FMASK_TEST))|FLAG_IF; - //Jump to retf so that we only need to store cs:ip on the stack - reg_ip++; - /* Setup the rest of the registers */ - reg_ax=reg_bx=0;reg_cx=0xff; - reg_dx=pspseg; - reg_si=RealOff(csip); - reg_di=RealOff(sssp); - reg_bp=0x91c; /* DOS internal stack begin relict */ - SegSet16(ds,pspseg);SegSet16(es,pspseg); -#if C_DEBUG - /* Started from debug.com, then set breakpoint at start */ - DEBUG_CheckExecuteBreakpoint(RealSeg(csip),RealOff(csip)); -#endif - /* Add the filename to PSP and environment MCB's */ + /* Save the SS:SP on the PSP of new program */ + newpsp.SetStack(RealMakeSeg(ss,reg_sp)); + + /* Setup bx, contains a 0xff in bl and bh if the drive in the fcb is not valid */ + DOS_FCB fcb1(RealSeg(block.exec.fcb1),RealOff(block.exec.fcb1)); + DOS_FCB fcb2(RealSeg(block.exec.fcb2),RealOff(block.exec.fcb2)); + Bit8u d1 = fcb1.GetDrive(); //depends on 0 giving the dos.default drive + if ( (d1>=DOS_DRIVES) || !Drives[d1] ) reg_bl = 0xFF; else reg_bl = 0; + Bit8u d2 = fcb2.GetDrive(); + if ( (d2>=DOS_DRIVES) || !Drives[d2] ) reg_bh = 0xFF; else reg_bh = 0; + + /* Write filename in new program MCB */ char stripname[8]= { 0 };Bitu index=0; while (char chr=*name++) { switch (chr) { @@ -498,6 +468,48 @@ bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { DOS_MCB pspmcb(dos.psp()-1); pspmcb.SetFileName(stripname); DOS_UpdatePSPName(); + } + + if (flags==LOAD) { + /* First word on the stack is the value ax should contain on startup */ + real_writew(RealSeg(sssp-2),RealOff(sssp-2),reg_bx); + /* Write initial CS:IP and SS:SP in param block */ + block.exec.initsssp = sssp-2; + block.exec.initcsip = csip; + block.SaveData(); + /* Changed registers */ + reg_sp+=18; + reg_ax=RealOff(csip); + reg_bx=memsize; + reg_dx=0; + return true; + } + + if (flags==LOADNGO) { + if ((reg_sp>0xfffe) || (reg_sp<18)) LOG(LOG_EXEC,LOG_ERROR)("stack underflow/wrap at EXEC"); + /* Set the stack for new program */ + SegSet16(ss,RealSeg(sssp));reg_sp=RealOff(sssp); + /* Add some flags and CS:IP on the stack for the IRET */ + CPU_Push16(RealSeg(csip)); + CPU_Push16(RealOff(csip)); + /* DOS starts programs with a RETF, so critical flags + * should not be modified (IOPL in v86 mode); + * interrupt flag is set explicitly, test flags cleared */ + reg_flags=(reg_flags&(~FMASK_TEST))|FLAG_IF; + //Jump to retf so that we only need to store cs:ip on the stack + reg_ip++; + /* Setup the rest of the registers */ + reg_ax=reg_bx; + reg_cx=0xff; + reg_dx=pspseg; + reg_si=RealOff(csip); + reg_di=RealOff(sssp); + reg_bp=0x91c; /* DOS internal stack begin relict */ + SegSet16(ds,pspseg);SegSet16(es,pspseg); +#if C_DEBUG + /* Started from debug.com, then set breakpoint at start */ + DEBUG_CheckExecuteBreakpoint(RealSeg(csip),RealOff(csip)); +#endif return true; } return false; diff --git a/src/dos/dos_files.cpp b/src/dos/dos_files.cpp index c1574f1..8d88361 100644 --- a/src/dos/dos_files.cpp +++ b/src/dos/dos_files.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -52,7 +52,7 @@ Bit8u DOS_GetDefaultDrive(void) { void DOS_SetDefaultDrive(Bit8u drive) { // if (drive<=DOS_DRIVES && ((drive<2) || Drives[drive])) DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(drive); - if (drive<=DOS_DRIVES && ((drive<2) || Drives[drive])) {dos.current_drive = drive; DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(drive);} + if (drive='a') && (c<='z')) {upname[w++]=c-32;continue;} - if ((c>='A') && (c<='Z')) {upname[w++]=c;continue;} - if ((c>='0') && (c<='9')) {upname[w++]=c;continue;} - switch (c) { - case '/': - upname[w++]='\\'; - break; - case ' ': /* should be seperator */ - break; - case '\\': case '$': case '#': case '@': case '(': case ')': - case '!': case '%': case '{': case '}': case '`': case '~': - case '_': case '-': case '.': case '*': case '?': case '&': - case '\'': case '+': case '^': case 246: case 255: case 0xa0: - case 0xe5: case 0xbd: case 0x9d: - upname[w++]=c; - break; - default: - LOG(LOG_FILES,LOG_NORMAL)("Makename encountered an illegal char %c hex:%X in %s!",c,c,name); - DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; - break; - } + c=name_int[r++]; + if ((c>='a') && (c<='z')) c-=32; + else if (c==' ') continue; /* should be separator */ + else if (c=='/') c='\\'; + upname[w++]=c; } + while (r>0 && name_int[r-1]==' ') r--; if (r>=DOS_PATHLENGTH) { DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; } upname[w]=0; /* Now parse the new file name to make the final filename */ @@ -175,6 +160,24 @@ bool DOS_MakeName(char const * const name,char * const fullname,Bit8u * drive) { if((strlen(tempdir) - strlen(ext)) > 8) memmove(tempdir + 8, ext, 5); } else tempdir[8]=0; + for (Bitu i=0;i='A') && (c<='Z')) continue; + if ((c>='0') && (c<='9')) continue; + switch (c) { + case '$': case '#': case '@': case '(': case ')': + case '!': case '%': case '{': case '}': case '`': case '~': + case '_': case '-': case '.': case '*': case '?': case '&': + case '\'': case '+': case '^': case 246: case 255: case 0xa0: + case 0xe5: case 0xbd: case 0x9d: + break; + default: + LOG(LOG_FILES,LOG_NORMAL)("Makename encountered an illegal char %c hex:%X in %s!",c,c,name); + DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; + break; + } + } + if (strlen(fullname)+strlen(tempdir)>=DOS_PATHLENGTH) { DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; } @@ -306,6 +309,7 @@ bool DOS_Rename(char const * const oldname,char const * const newname) { } bool DOS_FindFirst(char * search,Bit16u attr,bool fcb_findfirst) { + LOG(LOG_FILES,LOG_NORMAL)("file search attributes %X name %s",attr,search); DOS_DTA dta(dos.dta()); Bit8u drive;char fullsearch[DOS_PATHLENGTH]; char dir[DOS_PATHLENGTH];char pattern[DOS_PATHLENGTH]; @@ -361,8 +365,8 @@ bool DOS_FindNext(void) { } -bool DOS_ReadFile(Bit16u entry,Bit8u * data,Bit16u * amount) { - Bit32u handle=RealHandle(entry); +bool DOS_ReadFile(Bit16u entry,Bit8u * data,Bit16u * amount,bool fcb) { + Bit32u handle = fcb?entry:RealHandle(entry); if (handle>=DOS_FILES) { DOS_SetError(DOSERR_INVALID_HANDLE); return false; @@ -383,8 +387,8 @@ bool DOS_ReadFile(Bit16u entry,Bit8u * data,Bit16u * amount) { return ret; } -bool DOS_WriteFile(Bit16u entry,Bit8u * data,Bit16u * amount) { - Bit32u handle=RealHandle(entry); +bool DOS_WriteFile(Bit16u entry,Bit8u * data,Bit16u * amount,bool fcb) { + Bit32u handle = fcb?entry:RealHandle(entry); if (handle>=DOS_FILES) { DOS_SetError(DOSERR_INVALID_HANDLE); return false; @@ -405,8 +409,8 @@ bool DOS_WriteFile(Bit16u entry,Bit8u * data,Bit16u * amount) { return ret; } -bool DOS_SeekFile(Bit16u entry,Bit32u * pos,Bit32u type) { - Bit32u handle=RealHandle(entry); +bool DOS_SeekFile(Bit16u entry,Bit32u * pos,Bit32u type,bool fcb) { + Bit32u handle = fcb?entry:RealHandle(entry); if (handle>=DOS_FILES) { DOS_SetError(DOSERR_INVALID_HANDLE); return false; @@ -418,8 +422,8 @@ bool DOS_SeekFile(Bit16u entry,Bit32u * pos,Bit32u type) { return Files[handle]->Seek(pos,type); } -bool DOS_CloseFile(Bit16u entry) { - Bit32u handle=RealHandle(entry); +bool DOS_CloseFile(Bit16u entry, bool fcb) { + Bit32u handle = fcb?entry:RealHandle(entry); if (handle>=DOS_FILES) { DOS_SetError(DOSERR_INVALID_HANDLE); return false; @@ -431,8 +435,10 @@ bool DOS_CloseFile(Bit16u entry) { if (Files[handle]->IsOpen()) { Files[handle]->Close(); } + DOS_PSP psp(dos.psp()); - psp.SetFileHandle(entry,0xff); + if (!fcb) psp.SetFileHandle(entry,0xff); + if (Files[handle]->RemoveRef()<=0) { delete Files[handle]; Files[handle]=0; @@ -469,11 +475,11 @@ static bool PathExists(char const * const name) { } -bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry) { +bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry,bool fcb) { // Creation of a device is the same as opening it // Tc201 installer if (DOS_FindDevice(name) != DOS_DEVICES) - return DOS_OpenFile(name, OPEN_READ, entry); + return DOS_OpenFile(name, OPEN_READ, entry, fcb); LOG(LOG_FILES,LOG_NORMAL)("file create attributes %X file %s",attributes,name); char fullname[DOS_PATHLENGTH];Bit8u drive; @@ -492,7 +498,7 @@ bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry) { return false; } /* We have a position in the main table now find one in the psp table */ - *entry = psp.FindFreeFileEntry(); + *entry = fcb?handle:psp.FindFreeFileEntry(); if (*entry==0xff) { DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); return false; @@ -506,7 +512,7 @@ bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry) { if (foundit) { Files[handle]->SetDrive(drive); Files[handle]->AddRef(); - psp.SetFileHandle(*entry,handle); + if (!fcb) psp.SetFileHandle(*entry,handle); return true; } else { if(!PathExists(name)) DOS_SetError(DOSERR_PATH_NOT_FOUND); @@ -515,7 +521,7 @@ bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry) { } } -bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry) { +bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry,bool fcb) { /* First check for devices */ if (flags>2) LOG(LOG_FILES,LOG_ERROR)("Special file open command %X file %s",flags,name); else LOG(LOG_FILES,LOG_NORMAL)("file open command %X file %s",flags,name); @@ -548,7 +554,7 @@ bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry) { return false; } /* We have a position in the main table now find one in the psp table */ - *entry = psp.FindFreeFileEntry(); + *entry = fcb?handle:psp.FindFreeFileEntry(); if (*entry==0xff) { DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); @@ -563,7 +569,7 @@ bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry) { } if (exists || device ) { Files[handle]->AddRef(); - psp.SetFileHandle(*entry,handle); + if (!fcb) psp.SetFileHandle(*entry,handle); return true; } else { //Test if file exists, but opened in read-write mode (and writeprotected) @@ -630,6 +636,11 @@ bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bi bool DOS_UnlinkFile(char const * const name) { char fullname[DOS_PATHLENGTH];Bit8u drive; + // An existing device returns an access denied error + if (DOS_FindDevice(name) != DOS_DEVICES) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } if (!DOS_MakeName(name,fullname,&drive)) return false; if(Drives[drive]->FileUnlink(fullname)){ return true; @@ -765,7 +776,21 @@ bool DOS_CreateTempFile(char * const name,Bit16u * entry) { return true; } -#define FCB_SEP ":.;,=+" +char DOS_ToUpper(char c) { + unsigned char uc = *reinterpret_cast(&c); + if (uc > 0x60 && uc < 0x7B) uc -= 0x20; + else if (uc > 0x7F && uc < 0xA5) { + const unsigned char t[0x25] = { + 0x00, 0x9a, 0x45, 0x41, 0x8E, 0x41, 0x8F, 0x80, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x4F, 0x99, 0x4F, 0x55, 0x55, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x49, 0x4F, 0x55, 0xA5}; + if (t[uc - 0x80]) uc = t[uc-0x80]; + } + char sc = *reinterpret_cast(&uc); + return sc; +} + +#define FCB_SEP ":;,=+" #define ILLEGAL ":.;,=+ \t/\"[]<>|" static bool isvalid(const char in){ @@ -813,77 +838,87 @@ Bit8u FCB_Parsename(Bit16u seg,Bit16u offset,Bit8u parser ,char *string, Bit8u * fcb.GetName(fcb_name.full); fcb_name.part.drive[0]-='A'-1;fcb_name.part.drive[1]=0; fcb_name.part.name[8]=0;fcb_name.part.ext[3]=0; - /* Strip of the leading sepetaror */ - if((parser & PARSE_SEP_STOP) && *string) { //ignore leading seperator - char sep[] = FCB_SEP;char a[2]; - a[0]= *string;a[1]='\0'; - if (strcspn(a,sep)==0) string++; - } /* strip leading spaces */ while((*string==' ')||(*string=='\t')) string++; + + /* Strip of the leading separator */ + if((parser & PARSE_SEP_STOP) && *string) { + char sep[] = FCB_SEP;char a[2]; + a[0] = *string;a[1] = '\0'; + if (strcspn(a,sep) == 0) string++; + } + + /* Skip following spaces as well */ + while((*string==' ')||(*string=='\t')) string++; + /* Check for a drive */ if (string[1]==':') { + unsigned char d = *reinterpret_cast(&string[0]); + if (!isvalid(toupper(d))) {string += 2; goto savefcb;} //TODO check (for ret value) fcb_name.part.drive[0]=0; hasdrive=true; - if (isalpha(string[0]) && Drives[toupper(string[0])-'A']) { - fcb_name.part.drive[0]=(char)(toupper(string[0])-'A'+1); + if (isalpha(d) && Drives[toupper(d)-'A']) { //Floppies under dos always exist, but don't bother with that at this level + ; //THIS* was here } else ret=0xff; + fcb_name.part.drive[0]=DOS_ToUpper(string[0])-'A'+1; //Always do THIS* and continue parsing, just return the right code string+=2; } - /* Special checks for . and .. */ - if (string[0]=='.') { - string++; - if (!string[0]) { - hasname=true; - ret=PARSE_RET_NOWILD; - strcpy(fcb_name.part.name,". "); - goto savefcb; - } - if (string[1]=='.' && !string[1]) { - string++; - hasname=true; - ret=PARSE_RET_NOWILD; - strcpy(fcb_name.part.name,".. "); - goto savefcb; - } - goto checkext; - } - /* Copy the name */ + + /* Check for extension only file names */ + if (string[0] == '.') {string++;goto checkext;} + + /* do nothing if not a valid name */ + if(!isvalid(string[0])) goto savefcb; + hasname=true;finished=false;fill=' ';index=0; - while (index<8) { - if (!finished) { - if (string[0]=='*') {fill='?';fcb_name.part.name[index]='?';if (!ret) ret=1;finished=true;} - else if (string[0]=='?') {fcb_name.part.name[index]='?';if (!ret) ret=1;} - else if (isvalid(string[0])) {fcb_name.part.name[index]=(char)(toupper(string[0]));} - else { finished=true;continue; } - string++; - } else { - fcb_name.part.name[index]=fill; + /* Copy the name */ + while (true) { + unsigned char nc = *reinterpret_cast(&string[0]); + char ncs = (char)toupper(nc); //Should use DOS_ToUpper, but then more calls need to be changed. + if (ncs == '*') { //Handle * + fill = '?'; + ncs = '?'; } - index++; + if (ncs == '?' && !ret && index < 8) ret = 1; //Don't override bad drive + if (!isvalid(ncs)) { //Fill up the name. + while(index < 8) + fcb_name.part.name[index++] = fill; + break; + } + if (index < 8) { + fcb_name.part.name[index++] = (fill == '?')?fill:ncs; + } + string++; } if (!(string[0]=='.')) goto savefcb; string++; checkext: /* Copy the extension */ hasext=true;finished=false;fill=' ';index=0; - while (index<3) { - if (!finished) { - if (string[0]=='*') {fill='?';fcb_name.part.ext[index]='?';finished=true;} - else if (string[0]=='?') {fcb_name.part.ext[index]='?';if (!ret) ret=1;} - else if (isvalid(string[0])) {fcb_name.part.ext[index]=(char)(toupper(string[0]));} - else { finished=true;continue; } - string++; - } else { - fcb_name.part.ext[index]=fill; + while (true) { + unsigned char nc = *reinterpret_cast(&string[0]); + char ncs = (char)toupper(nc); + if (ncs == '*') { //Handle * + fill = '?'; + ncs = '?'; } - index++; + if (ncs == '?' && !ret && index < 3) ret = 1; + if (!isvalid(ncs)) { //Fill up the name. + while(index < 3) + fcb_name.part.ext[index++] = fill; + break; + } + if (index < 3) { + fcb_name.part.ext[index++] = (fill=='?')?fill:ncs; + } + string++; } savefcb: if (!hasdrive & !(parser & PARSE_DFLT_DRIVE)) fcb_name.part.drive[0] = 0; if (!hasname & !(parser & PARSE_BLNK_FNAME)) strcpy(fcb_name.part.name," "); if (!hasext & !(parser & PARSE_BLNK_FEXT)) strcpy(fcb_name.part.ext," "); fcb.SetName(fcb_name.part.drive[0],fcb_name.part.name,fcb_name.part.ext); + fcb.ClearBlockRecsize(); //Undocumented bonus work. *change=(Bit8u)(string-string_begin); return ret; } @@ -916,8 +951,7 @@ static void SaveFindResult(DOS_FCB & find_fcb) { fcb.Create(find_fcb.Extended()); fcb.SetName(drive,file_name,ext); fcb.SetAttr(find_attr); /* Only adds attribute if fcb is extended */ - fcb.SetResultAttr(attr); - fcb.SetSizeDateTime(size,date,time); + fcb.SetResult(size,date,time,attr); } bool DOS_FCBCreate(Bit16u seg,Bit16u offset) { @@ -927,7 +961,7 @@ bool DOS_FCBCreate(Bit16u seg,Bit16u offset) { Bit8u attr = DOS_ATTR_ARCHIVE; fcb.GetAttr(attr); if (!attr) attr = DOS_ATTR_ARCHIVE; //Better safe than sorry - if (!DOS_CreateFile(shortname,attr,&handle)) return false; + if (!DOS_CreateFile(shortname,attr,&handle,true)) return false; fcb.FileOpen((Bit8u)handle); return true; } @@ -937,27 +971,35 @@ bool DOS_FCBOpen(Bit16u seg,Bit16u offset) { char shortname[DOS_FCBNAME];Bit16u handle; fcb.GetName(shortname); + /* Search for file if name has wildcards */ + if (strpbrk(shortname,"*?")) { + LOG(LOG_FCB,LOG_WARN)("Wildcards in filename"); + if (!DOS_FCBFindFirst(seg,offset)) return false; + DOS_DTA find_dta(dos.tables.tempdta); + DOS_FCB find_fcb(RealSeg(dos.tables.tempdta),RealOff(dos.tables.tempdta)); + char name[DOS_NAMELENGTH_ASCII],file_name[9],ext[4]; + Bit32u size;Bit16u date,time;Bit8u attr; + find_dta.GetResult(name,size,date,time,attr); + DTAExtendName(name,file_name,ext); + find_fcb.SetName(fcb.GetDrive()+1,file_name,ext); + find_fcb.GetName(shortname); + } + /* First check if the name is correct */ Bit8u drive; char fullname[DOS_PATHLENGTH]; if (!DOS_MakeName(shortname,fullname,&drive)) return false; /* Check, if file is already opened */ - for (Bit8u i=0;iIsOpen() && Files[i]->IsName(fullname)) { - handle = psp.FindEntryByHandle(i); - if (handle==0xFF) { - // This shouldnt happen - LOG(LOG_FILES,LOG_ERROR)("DOS: File %s is opened but has no psp entry.",shortname); - return false; - } - fcb.FileOpen((Bit8u)handle); + Files[i]->AddRef(); + fcb.FileOpen(i); return true; } } - if (!DOS_OpenFile(shortname,OPEN_READWRITE,&handle)) return false; + if (!DOS_OpenFile(shortname,OPEN_READWRITE,&handle,true)) return false; fcb.FileOpen((Bit8u)handle); return true; } @@ -967,7 +1009,7 @@ bool DOS_FCBClose(Bit16u seg,Bit16u offset) { if(!fcb.Valid()) return false; Bit8u fhandle; fcb.FileClose(fhandle); - DOS_CloseFile(fhandle); + DOS_CloseFile(fhandle,true); return true; } @@ -1001,11 +1043,15 @@ Bit8u DOS_FCBRead(Bit16u seg,Bit16u offset,Bit16u recno) { LOG(LOG_FCB,LOG_WARN)("Reopened closed FCB"); fcb.GetSeqData(fhandle,rec_size); } + if (rec_size == 0) { + rec_size = 128; + fcb.SetSeqData(fhandle,rec_size); + } fcb.GetRecord(cur_block,cur_rec); Bit32u pos=((cur_block*128)+cur_rec)*rec_size; - if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET)) return FCB_READ_NODATA; + if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_READ_NODATA; Bit16u toread=rec_size; - if (!DOS_ReadFile(fhandle,dos_copybuf,&toread)) return FCB_READ_NODATA; + if (!DOS_ReadFile(fhandle,dos_copybuf,&toread,true)) return FCB_READ_NODATA; if (toread==0) return FCB_READ_NODATA; if (toread < rec_size) { //Zero pad copybuffer to rec_size Bitu i = toread; @@ -1028,12 +1074,16 @@ Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u recno) { LOG(LOG_FCB,LOG_WARN)("Reopened closed FCB"); fcb.GetSeqData(fhandle,rec_size); } + if (rec_size == 0) { + rec_size = 128; + fcb.SetSeqData(fhandle,rec_size); + } fcb.GetRecord(cur_block,cur_rec); Bit32u pos=((cur_block*128)+cur_rec)*rec_size; - if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET)) return FCB_ERR_WRITE; + if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_ERR_WRITE; MEM_BlockRead(Real2Phys(dos.dta())+recno*rec_size,dos_copybuf,rec_size); Bit16u towrite=rec_size; - if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite)) return FCB_ERR_WRITE; + if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite,true)) return FCB_ERR_WRITE; Bit32u size;Bit16u date,time; fcb.GetSizeDateTime(size,date,time); if (pos+towrite>size) size=pos+towrite; @@ -1045,9 +1095,8 @@ Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u recno) { Bit16u min = (Bit16u)((seconds % 3600)/60); Bit16u sec = (Bit16u)(seconds % 60); time = DOS_PackTime(hour,min,sec); - Bit8u temp=RealHandle(fhandle); - Files[temp]->time=time; - Files[temp]->date=date; + Files[fhandle]->time = time; + Files[fhandle]->date = date; fcb.SetSizeDateTime(size,date,time); if (++cur_rec>127) { cur_block++;cur_rec=0; } fcb.SetRecord(cur_block,cur_rec); @@ -1060,9 +1109,9 @@ Bit8u DOS_FCBIncreaseSize(Bit16u seg,Bit16u offset) { fcb.GetSeqData(fhandle,rec_size); fcb.GetRecord(cur_block,cur_rec); Bit32u pos=((cur_block*128)+cur_rec)*rec_size; - if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET)) return FCB_ERR_WRITE; + if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_ERR_WRITE; Bit16u towrite=0; - if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite)) return FCB_ERR_WRITE; + if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite,true)) return FCB_ERR_WRITE; Bit32u size;Bit16u date,time; fcb.GetSizeDateTime(size,date,time); if (pos+towrite>size) size=pos+towrite; @@ -1074,9 +1123,8 @@ Bit8u DOS_FCBIncreaseSize(Bit16u seg,Bit16u offset) { Bit16u min = (Bit16u)((seconds % 3600)/60); Bit16u sec = (Bit16u)(seconds % 60); time = DOS_PackTime(hour,min,sec); - Bit8u temp=RealHandle(fhandle); - Files[temp]->time=time; - Files[temp]->date=date; + Files[fhandle]->time = time; + Files[fhandle]->date = date; fcb.SetSizeDateTime(size,date,time); fcb.SetRecord(cur_block,cur_rec); return FCB_SUCCESS; @@ -1127,7 +1175,7 @@ Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore) fcb.GetRandom(random); fcb.SetRecord((Bit16u)(random / 128),(Bit8u)(random & 127)); if (restore) fcb.GetRecord(old_block,old_rec); - if (numRec>0) { + if (*numRec > 0) { /* Write records */ for (count=0; count<*numRec; count++) { error = DOS_FCBWrite(seg,offset,count);// dos_fcbwrite return 0 false when true... @@ -1141,19 +1189,23 @@ Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore) fcb.GetRecord(new_block,new_rec); if (restore) fcb.SetRecord(old_block,old_rec); /* Update the random record pointer with new position only when restore is false */ - if(!restore) fcb.SetRandom(new_block*128+new_rec); + if (!restore) fcb.SetRandom(new_block*128+new_rec); return error; } bool DOS_FCBGetFileSize(Bit16u seg,Bit16u offset) { - char shortname[DOS_PATHLENGTH];Bit16u entry;Bit8u handle;Bit16u rec_size; + char shortname[DOS_PATHLENGTH];Bit16u entry; DOS_FCB fcb(seg,offset); fcb.GetName(shortname); - if (!DOS_OpenFile(shortname,OPEN_READ,&entry)) return false; - handle = RealHandle(entry); + if (!DOS_OpenFile(shortname,OPEN_READ,&entry,true)) return false; Bit32u size = 0; - Files[handle]->Seek(&size,DOS_SEEK_END); - DOS_CloseFile(entry);fcb.GetSeqData(handle,rec_size); + Files[entry]->Seek(&size,DOS_SEEK_END); + DOS_CloseFile(entry,true); + + Bit8u handle; Bit16u rec_size; + fcb.GetSeqData(handle,rec_size); + if (rec_size == 0) rec_size = 128; //Use default if missing. + Bit32u random=(size/rec_size); if (size % rec_size) random++; fcb.SetRandom(random); @@ -1180,7 +1232,7 @@ bool DOS_FCBDeleteFile(Bit16u seg,Bit16u offset){ nextfile = DOS_FCBFindNext(seg,offset); } dos.dta(old_dta); /*Restore dta */ - return return_value; + return return_value; } bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset){ @@ -1199,12 +1251,12 @@ bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset){ for (Bit8u i=0;iIsOpen() && Files[i]->IsName(fullname)) { Bit16u handle = psp.FindEntryByHandle(i); + //(more than once maybe) if (handle == 0xFF) { - // This shouldnt happen - LOG(LOG_FILES,LOG_ERROR)("DOS: File %s is opened but has no psp entry.",oldname); - return false; + DOS_CloseFile(i,true); + } else { + DOS_CloseFile(handle); } - DOS_CloseFile(handle); } } @@ -1229,11 +1281,14 @@ bool DOS_FileExists(char const * const name) { bool DOS_GetAllocationInfo(Bit8u drive,Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters) { if (!drive) drive = DOS_GetDefaultDrive(); else drive--; - if (drive >= DOS_DRIVES || !Drives[drive]) return false; + if (drive >= DOS_DRIVES || !Drives[drive]) { + DOS_SetError(DOSERR_INVALID_DRIVE); + return false; + } Bit16u _free_clusters; Drives[drive]->AllocationInfo(_bytes_sector,_sectors_cluster,_total_clusters,&_free_clusters); SegSet16(ds,RealSeg(dos.tables.mediaid)); - reg_bx=RealOff(dos.tables.mediaid+drive*2); + reg_bx=RealOff(dos.tables.mediaid+drive*9); return true; } diff --git a/src/dos/dos_ioctl.cpp b/src/dos/dos_ioctl.cpp index 2eeddb0..90ef826 100644 --- a/src/dos/dos_ioctl.cpp +++ b/src/dos/dos_ioctl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -25,6 +25,7 @@ #include "dos_inc.h" bool DOS_IOCTL(void) { +// LOG(LOG_IOCTL,LOG_WARN)("%X %X %X %X",reg_ax,reg_bx,reg_cx,reg_dx); Bitu handle=0;Bit8u drive=0; /* calls 0-4,6,7,10,12,16 use a file handle */ if ((reg_al<4) || (reg_al==0x06) || (reg_al==0x07) || (reg_al==0x0a) || (reg_al==0x0c) || (reg_al==0x10)) { @@ -151,28 +152,31 @@ bool DOS_IOCTL(void) { return true; case 0x0D: /* Generic block device request */ { - if ((drive < 2) || Drives[drive]->isRemovable()) { + if (drive < 2 && !Drives[drive]) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + if (reg_ch != 0x08 || Drives[drive]->isRemovable()) { DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } PhysPt ptr = SegPhys(ds)+reg_dx; switch (reg_cl) { case 0x60: /* Get Device parameters */ - mem_writeb(ptr ,0x03); // special function - mem_writeb(ptr+1,(drive>=2)?0x05:0x14); // fixed disc(5), 1.44 floppy(14) - mem_writew(ptr+2,drive>=2); // nonremovable ? + //mem_writeb(ptr+0,0); // special functions (call value) + mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7) + mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable mem_writew(ptr+4,0x0000); // num of cylinders mem_writeb(ptr+6,0x00); // media type (00=other type) - // drive parameter block following - mem_writeb(ptr+7,drive); // drive - mem_writeb(ptr+8,0x00); // unit number - mem_writed(ptr+0x1f,0xffffffff); // next parameter block + // bios parameter block following + mem_writew(ptr+7,0x0200); // bytes per sector (Win3 File Mgr. uses it) break; - case 0x46: - case 0x66: /* Volume label */ + case 0x46: /* Set volume serial number */ + break; + case 0x66: /* Get volume serial number */ { char const* bufin=Drives[drive]->GetLabel(); - char buffer[11] ={' '}; + char buffer[11];memset(buffer,' ',11); char const* find_ext=strchr(bufin,'.'); if (find_ext) { @@ -180,7 +184,7 @@ bool DOS_IOCTL(void) { if (size>8) size=8; memcpy(buffer,bufin,size); find_ext++; - memcpy(buffer+size,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext)); + memcpy(buffer+8,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext)); } else { memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin)); } @@ -188,10 +192,10 @@ bool DOS_IOCTL(void) { char buf2[8]={ 'F','A','T','1','6',' ',' ',' '}; if(drive<2) buf2[4] = '2'; //FAT12 for floppies - mem_writew(ptr+0,0); // 0 + //mem_writew(ptr+0,0); //Info level (call value) mem_writed(ptr+2,0x1234); //Serial number MEM_BlockWrite(ptr+6,buffer,11);//volumename - if(reg_cl == 0x66) MEM_BlockWrite(ptr+0x11, buf2,8);//filesystem + MEM_BlockWrite(ptr+0x11,buf2,8);//filesystem } break; default : @@ -199,6 +203,7 @@ bool DOS_IOCTL(void) { DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } + reg_ax=0; return true; } case 0x0E: /* Get Logical Drive Map */ diff --git a/src/dos/dos_keyboard_layout.cpp b/src/dos/dos_keyboard_layout.cpp index 33b7d10..f600fc6 100644 --- a/src/dos/dos_keyboard_layout.cpp +++ b/src/dos/dos_keyboard_layout.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -911,6 +911,8 @@ Bitu keyboard_layout::read_codepage_file(const char* codepage_file_name, Bit32s for (Bitu i=0;i<256*16;i++) { phys_writeb(font16pt+i,cpi_buf[font_data_start+i]); } + // terminate alternate list to prevent loading + phys_writeb(Real2Phys(int10.rom.font_16_alternate),0); font_changed=true; } else if (font_height==0x0e) { // 14x8 font @@ -918,6 +920,8 @@ Bitu keyboard_layout::read_codepage_file(const char* codepage_file_name, Bit32s for (Bitu i=0;i<256*14;i++) { phys_writeb(font14pt+i,cpi_buf[font_data_start+i]); } + // terminate alternate list to prevent loading + phys_writeb(Real2Phys(int10.rom.font_14_alternate),0); font_changed=true; } else if (font_height==0x08) { // 8x8 fonts diff --git a/src/dos/dos_keyboard_layout_data.h b/src/dos/dos_keyboard_layout_data.h index 8e60111..e850a6c 100644 --- a/src/dos/dos_keyboard_layout_data.h +++ b/src/dos/dos_keyboard_layout_data.h @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/dos/dos_memory.cpp b/src/dos/dos_memory.cpp index f1f7dcc..6037eec 100644 --- a/src/dos/dos_memory.cpp +++ b/src/dos/dos_memory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,16 +11,15 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dosbox.h" #include "mem.h" #include "dos_inc.h" -#include "callback.h" #define UMB_START_SEG 0x9fff @@ -33,7 +32,8 @@ static void DOS_CompressMemory(void) { while (mcb.GetType()!=0x5a) { mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1)); - if ((mcb.GetPSPSeg()==0) && (mcb_next.GetPSPSeg()==0)) { + if (GCC_UNLIKELY((mcb_next.GetType()!=0x4d) && (mcb_next.GetType()!=0x5a))) E_Exit("Corrupt MCB chain"); + if ((mcb.GetPSPSeg()==MCB_FREE) && (mcb_next.GetPSPSeg()==MCB_FREE)) { mcb.SetSize(mcb.GetSize()+mcb_next.GetSize()+1); mcb.SetType(mcb_next.GetType()); } else { @@ -50,14 +50,8 @@ void DOS_FreeProcessMemory(Bit16u pspseg) { if (mcb.GetPSPSeg()==pspseg) { mcb.SetPSPSeg(MCB_FREE); } - if (mcb.GetType()==0x5a) { - /* check if currently last block reaches up to the PCJr graphics memory */ - if ((machine==MCH_PCJR) && (mcb_segment+mcb.GetSize()==0x17fe) && - (real_readb(0x17ff,0)==0x4d) && (real_readw(0x17ff,1)==8)) { - /* re-enable the memory past segment 0x2000 */ - mcb.SetType(0x4d); - } else break; - } + if (mcb.GetType()==0x5a) break; + if (GCC_UNLIKELY(mcb.GetType()!=0x4d)) E_Exit("Corrupt MCB chain"); mcb_segment+=mcb.GetSize()+1; mcb.SetPt(mcb_segment); } @@ -111,7 +105,7 @@ bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks) { Bit16u found_seg=0,found_seg_size=0; for (;;) { mcb.SetPt(mcb_segment); - if (mcb.GetPSPSeg()==0) { + if (mcb.GetPSPSeg()==MCB_FREE) { /* Check for enough free memory in current block */ Bit16u block_size=mcb.GetSize(); if (block_size<(*blocks)) { @@ -245,6 +239,7 @@ bool DOS_ResizeMemory(Bit16u segment,Bit16u * blocks) { mcb_new_next.SetSize(total-*blocks-1); mcb_new_next.SetPSPSeg(MCB_FREE); mcb.SetPSPSeg(dos.psp()); + DOS_CompressMemory(); return true; } /* MCB will grow, try to join with following MCB */ @@ -305,10 +300,10 @@ bool DOS_FreeMemory(Bit16u segment) { void DOS_BuildUMBChain(bool umb_active,bool ems_active) { - if (umb_active && (machine!=MCH_TANDY)) { + if (umb_active && (!IS_TANDY_ARCH)) { Bit16u first_umb_seg = 0xd000; Bit16u first_umb_size = 0x2000; - if(ems_active || (machine == MCH_PCJR)) first_umb_size = 0x1000; + if(ems_active) first_umb_size = 0x1000; dos_infoblock.SetStartOfUMBChain(UMB_START_SEG); dos_infoblock.SetUMBChainState(0); // UMBs not linked yet @@ -384,28 +379,18 @@ bool DOS_LinkUMBsToMemChain(Bit16u linkstate) { } -static Bitu DOS_default_handler(void) { - LOG(LOG_CPU,LOG_ERROR)("DOS rerouted Interrupt Called %X",lastint); - return CBRET_NONE; -} - -static CALLBACK_HandlerObject callbackhandler; void DOS_SetupMemory(void) { /* Let dos claim a few bios interrupts. Makes DOSBox more compatible with * buggy games, which compare against the interrupt table. (probably a * broken linked list implementation) */ - callbackhandler.Allocate(&DOS_default_handler,"DOS default int"); Bit16u ihseg = 0x70; - Bit16u ihofs = 0x08; - real_writeb(ihseg,ihofs+0x00,(Bit8u)0xFE); //GRP 4 - real_writeb(ihseg,ihofs+0x01,(Bit8u)0x38); //Extra Callback instruction - real_writew(ihseg,ihofs+0x02,callbackhandler.Get_callback()); //The immediate word - real_writeb(ihseg,ihofs+0x04,(Bit8u)0xCF); //An IRET Instruction + Bit16u ihofs = 0xF4; + real_writeb(ihseg,ihofs,(Bit8u)0xCF); //An IRET Instruction RealSetVec(0x01,RealMake(ihseg,ihofs)); //BioMenace (offset!=4) RealSetVec(0x02,RealMake(ihseg,ihofs)); //BioMenace (segment<0x8000) RealSetVec(0x03,RealMake(ihseg,ihofs)); //Alien Incident (offset!=0) RealSetVec(0x04,RealMake(ihseg,ihofs)); //Shadow President (lower byte of segment!=0) -// RealSetVec(0x0f,RealMake(ihseg,ihofs)); //Always a tricky one (soundblaster irq) + RealSetVec(0x0f,RealMake(ihseg,ihofs)); //Always a tricky one (soundblaster irq) // Create a dummy device MCB with PSPSeg=0x0008 DOS_MCB mcb_devicedummy((Bit16u)DOS_MEM_START); diff --git a/src/dos/dos_misc.cpp b/src/dos/dos_misc.cpp index d658273..c1a7d2e 100644 --- a/src/dos/dos_misc.cpp +++ b/src/dos/dos_misc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -90,7 +90,7 @@ static bool DOS_MultiplexFunctions(void) { mem_writew(sftptr+sftofs+0x02,(Bit16u)(Files[reg_bx]->flags&3)); // file open mode mem_writeb(sftptr+sftofs+0x04,(Bit8u)(Files[reg_bx]->attr)); // file attribute mem_writew(sftptr+sftofs+0x05,0x40|drive); // device info word - mem_writed(sftptr+sftofs+0x07,RealMake(dos.tables.dpb,drive)); // dpb of the drive + mem_writed(sftptr+sftofs+0x07,RealMake(dos.tables.dpb,drive*9)); // dpb of the drive mem_writew(sftptr+sftofs+0x0d,Files[reg_bx]->time); // packed file time mem_writew(sftptr+sftofs+0x0f,Files[reg_bx]->date); // packed file date Bit32u curpos=0; diff --git a/src/dos/dos_mscdex.cpp b/src/dos/dos_mscdex.cpp index 835bcfc..c211bcc 100644 --- a/src/dos/dos_mscdex.cpp +++ b/src/dos/dos_mscdex.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -113,11 +113,9 @@ public: void GetDrives (PhysPt data); void GetDriverInfo (PhysPt data); bool GetVolumeName (Bit8u subUnit, char* name); - bool GetCopyrightName (Bit16u drive, PhysPt data); - bool GetAbstractName (Bit16u drive, PhysPt data); - bool GetDocumentationName(Bit16u drive, PhysPt data); + bool GetFileName (Bit16u drive, Bit16u pos, PhysPt data); bool GetDirectoryEntry (Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error); - bool ReadVTOC (Bit16u drive, Bit16u volume, PhysPt data, Bit16u& error); + bool ReadVTOC (Bit16u drive, Bit16u volume, PhysPt data, Bit16u& offset, Bit16u& error); bool ReadSectors (Bit16u drive, Bit32u sector, Bit16u num, PhysPt data); bool ReadSectors (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data); bool ReadSectorsMSF (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data); @@ -409,9 +407,11 @@ bool CMscdex::HasDrive(Bit16u drive) { } void CMscdex::ReplaceDrive(CDROM_Interface* newCdrom, Bit8u subUnit) { - delete cdrom[subUnit]; + if (cdrom[subUnit] != NULL) { + StopAudio(subUnit); + delete cdrom[subUnit]; + } cdrom[subUnit] = newCdrom; - StopAudio(subUnit); } PhysPt CMscdex::GetDefaultBuffer(void) { @@ -506,16 +506,21 @@ bool CMscdex::GetAudioStatus(Bit8u subUnit, bool& playing, bool& pause, TMSF& st if (subUnit>=numDrives) return false; dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioStatus(playing,pause); if (dinfo[subUnit].lastResult) { - // Start - Bit32u addr = dinfo[subUnit].audioStart + 150; - start.fr = (Bit8u)(addr%75); addr/=75; - start.sec = (Bit8u)(addr%60); - start.min = (Bit8u)(addr/60); - // End - addr = dinfo[subUnit].audioEnd + 150; - end.fr = (Bit8u)(addr%75); addr/=75; - end.sec = (Bit8u)(addr%60); - end.min = (Bit8u)(addr/60); + if (playing) { + // Start + Bit32u addr = dinfo[subUnit].audioStart + 150; + start.fr = (Bit8u)(addr%75); addr/=75; + start.sec = (Bit8u)(addr%60); + start.min = (Bit8u)(addr/60); + // End + addr = dinfo[subUnit].audioEnd + 150; + end.fr = (Bit8u)(addr%75); addr/=75; + end.sec = (Bit8u)(addr%60); + end.min = (Bit8u)(addr/60); + } else { + memset(&start,0,sizeof(start)); + memset(&end,0,sizeof(end)); + } } else { playing = false; pause = false; @@ -572,7 +577,7 @@ Bit32u CMscdex::GetVolumeSize(Bit8u subUnit) { return 0; } -bool CMscdex::ReadVTOC(Bit16u drive, Bit16u volume, PhysPt data, Bit16u& error) { +bool CMscdex::ReadVTOC(Bit16u drive, Bit16u volume, PhysPt data, Bit16u& offset, Bit16u& error) { Bit8u subunit = GetSubUnit(drive); /* if (subunit>=numDrives) { error=MSCDEX_ERROR_UNKNOWN_DRIVE; @@ -584,11 +589,16 @@ bool CMscdex::ReadVTOC(Bit16u drive, Bit16u volume, PhysPt data, Bit16u& error) } char id[5]; MEM_BlockRead(data + 1, id, 5); - if (strncmp("CD001",id, 5)!=0) { - error = MSCDEX_ERROR_BAD_FORMAT; - return false; + if (strncmp("CD001", id, 5)==0) offset = 0; + else { + MEM_BlockRead(data + 9, id, 5); + if (strncmp("CDROM", id, 5)==0) offset = 8; + else { + error = MSCDEX_ERROR_BAD_FORMAT; + return false; + } } - Bit8u type = mem_readb(data); + Bit8u type = mem_readb(data + offset); error = (type == 1) ? 1 : (type == 0xFF) ? 0xFF : 0; return true; } @@ -597,12 +607,12 @@ bool CMscdex::GetVolumeName(Bit8u subUnit, char* data) { if (subUnit>=numDrives) return false; Bit16u drive = dinfo[subUnit].drive; - Bit16u error; + Bit16u offset = 0, error; bool success = false; PhysPt ptoc = GetTempBuffer(); - success = ReadVTOC(drive,0x00,ptoc,error); + success = ReadVTOC(drive,0x00,ptoc,offset,error); if (success) { - MEM_StrCopy(ptoc+40,data,31); + MEM_StrCopy(ptoc+offset+40,data,31); data[31] = 0; rtrim(data); }; @@ -610,38 +620,19 @@ bool CMscdex::GetVolumeName(Bit8u subUnit, char* data) { return success; } -bool CMscdex::GetCopyrightName(Bit16u drive, PhysPt data) { - Bit16u error; +bool CMscdex::GetFileName(Bit16u drive, Bit16u pos, PhysPt data) { + Bit16u offset = 0, error; bool success = false; PhysPt ptoc = GetTempBuffer(); - success = ReadVTOC(drive,0x00,ptoc,error); + success = ReadVTOC(drive,0x00,ptoc,offset,error); if (success) { - MEM_BlockCopy(data,ptoc+702,37); - mem_writeb(data+37,0); - }; - return success; -} - -bool CMscdex::GetAbstractName(Bit16u drive, PhysPt data) { - Bit16u error; - bool success = false; - PhysPt ptoc = GetTempBuffer(); - success = ReadVTOC(drive,0x00,ptoc,error); - if (success) { - MEM_BlockCopy(data,ptoc+739,37); - mem_writeb(data+37,0); - }; - return success; -} - -bool CMscdex::GetDocumentationName(Bit16u drive, PhysPt data) { - Bit16u error; - bool success = false; - PhysPt ptoc = GetTempBuffer(); - success = ReadVTOC(drive,0x00,ptoc,error); - if (success) { - MEM_BlockCopy(data,ptoc+776,37); - mem_writeb(data+37,0); + Bitu len; + for (len=0;len<37;len++) { + Bit8u c=mem_readb(ptoc+offset+pos+len); + if (c==0 || c==0x20) break; + } + MEM_BlockCopy(data,ptoc+offset+pos,len); + mem_writeb(data+len,0); }; return success; } @@ -698,13 +689,16 @@ bool CMscdex::GetDirectoryEntry(Bit16u drive, bool copyFlag, PhysPt pathname, Ph // read vtoc PhysPt defBuffer = GetDefaultBuffer(); if (!ReadSectors(GetSubUnit(drive),false,16,1,defBuffer)) return false; - // TODO: has to be iso 9960 MEM_StrCopy(defBuffer+1,volumeID,5); volumeID[5] = 0; bool iso = (strcmp("CD001",volumeID)==0); - if (!iso) E_Exit("MSCDEX: GetDirEntry: Not an ISO 9960 CD."); + if (!iso) { + MEM_StrCopy(defBuffer+9,volumeID,5); + if (strcmp("CDROM",volumeID)!=0) E_Exit("MSCDEX: GetDirEntry: Not an ISO 9660 or HSF CD."); + } + Bit16u offset = iso ? 156:180; // get directory position - Bitu dirEntrySector = mem_readd(defBuffer+156+2); - Bits dirSize = mem_readd(defBuffer+156+10); + Bitu dirEntrySector = mem_readd(defBuffer+offset+2); + Bits dirSize = mem_readd(defBuffer+offset+10); Bitu index; while (dirSize>0) { index = 0; @@ -723,25 +717,25 @@ bool CMscdex::GetDirectoryEntry(Bit16u drive, bool copyFlag, PhysPt pathname, Ph do { entryLength = mem_readb(defBuffer+index); if (entryLength==0) break; + if (mem_readb(defBuffer + index + (iso?0x19:0x18) ) & 4) { + // skip associated files + index += entryLength; + continue; + } nameLength = mem_readb(defBuffer+index+32); MEM_StrCopy(defBuffer+index+33,entryName,nameLength); + // strip separator and file version number + char* separator = strchr(entryName,';'); + if (separator) *separator = 0; + // strip trailing period + size_t entrylen = strlen(entryName); + if (entrylen>0 && entryName[entrylen-1]=='.') entryName[entrylen-1] = 0; + if (strcmp(entryName,useName)==0) { //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Found : %s",useName); foundName = true; break; } - /* Xcom Apocalipse searches for MUSIC. and expects to find MUSIC;1 - * All Files on the CDROM are of the kind blah;1 - */ - char* longername = strchr(entryName,';'); - if(longername) { - *longername = 0; - if (strcmp(entryName,useName)==0) { - //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Found : %s",useName); - foundName = true; - break; - } - } index += entryLength; } while (index+33<=2048); @@ -758,8 +752,9 @@ bool CMscdex::GetDirectoryEntry(Bit16u drive, bool copyFlag, PhysPt pathname, Ph memcpy( &writeBuf[1], &readBuf[0x2], 4); // 01h DWORD Logical Block Number of file start writeBuf[5] = 0;writeBuf[6] = 8; // 05h WORD size of disk in logical blocks memcpy( &writeBuf[7], &readBuf[0xa], 4); // 07h DWORD file length in bytes - memcpy( &writeBuf[0xb], &readBuf[0x12], 7); // 0bh DWORD date and time - writeBuf[0x12] = readBuf[0x19]; // 12h BYTE bit flags + memcpy( &writeBuf[0xb], &readBuf[0x12], 6); // 0bh BYTEs date and time + writeBuf[0x11] = iso ? readBuf[0x18]:0; // 11h BYTE time zone + writeBuf[0x12] = readBuf[iso ? 0x19:0x18]; // 12h BYTE bit flags writeBuf[0x13] = readBuf[0x1a]; // 13h BYTE interleave size writeBuf[0x14] = readBuf[0x1b]; // 14h BYTE interleave skip factor memcpy( &writeBuf[0x15], &readBuf[0x1c], 2); // 15h WORD volume set sequence number @@ -904,7 +899,7 @@ static CMscdex* mscdex = 0; static PhysPt curReqheaderPtr = 0; static Bit16u MSCDEX_IOCTL_Input(PhysPt buffer,Bit8u drive_unit) { - Bitu ioctl_fct = mem_readb(buffer); + Bit8u ioctl_fct = mem_readb(buffer); MSCDEX_LOG("MSCDEX: IOCTL INPUT Subfunction %02X",ioctl_fct); switch (ioctl_fct) { case 0x00 : /* Get Device Header address */ @@ -1021,7 +1016,7 @@ static Bit16u MSCDEX_IOCTL_Input(PhysPt buffer,Bit8u drive_unit) { } static Bit16u MSCDEX_IOCTL_Optput(PhysPt buffer,Bit8u drive_unit) { - Bitu ioctl_fct = mem_readb(buffer); + Bit8u ioctl_fct = mem_readb(buffer); // MSCDEX_LOG("MSCDEX: IOCTL OUTPUT Subfunction %02X",ioctl_fct); switch (ioctl_fct) { case 0x00 : // Unload /eject media @@ -1129,6 +1124,7 @@ static Bitu MSCDEX_Interrupt_Handler(void) { static bool MSCDEX_Handler(void) { if(reg_ah == 0x11) { if(reg_al == 0x00) { + if (mscdex->rootDriverHeaderSeg==0) return false; PhysPt check = PhysMake(SegValue(ss),reg_sp); if(mem_readw(check+6) == 0xDADA) { //MSCDEX sets word on stack to ADAD if it DADA on entry. @@ -1145,6 +1141,7 @@ static bool MSCDEX_Handler(void) { } if (reg_ah!=0x15) return false; // not handled here, continue chain + if (mscdex->rootDriverHeaderSeg==0) return false; // not handled if MSCDEX not installed PhysPt data = PhysMake(SegValue(es),reg_bx); MSCDEX_LOG("MSCDEX: INT 2F %04X BX= %04X CX=%04X",reg_ax,reg_bx,reg_cx); @@ -1159,23 +1156,9 @@ static bool MSCDEX_Handler(void) { mscdex->GetDriverInfo(data); return true; case 0x1502: /* Get Copyright filename */ - if (mscdex->GetCopyrightName(reg_cx,data)) { - CALLBACK_SCF(false); - } else { - reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE; - CALLBACK_SCF(true); - }; - return true; case 0x1503: /* Get Abstract filename */ - if (mscdex->GetAbstractName(reg_cx,data)) { - CALLBACK_SCF(false); - } else { - reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE; - CALLBACK_SCF(true); - }; - return true; case 0x1504: /* Get Documentation filename */ - if (mscdex->GetDocumentationName(reg_cx,data)) { + if (mscdex->GetFileName(reg_cx,702+(reg_al-2)*37,data)) { CALLBACK_SCF(false); } else { reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE; @@ -1183,8 +1166,8 @@ static bool MSCDEX_Handler(void) { }; return true; case 0x1505: { // read vtoc - Bit16u error = 0; - if (mscdex->ReadVTOC(reg_cx,reg_dx,data,error)) { + Bit16u offset = 0, error = 0; + if (mscdex->ReadVTOC(reg_cx,reg_dx,data,offset,error)) { // reg_ax = error; // return code CALLBACK_SCF(false); } else { @@ -1258,7 +1241,7 @@ static bool MSCDEX_Handler(void) { } return true; }; - LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unknwon call : %04X",reg_ax); + LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unknown call : %04X",reg_ax); return true; } @@ -1317,6 +1300,11 @@ void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit) mscdex->ReplaceDrive(cdrom, subUnit); } +Bit8u MSCDEX_GetSubUnit(char driveLetter) +{ + return mscdex->GetSubUnit(driveLetter-'A'); +} + bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name) { return mscdex->GetVolumeName(subUnit,name); diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp index 9dadc18..56928d0 100644 --- a/src/dos/dos_programs.cpp +++ b/src/dos/dos_programs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -33,8 +33,11 @@ #include "dos_system.h" #include "dos_inc.h" #include "bios.h" +#include "bios_disk.h" #include "setup.h" #include "control.h" +#include "inout.h" +#include "dma.h" #if defined(OS2) @@ -43,6 +46,12 @@ #include "os2.h" #endif +#if defined(WIN32) +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFMT)==S_IFDIR) +#endif +#endif + #if C_DEBUG Bitu DEBUG_EnableDebugger(void); #endif @@ -120,8 +129,80 @@ int MountDOSBoxDir(char DriveLetter, const char *path) { } #endif +static const char* UnmountHelper(char umount) { + int i_drive; + if (umount < '0' || umount > 3+'0') + i_drive = toupper(umount) - 'A'; + else + i_drive = umount - '0'; + + if (i_drive >= DOS_DRIVES || i_drive < 0) + return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"); + + if (i_drive < MAX_DISK_IMAGES && Drives[i_drive] == NULL && imageDiskList[i_drive] == NULL) + return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"); + + if (i_drive >= MAX_DISK_IMAGES && Drives[i_drive] == NULL) + return MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"); + + if (Drives[i_drive]) { + switch (DriveManager::UnmountDrive(i_drive)) { + case 1: return MSG_Get("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL"); + case 2: return MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS"); + } + Drives[i_drive] = 0; + mem_writeb(Real2Phys(dos.tables.mediaid)+i_drive*9,0); + if (i_drive == DOS_GetDefaultDrive()) { + DOS_SetDrive(ZDRIVE_NUM); + } + + } + + if (i_drive < MAX_DISK_IMAGES && imageDiskList[i_drive]) { + delete imageDiskList[i_drive]; + imageDiskList[i_drive] = NULL; + } + + return MSG_Get("PROGRAM_MOUNT_UMOUNT_SUCCESS"); +} + class MOUNT : public Program { public: + void Move_Z(char new_z) { + char newz_drive = (char) toupper(new_z); + int i_newz = newz_drive - 'A'; + if (i_newz >= 0 && i_newz < DOS_DRIVES-1 && !Drives[i_newz]) { + ZDRIVE_NUM = i_newz; + /* remap drives */ + Drives[i_newz] = Drives[25]; + Drives[25] = 0; + if (!first_shell) return; //Should not be possible + /* Update environment */ + std::string line = ""; + char ppp[2] = {newz_drive,0}; + std::string tempenv = ppp; tempenv += ":\\"; + if (first_shell->GetEnvStr("PATH",line)){ + std::string::size_type idx = line.find('='); + std::string value = line.substr(idx +1 , std::string::npos); + while ( (idx = value.find("Z:\\")) != std::string::npos || + (idx = value.find("z:\\")) != std::string::npos ) + value.replace(idx,3,tempenv); + line = value; + } + if (!line.size()) line = tempenv; + first_shell->SetEnv("PATH",line.c_str()); + tempenv += "COMMAND.COM"; + first_shell->SetEnv("COMSPEC",tempenv.c_str()); + + /* Update batch file if running from Z: (very likely: autoexec) */ + if(first_shell->bf) { + std::string &name = first_shell->bf->filename; + if(name.length() >2 && name[0] == 'Z' && name[1] == ':') name[0] = newz_drive; + } + /* Change the active drive */ + if (DOS_GetDefaultDrive() == 25) DOS_SetDrive(i_newz); + } + } void ListMounts(void) { char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr; /* Command uses dta so set it to our internal dta */ @@ -136,7 +217,7 @@ public: for (int d = 0;d < DOS_DRIVES;d++) { if (!Drives[d]) continue; - char root[4] = {'A'+d,':','\\',0}; + char root[7] = {static_cast('A'+d),':','\\','*','.','*',0}; bool ret = DOS_FindFirst(root,DOS_ATTR_VOLUME); if (ret) { dta.GetResult(name,size,date,time,attr); @@ -176,70 +257,22 @@ public: WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); return; } + bool path_relative_to_last_config = false; + if (cmd->FindExist("-pr",true)) path_relative_to_last_config = true; /* Check for unmounting */ if (cmd->FindString("-u",umount,false)) { - umount[0] = toupper(umount[0]); - int i_drive = umount[0]-'A'; - if (i_drive < DOS_DRIVES && i_drive >= 0 && Drives[i_drive]) { - switch (DriveManager::UnmountDrive(i_drive)) { - case 0: - Drives[i_drive] = 0; - if(i_drive == DOS_GetDefaultDrive()) - DOS_SetDrive(ZDRIVE_NUM); - WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_SUCCESS"),umount[0]); - break; - case 1: - WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL")); - break; - case 2: - WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS")); - break; - } - } else { - WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"),umount[0]); - } + WriteOut(UnmountHelper(umount[0]), toupper(umount[0])); return; } /* Check for moving Z: */ /* Only allowing moving it once. It is merely a convenience added for the wine team */ if (ZDRIVE_NUM == 25 && cmd->FindString("-z", newz,false)) { - newz[0] = toupper(newz[0]); - int i_newz = newz[0] - 'A'; - if (i_newz >= 0 && i_newz < DOS_DRIVES-1 && !Drives[i_newz]) { - ZDRIVE_NUM = i_newz; - /* remap drives */ - Drives[i_newz] = Drives[25]; - Drives[25] = 0; - DOS_Shell *fs = static_cast(first_shell); //dynamic ? - /* Update environment */ - std::string line = ""; - char ppp[2] = {newz[0],0}; - std::string tempenv = ppp; tempenv += ":\\"; - if (fs->GetEnvStr("PATH",line)){ - std::string::size_type idx = line.find('='); - std::string value = line.substr(idx +1 , std::string::npos); - while ( (idx = value.find("Z:\\")) != std::string::npos || - (idx = value.find("z:\\")) != std::string::npos ) - value.replace(idx,3,tempenv); - line = value; - } - if (!line.size()) line = tempenv; - fs->SetEnv("PATH",line.c_str()); - tempenv += "COMMAND.COM"; - fs->SetEnv("COMSPEC",tempenv.c_str()); - - /* Update batch file if running from Z: (very likely: autoexec) */ - if(fs->bf) { - std::string &name = fs->bf->filename; - if(name.length() >2 && name[0] == 'Z' && name[1] == ':') name[0] = newz[0]; - } - /* Change the active drive */ - if (DOS_GetDefaultDrive() == 25) DOS_SetDrive(i_newz); - } + Move_Z(newz[0]); return; } + /* Show list of cdroms */ if (cmd->FindExist("-cd",false)) { int num = SDL_CDNumDrives(); @@ -253,20 +286,20 @@ public: std::string type="dir"; cmd->FindString("-t",type,true); bool iscdrom = (type =="cdrom"); //Used for mscdex bug cdrom label name emulation - if (type=="floppy" || type=="dir" || type=="cdrom") { - Bit16u sizes[4]; + if (type=="floppy" || type=="dir" || type=="cdrom" || type =="overlay") { + Bit16u sizes[4] ={0}; Bit8u mediaid; - std::string str_size; + std::string str_size = ""; if (type=="floppy") { str_size="512,1,2880,2880";/* All space free */ mediaid=0xF0; /* Floppy 1.44 media */ - } else if (type=="dir") { + } else if (type=="dir" || type == "overlay") { // 512*32*32765==~500MB total size // 512*32*16000==~250MB total free size str_size="512,32,32765,16000"; mediaid=0xF8; /* Hard Disk */ } else if (type=="cdrom") { - str_size="2048,1,32765,0"; + str_size="2048,1,65535,0"; mediaid=0xF8; /* Hard Disk */ } else { WriteOut(MSG_Get("PROGAM_MOUNT_ILL_TYPE"),type.c_str()); @@ -292,30 +325,43 @@ public: } cmd->FindString("-size",str_size,true); - char number[20];const char * scan=str_size.c_str(); - Bitu index=0;Bitu count=0; + char number[21] = { 0 };const char * scan = str_size.c_str(); + Bitu index = 0;Bitu count = 0; /* Parse the str_size string */ - while (*scan) { + while (*scan && index < 20 && count < 4) { if (*scan==',') { - number[index]=0;sizes[count++]=atoi(number); - index=0; - } else number[index++]=*scan; + number[index] = 0; + sizes[count++] = atoi(number); + index = 0; + } else number[index++] = *scan; scan++; } - number[index]=0;sizes[count++]=atoi(number); + if (count < 4) { + number[index] = 0; //always goes correct as index is max 20 at this point. + sizes[count] = atoi(number); + } // get the drive letter cmd->FindCommand(1,temp_line); if ((temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) goto showusage; - drive=toupper(temp_line[0]); - if (!isalpha(drive)) goto showusage; + int i_drive = toupper(temp_line[0]); + if (!isalpha(i_drive)) goto showusage; + if ((i_drive - 'A') >= DOS_DRIVES || (i_drive-'A') < 0 ) goto showusage; + drive = static_cast(i_drive); if (!cmd->FindCommand(2,temp_line)) goto showusage; if (!temp_line.size()) goto showusage; + if(path_relative_to_last_config && control->configfiles.size() && !Cross::IsPathAbsolute(temp_line)) { + std::string lastconfigdir(control->configfiles[control->configfiles.size()-1]); + std::string::size_type pos = lastconfigdir.rfind(CROSS_FILESPLIT); + if(pos == std::string::npos) pos = 0; //No directory then erase string + lastconfigdir.erase(pos); + if (lastconfigdir.length()) temp_line = lastconfigdir + CROSS_FILESPLIT + temp_line; + } struct stat test; //Win32 : strip tailing backslashes //os2: some special drive check - //rest: substiture ~ for home + //rest: substitute ~ for home bool failed = false; #if defined (WIN32) || defined(OS2) /* Removing trailing backslash if not root dir so stat will succeed */ @@ -356,7 +402,7 @@ public: return; } /* Not a switch so a normal directory/file */ - if (!(test.st_mode & S_IFDIR)) { + if (!S_ISDIR(test.st_mode)) { #ifdef OS2 HFILE cdrom_fd = 0; ULONG ulAction = 0; @@ -432,7 +478,39 @@ public: #else if(temp_line == "/") WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_OTHER")); #endif - newdrive=new localDrive(temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid); + if(type == "overlay") { + //Ensure that the base drive exists: + if (!Drives[drive-'A']) { + WriteOut("No basedrive mounted yet!"); + return; + } + localDrive* ldp = dynamic_cast(Drives[drive-'A']); + cdromDrive* cdp = dynamic_cast(Drives[drive-'A']); + if (!ldp || cdp) { + WriteOut("Basedrive not compatible"); + return; + } + std::string base = ldp->getBasedir(); + Bit8u o_error = 0; + newdrive = new Overlay_Drive(base.c_str(),temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid,o_error); + //Erase old drive on success + if (newdrive) { + if (o_error) { + if (o_error == 1) WriteOut("No mixing of relative and absolute paths. Overlay failed."); + else if (o_error == 2) WriteOut("overlay directory can not be the same as underlying file system."); + else WriteOut("Something went wrong"); + delete newdrive; + return; + } + delete Drives[drive-'A']; + Drives[drive-'A'] = 0; + } else { + WriteOut("overlaydrive construction failed."); + return; + } + } else { + newdrive=new localDrive(temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid); + } } } else { WriteOut(MSG_Get("PROGRAM_MOUNT_ILL_TYPE"),type.c_str()); @@ -446,16 +524,17 @@ public: if (!newdrive) E_Exit("DOS:Can't create drive"); Drives[drive-'A']=newdrive; /* Set the correct media byte in the table */ - mem_writeb(Real2Phys(dos.tables.mediaid)+(drive-'A')*2,newdrive->GetMediaByte()); - WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"),drive,newdrive->GetInfo()); + mem_writeb(Real2Phys(dos.tables.mediaid)+(drive-'A')*9,newdrive->GetMediaByte()); + if (type != "overlay") WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"),drive,newdrive->GetInfo()); + else WriteOut("Overlay %s on drive %c mounted.\n",temp_line.c_str(),drive); /* check if volume label is given and don't allow it to updated in the future */ if (cmd->FindString("-label",label,true)) newdrive->dirCache.SetLabel(label.c_str(),iscdrom,false); /* For hard drives set the label to DRIVELETTER_Drive. * For floppy drives set the label to DRIVELETTER_Floppy. * This way every drive except cdroms should get a label.*/ - else if(type == "dir") { + else if(type == "dir" || type == "overlay") { label = drive; label += "_DRIVE"; - newdrive->dirCache.SetLabel(label.c_str(),iscdrom,true); + newdrive->dirCache.SetLabel(label.c_str(),iscdrom,false); } else if(type == "floppy") { label = drive; label += "_FLOPPY"; newdrive->dirCache.SetLabel(label.c_str(),iscdrom,true); @@ -492,9 +571,7 @@ public: Bit16u seg,blocks;blocks=0xffff; DOS_AllocateMemory(&seg,&blocks); - if ((machine==MCH_PCJR) && (real_readb(0x2000,0)==0x5a) && (real_readw(0x2000,1)==0) && (real_readw(0x2000,3)==0x7ffe)) { - WriteOut(MSG_Get("PROGRAM_MEM_CONVEN"),0x7ffe*16/1024); - } else WriteOut(MSG_Get("PROGRAM_MEM_CONVEN"),blocks*16/1024); + WriteOut(MSG_Get("PROGRAM_MEM_CONVEN"),blocks*16/1024); if (umb_start!=0xffff) { DOS_LinkUMBsToMemChain(1); @@ -604,9 +681,9 @@ private: //File not found on mounted filesystem. Try regular filesystem std::string filename_s(filename); Cross::ResolveHomedir(filename_s); - tmpfile = fopen(filename_s.c_str(),"rb+"); + tmpfile = fopen_wrap(filename_s.c_str(),"rb+"); if(!tmpfile) { - if( (tmpfile = fopen(filename_s.c_str(),"rb")) ) { + if( (tmpfile = fopen_wrap(filename_s.c_str(),"rb")) ) { //File exists; So can't be opened in correct mode => error 2 // fclose(tmpfile); // if(tryload) error = 2; @@ -659,7 +736,7 @@ public: FILE *usefile_1=NULL; FILE *usefile_2=NULL; Bitu i=0; - Bit32u floppysize; + Bit32u floppysize=0; Bit32u rombytesize_1=0; Bit32u rombytesize_2=0; Bit8u drive = 'A'; @@ -702,13 +779,16 @@ public: i++; continue; } - + + if ( i >= MAX_SWAPPABLE_DISKS ) { + return; //TODO give a warning. + } WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_OPEN"), temp_line.c_str()); Bit32u rombytesize; FILE *usefile = getFSFile(temp_line.c_str(), &floppysize, &rombytesize); if(usefile != NULL) { if(diskSwap[i] != NULL) delete diskSwap[i]; - diskSwap[i] = new imageDisk(usefile, (Bit8u *)temp_line.c_str(), floppysize, false); + diskSwap[i] = new imageDisk(usefile, temp_line.c_str(), floppysize, false); if (usefile_1==NULL) { usefile_1=usefile; rombytesize_1=rombytesize; @@ -894,6 +974,9 @@ public: WriteOut(MSG_Get("PROGRAM_BOOT_BOOT"), drive); for(i=0;i<512;i++) real_writeb(0, 0x7c00 + i, bootarea.rawdata[i]); + /* create appearance of floppy drive DMA usage (Demon's Forge) */ + if (!IS_TANDY_ARCH && floppysize!=0) GetDMAChannel(2)->tcount=true; + /* revector some dos-allocated interrupts */ real_writed(0,0x01*4,0xf000ff53); real_writed(0,0x03*4,0xf000ff53); @@ -920,61 +1003,140 @@ static void BOOT_ProgramStart(Program * * make) { } -#if C_DEBUG -class LDGFXROM : public Program { +class LOADROM : public Program { public: void Run(void) { - if (!(cmd->FindCommand(1, temp_line))) return; + if (!(cmd->FindCommand(1, temp_line))) { + WriteOut(MSG_Get("PROGRAM_LOADROM_SPECIFY_FILE")); + return; + } Bit8u drive; char fullname[DOS_PATHLENGTH]; - localDrive* ldp=0; if (!DOS_MakeName((char *)temp_line.c_str(),fullname,&drive)) return; - try { + try { + /* try to read ROM file into buffer */ ldp=dynamic_cast(Drives[drive]); if(!ldp) return; FILE *tmpfile = ldp->GetSystemFilePtr(fullname, "rb"); if(tmpfile == NULL) { - LOG_MSG("BIOS file not accessible."); + WriteOut(MSG_Get("PROGRAM_LOADROM_CANT_OPEN")); return; } fseek(tmpfile, 0L, SEEK_END); - if (ftell(tmpfile)>0x10000) { - LOG_MSG("BIOS file too large."); + if (ftell(tmpfile)>0x8000) { + WriteOut(MSG_Get("PROGRAM_LOADROM_TOO_LARGE")); + fclose(tmpfile); return; } fseek(tmpfile, 0L, SEEK_SET); - - PhysPt rom_base=PhysMake(0xc000,0); - - Bit8u vga_buffer[0x10000]; - Bitu data_written=0; - Bitu data_read=fread(vga_buffer, 1, 0x10000, tmpfile); - for (Bitu ct=0; ct= 0x4000 && rom_buffer[0] == 0x55 && rom_buffer[1] == 0xaa && + (rom_buffer[3] & 0xfc) == 0xe8 && strncmp((char*)(&rom_buffer[0x1e]), "IBM", 3) == 0) { + + if (!IS_EGAVGA_ARCH) { + WriteOut(MSG_Get("PROGRAM_LOADROM_INCOMPATIBLE")); + return; + } + rom_base = PhysMake(0xc000, 0); // video BIOS + } + else if (data_read == 0x8000 && rom_buffer[0] == 0xe9 && rom_buffer[1] == 0x8f && + rom_buffer[2] == 0x7e && strncmp((char*)(&rom_buffer[0x4cd4]), "IBM", 3) == 0) { + + rom_base = PhysMake(0xf600, 0); // BASIC + } + + if (rom_base) { + /* write buffer into ROM */ + for (Bitu i=0; iFindCommand(1, temp_line))) { + WriteOut("Must specify BIOS file to load.\n"); + return; + } + + Bit8u drive; + char fullname[DOS_PATHLENGTH]; + localDrive* ldp = 0; + if (!DOS_MakeName((char *)temp_line.c_str(), fullname, &drive)) return; + + try { + /* try to read ROM file into buffer */ + ldp = dynamic_cast(Drives[drive]); + if (!ldp) return; + + FILE *tmpfile = ldp->GetSystemFilePtr(fullname, "rb"); + if (tmpfile == NULL) { + WriteOut("Can't open a file"); + return; + } + fseek(tmpfile, 0L, SEEK_END); + if (ftell(tmpfile) > 64 * 1024) { + WriteOut("BIOS File too large"); + fclose(tmpfile); + return; + } + fseek(tmpfile, 0L, SEEK_SET); + Bit8u buffer[64*1024]; + Bitu data_read = fread(buffer, 1, sizeof( buffer), tmpfile); + fclose(tmpfile); + + Bit32u rom_base = PhysMake(0xf000, 0); // override regular dosbox bios + /* write buffer into ROM */ + for (Bitu i = 0; i < data_read; i++) phys_writeb(rom_base + i, buffer[i]); + + //Start executing this bios + memset(&cpu_regs, 0, sizeof(cpu_regs)); + memset(&Segs, 0, sizeof(Segs)); + + + SegSet16(cs, 0xf000); + reg_eip = 0xfff0; + } + catch (...) { + return; + } + } +}; + +static void BIOSTEST_ProgramStart(Program * * make) { + *make = new BIOSTEST; +} + +#endif // LOADFIX @@ -1139,237 +1301,248 @@ public: WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); return; } - DOS_Drive * newdrive = NULL; - imageDisk * newImage = NULL; - Bit32u imagesize; + char drive; std::string label; std::vector paths; std::string umount; /* Check for unmounting */ if (cmd->FindString("-u",umount,false)) { - umount[0] = toupper(umount[0]); - int i_drive = umount[0]-'A'; - if (i_drive < DOS_DRIVES && i_drive >= 0 && Drives[i_drive]) { - switch (DriveManager::UnmountDrive(i_drive)) { - case 0: - Drives[i_drive] = 0; - if (i_drive == DOS_GetDefaultDrive()) - DOS_SetDrive(toupper('Z') - 'A'); - WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_SUCCESS"),umount[0]); - break; - case 1: - WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NO_VIRTUAL")); - break; - case 2: - WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS")); - break; - } - } else { - WriteOut(MSG_Get("PROGRAM_MOUNT_UMOUNT_NOT_MOUNTED"),umount[0]); - } + WriteOut(UnmountHelper(umount[0]), toupper(umount[0])); return; } - std::string type="hdd"; - std::string fstype="fat"; + std::string type = "hdd"; + std::string fstype = "fat"; cmd->FindString("-t",type,true); cmd->FindString("-fs",fstype,true); if(type == "cdrom") type = "iso"; //Tiny hack for people who like to type -t cdrom - Bit8u mediaid; - if (type=="floppy" || type=="hdd" || type=="iso") { - Bit16u sizes[4]; - bool imgsizedetect=false; - - std::string str_size; - mediaid=0xF8; - if (type=="floppy") { - mediaid=0xF0; - } else if (type=="iso") { - str_size=="2048,1,60000,0"; // ignored, see drive_iso.cpp (AllocationInfo) - mediaid=0xF8; - fstype = "iso"; - } - cmd->FindString("-size",str_size,true); - if ((type=="hdd") && (str_size.size()==0)) { - imgsizedetect=true; - } else { - char number[20]; - const char * scan=str_size.c_str(); - Bitu index=0;Bitu count=0; - - while (*scan) { - if (*scan==',') { - number[index]=0;sizes[count++]=atoi(number); - index=0; - } else number[index++]=*scan; - scan++; - } - number[index]=0;sizes[count++]=atoi(number); - } - - if(fstype=="fat" || fstype=="iso") { - // get the drive letter - if (!cmd->FindCommand(1,temp_line) || (temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) { - WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE")); - return; - } - drive=toupper(temp_line[0]); - if (!isalpha(drive)) { - WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE")); - return; - } - } else if (fstype=="none") { - cmd->FindCommand(1,temp_line); - if ((temp_line.size() > 1) || (!isdigit(temp_line[0]))) { - WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2")); - return; - } - drive=temp_line[0]; - if ((drive<'0') || (drive>3+'0')) { - WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2")); - return; - } - } else { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FORMAT_UNSUPPORTED"),fstype.c_str()); - return; - } - - // find all file parameters, assuming that all option parameters have been removed - while(cmd->FindCommand((unsigned int)(paths.size() + 2), temp_line) && temp_line.size()) { - - struct stat test; - if (stat(temp_line.c_str(),&test)) { - //See if it works if the ~ are written out - std::string homedir(temp_line); - Cross::ResolveHomedir(homedir); - if(!stat(homedir.c_str(),&test)) { - temp_line = homedir; - } else { - // convert dosbox filename to system filename - char fullname[CROSS_LEN]; - char tmp[CROSS_LEN]; - safe_strncpy(tmp, temp_line.c_str(), CROSS_LEN); - - Bit8u dummy; - if (!DOS_MakeName(tmp, fullname, &dummy) || strncmp(Drives[dummy]->GetInfo(),"local directory",15)) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_NON_LOCAL_DRIVE")); - return; - } - - localDrive *ldp = dynamic_cast(Drives[dummy]); - if (ldp==NULL) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND")); - return; - } - ldp->GetSystemFilename(tmp, fullname); - temp_line = tmp; - - if (stat(temp_line.c_str(),&test)) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND")); - return; - } - } - } - if ((test.st_mode & S_IFDIR)) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT")); - return; - } - paths.push_back(temp_line); - } - if (paths.size() == 0) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_FILE")); - return; - } - if (paths.size() == 1) - temp_line = paths[0]; - if (paths.size() > 1 && fstype != "iso") { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MULTIPLE_NON_CUEISO_FILES")); - return; - } - - if(fstype=="fat") { - if (imgsizedetect) { - FILE * diskfile = fopen(temp_line.c_str(), "rb+"); - if(!diskfile) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE")); - return; - } - fseek(diskfile, 0L, SEEK_END); - Bit32u fcsize = (Bit32u)(ftell(diskfile) / 512L); - Bit8u buf[512]; - fseek(diskfile, 0L, SEEK_SET); - if (fread(buf,sizeof(Bit8u),512,diskfile)<512) { - fclose(diskfile); - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE")); - return; - } - fclose(diskfile); - if ((buf[510]!=0x55) || (buf[511]!=0xaa)) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY")); - return; - } - Bitu sectors=(Bitu)(fcsize/(16*63)); - if (sectors*16*63!=fcsize) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY")); - return; - } - sizes[0]=512; sizes[1]=63; sizes[2]=16; sizes[3]=sectors; - LOG_MSG("autosized image file: %d:%d:%d:%d",sizes[0],sizes[1],sizes[2],sizes[3]); - } - - newdrive=new fatDrive(temp_line.c_str(),sizes[0],sizes[1],sizes[2],sizes[3],0); - if(!(dynamic_cast(newdrive))->created_successfully) { - delete newdrive; - newdrive = 0; - } - } else if (fstype=="iso") { - } else { - FILE *newDisk = fopen(temp_line.c_str(), "rb+"); - fseek(newDisk,0L, SEEK_END); - imagesize = (ftell(newDisk) / 1024); - - newImage = new imageDisk(newDisk, (Bit8u *)temp_line.c_str(), imagesize, (imagesize > 2880)); - if(imagesize>2880) newImage->Set_Geometry(sizes[2],sizes[3],sizes[1],sizes[0]); - } - } else { + //Check type and exit early. + if (type != "floppy" && type != "hdd" && type != "iso") { WriteOut(MSG_Get("PROGRAM_IMGMOUNT_TYPE_UNSUPPORTED"),type.c_str()); return; } - if(fstype=="fat") { - if (Drives[drive-'A']) { - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED")); - if (newdrive) delete newdrive; + Bit16u sizes[4] = {0}; + bool imgsizedetect = false; + + std::string str_size = ""; + Bit8u mediaid = 0xF8; + + if (type == "floppy") { + mediaid = 0xF0; + } else if (type == "iso") { + //str_size="2048,1,65535,0"; // ignored, see drive_iso.cpp (AllocationInfo) + mediaid = 0xF8; + fstype = "iso"; + } + + cmd->FindString("-size",str_size,true); + if ((type=="hdd") && (str_size.size()==0)) { + imgsizedetect = true; + } else { + char number[21] = { 0 };const char * scan = str_size.c_str(); + Bitu index = 0;Bitu count = 0; + /* Parse the str_size string */ + while (*scan && index < 20 && count < 4) { + if (*scan==',') { + number[index] = 0; + sizes[count++] = atoi(number); + index = 0; + } else number[index++] = *scan; + scan++; + } + if (count < 4) { + number[index] = 0; //always goes correct as index is max 20 at this point. + sizes[count] = atoi(number); + } + } + + if(fstype=="fat" || fstype=="iso") { + // get the drive letter + if (!cmd->FindCommand(1,temp_line) || (temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) { + WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE")); return; } - if (!newdrive) {WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE"));return;} - Drives[drive-'A']=newdrive; - // Set the correct media byte in the table - mem_writeb(Real2Phys(dos.tables.mediaid)+(drive-'A')*2,mediaid); - WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"),drive,temp_line.c_str()); - if(((fatDrive *)newdrive)->loadedDisk->hardDrive) { - if(imageDiskList[2] == NULL) { - imageDiskList[2] = ((fatDrive *)newdrive)->loadedDisk; - updateDPT(); + int i_drive = toupper(temp_line[0]); + if (!isalpha(i_drive) || (i_drive - 'A') >= DOS_DRIVES || (i_drive - 'A') <0) { + WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_DRIVE")); + return; + } + drive = static_cast(i_drive); + } else if (fstype=="none") { + cmd->FindCommand(1,temp_line); + if ((temp_line.size() > 1) || (!isdigit(temp_line[0]))) { + WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2")); + return; + } + drive = temp_line[0]; + if ((drive<'0') || (drive>=(MAX_DISK_IMAGES+'0'))) { + WriteOut_NoParsing(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY2")); + return; + } + } else { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FORMAT_UNSUPPORTED"),fstype.c_str()); + return; + } + + // find all file parameters, assuming that all option parameters have been removed + while(cmd->FindCommand((unsigned int)(paths.size() + 2), temp_line) && temp_line.size()) { + + struct stat test; + if (stat(temp_line.c_str(),&test)) { + //See if it works if the ~ are written out + std::string homedir(temp_line); + Cross::ResolveHomedir(homedir); + if(!stat(homedir.c_str(),&test)) { + temp_line = homedir; + } else { + // convert dosbox filename to system filename + char fullname[CROSS_LEN]; + char tmp[CROSS_LEN]; + safe_strncpy(tmp, temp_line.c_str(), CROSS_LEN); + + Bit8u dummy; + if (!DOS_MakeName(tmp, fullname, &dummy) || strncmp(Drives[dummy]->GetInfo(),"local directory",15)) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_NON_LOCAL_DRIVE")); + return; + } + + localDrive *ldp = dynamic_cast(Drives[dummy]); + if (ldp==NULL) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND")); + return; + } + ldp->GetSystemFilename(tmp, fullname); + temp_line = tmp; + + if (stat(temp_line.c_str(),&test)) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_FILE_NOT_FOUND")); + return; + } + } + } + if (S_ISDIR(test.st_mode)) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT")); + return; + } + paths.push_back(temp_line); + } + if (paths.size() == 0) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_FILE")); + return; + } + if (paths.size() == 1) + temp_line = paths[0]; + + if(fstype=="fat") { + if (imgsizedetect) { + FILE * diskfile = fopen_wrap(temp_line.c_str(), "rb+"); + if (!diskfile) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE")); return; } - if(imageDiskList[3] == NULL) { - imageDiskList[3] = ((fatDrive *)newdrive)->loadedDisk; - updateDPT(); + fseek(diskfile, 0L, SEEK_END); + Bit32u fcsize = (Bit32u)(ftell(diskfile) / 512L); + Bit8u buf[512]; + fseek(diskfile, 0L, SEEK_SET); + if (fread(buf,sizeof(Bit8u),512,diskfile)<512) { + fclose(diskfile); + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE")); + return; + } + fclose(diskfile); + if ((buf[510]!=0x55) || (buf[511]!=0xaa)) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY")); + return; + } + Bitu sectors=(Bitu)(fcsize/(16*63)); + if (sectors*16*63!=fcsize) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY")); + return; + } + sizes[0]=512; sizes[1]=63; sizes[2]=16; sizes[3]=sectors; + + LOG_MSG("autosized image file: %d:%d:%d:%d",sizes[0],sizes[1],sizes[2],sizes[3]); + } + + if (Drives[drive-'A']) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED")); + return; + } + + std::vector imgDisks; + std::vector::size_type i; + std::vector::size_type ct; + + for (i = 0; i < paths.size(); i++) { + DOS_Drive* newDrive = new fatDrive(paths[i].c_str(),sizes[0],sizes[1],sizes[2],sizes[3],0); + imgDisks.push_back(newDrive); + if(!(dynamic_cast(newDrive))->created_successfully) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_CANT_CREATE")); + for(ct = 0; ct < imgDisks.size(); ct++) { + delete imgDisks[ct]; + } return; } } - if(!((fatDrive *)newdrive)->loadedDisk->hardDrive) { - imageDiskList[0] = ((fatDrive *)newdrive)->loadedDisk; + + // Update DriveManager + for(ct = 0; ct < imgDisks.size(); ct++) { + DriveManager::AppendDisk(drive - 'A', imgDisks[ct]); + } + DriveManager::InitializeDrive(drive - 'A'); + + // Set the correct media byte in the table + mem_writeb(Real2Phys(dos.tables.mediaid) + (drive - 'A') * 9, mediaid); + + /* Command uses dta so set it to our internal dta */ + RealPt save_dta = dos.dta(); + dos.dta(dos.tables.tempdta); + + for(ct = 0; ct < imgDisks.size(); ct++) { + DriveManager::CycleDisks(drive - 'A', (ct == (imgDisks.size() - 1))); + + char root[7] = {drive,':','\\','*','.','*',0}; + DOS_FindFirst(root, DOS_ATTR_VOLUME); // force obtaining the label and saving it in dirCache + } + dos.dta(save_dta); + + std::string tmp(paths[0]); + for (i = 1; i < paths.size(); i++) { + tmp += "; " + paths[i]; + } + WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive, tmp.c_str()); + + if (paths.size() == 1) { + DOS_Drive * newdrive = imgDisks[0]; + switch (drive - 'A') { + case 0: + case 1: + if(!((fatDrive *)newdrive)->loadedDisk->hardDrive) { + if(imageDiskList[drive - 'A'] != NULL) delete imageDiskList[drive - 'A']; + imageDiskList[drive - 'A'] = ((fatDrive *)newdrive)->loadedDisk; + } + break; + case 2: + case 3: + if(((fatDrive *)newdrive)->loadedDisk->hardDrive) { + if(imageDiskList[drive - 'A'] != NULL) delete imageDiskList[drive - 'A']; + imageDiskList[drive - 'A'] = ((fatDrive *)newdrive)->loadedDisk; + updateDPT(); + } + break; + } } } else if (fstype=="iso") { + if (Drives[drive-'A']) { WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED")); return; } + MSCDEX_SetCDInterface(CDROM_USE_SDL, -1); // create new drives for all images std::vector isoDisks; @@ -1404,7 +1577,7 @@ public: DriveManager::InitializeDrive(drive - 'A'); // Set the correct media byte in the table - mem_writeb(Real2Phys(dos.tables.mediaid) + (drive - 'A') * 2, mediaid); + mem_writeb(Real2Phys(dos.tables.mediaid) + (drive - 'A') * 9, mediaid); // Print status message (success) WriteOut(MSG_Get("MSCDEX_SUCCESS")); @@ -1413,15 +1586,33 @@ public: tmp += "; " + paths[i]; } WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive, tmp.c_str()); - - } else if (fstype=="none") { - if(imageDiskList[drive-'0'] != NULL) delete imageDiskList[drive-'0']; - imageDiskList[drive-'0'] = newImage; + + } else if (fstype == "none") { + FILE *newDisk = fopen_wrap(temp_line.c_str(), "rb+"); + if (!newDisk) { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_IMAGE")); + return; + } + fseek(newDisk,0L, SEEK_END); + Bit32u imagesize = (ftell(newDisk) / 1024); + const bool hdd = (imagesize > 2880); + //Seems to make sense to require a valid geometry.. + if (hdd && sizes[0] == 0 && sizes[1] == 0 && sizes[2] == 0 && sizes[3] == 0) { + fclose(newDisk); + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_SPECIFY_GEOMETRY")); + return; + } + + imageDisk * newImage = new imageDisk(newDisk, temp_line.c_str(), imagesize, hdd); + + if (hdd) newImage->Set_Geometry(sizes[2],sizes[3],sizes[1],sizes[0]); + if(imageDiskList[drive - '0'] != NULL) delete imageDiskList[drive - '0']; + imageDiskList[drive - '0'] = newImage; updateDPT(); - WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT_NUMBER"),drive-'0',temp_line.c_str()); + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_MOUNT_NUMBER"),drive - '0',temp_line.c_str()); } - // check if volume label is given. becareful for cdrom + // check if volume label is given. be careful for cdrom //if (cmd->FindString("-label",label,true)) newdrive->dirCache.SetLabel(label.c_str()); return; } @@ -1542,12 +1733,13 @@ void DOS_SetupPrograms(void) { MSG_Add("PROGRAM_LOADFIX_ERROR","Memory allocation error.\n"); MSG_Add("MSCDEX_SUCCESS","MSCDEX installed.\n"); - MSG_Add("MSCDEX_ERROR_MULTIPLE_CDROMS","MSCDEX: Failure: Drive-letters of multiple CDRom-drives have to be continuous.\n"); + MSG_Add("MSCDEX_ERROR_MULTIPLE_CDROMS","MSCDEX: Failure: Drive-letters of multiple CD-ROM drives have to be continuous.\n"); MSG_Add("MSCDEX_ERROR_NOT_SUPPORTED","MSCDEX: Failure: Not yet supported.\n"); + MSG_Add("MSCDEX_ERROR_PATH","MSCDEX: Specified location is not a CD-ROM drive.\n"); MSG_Add("MSCDEX_ERROR_OPEN","MSCDEX: Failure: Invalid file or unable to open.\n"); - MSG_Add("MSCDEX_TOO_MANY_DRIVES","MSCDEX: Failure: Too many CDRom-drives (max: 5). MSCDEX Installation failed.\n"); + MSG_Add("MSCDEX_TOO_MANY_DRIVES","MSCDEX: Failure: Too many CD-ROM drives (max: 5). MSCDEX Installation failed.\n"); MSG_Add("MSCDEX_LIMITED_SUPPORT","MSCDEX: Mounted subdirectory: limited support.\n"); - MSG_Add("MSCDEX_INVALID_FILEFORMAT","MSCDEX: Failure: File is either no iso/cue image or contains errors.\n"); + MSG_Add("MSCDEX_INVALID_FILEFORMAT","MSCDEX: Failure: File is either no ISO/CUE image or contains errors.\n"); MSG_Add("MSCDEX_UNKNOWN_ERROR","MSCDEX: Failure: Unknown error.\n"); MSG_Add("PROGRAM_RESCAN_SUCCESS","Drive cache cleared.\n"); @@ -1561,7 +1753,7 @@ void DOS_SetupPrograms(void) { "For information about special keys type \033[34;1mintro special\033[0m\n" "For more information about DOSBox, go to \033[34;1mhttp://www.dosbox.com/wiki\033[0m\n" "\n" - "\033[31;1mDOSBox will stop/exit without a warning if an error occured!\033[0m\n" + "\033[31;1mDOSBox will stop/exit without a warning if an error occurred!\033[0m\n" "\n" "\n" ); @@ -1575,9 +1767,9 @@ void DOS_SetupPrograms(void) { "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n" - "\xBA \033[32mmount c c:\\dosprogs\\\033[37m will create a C drive with c:\\dosprogs as contents.\xBA\n" + "\xBA \033[32mmount c c:\\dosgames\\\033[37m will create a C drive with c:\\dosgames as contents.\xBA\n" "\xBA \xBA\n" - "\xBA \033[32mc:\\dosprogs\\\033[37m is an example. Replace it with your own games directory. \033[37m \xBA\n" + "\xBA \033[32mc:\\dosgames\\\033[37m is an example. Replace it with your own games directory. \033[37m \xBA\n" "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n" @@ -1586,9 +1778,9 @@ void DOS_SetupPrograms(void) { "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n" - "\xBA \033[32mmount c ~/dosprogs\033[37m will create a C drive with ~/dosprogs as contents.\xBA\n" + "\xBA \033[32mmount c ~/dosgames\033[37m will create a C drive with ~/dosgames as contents.\xBA\n" "\xBA \xBA\n" - "\xBA \033[32m~/dosprogs\033[37m is an example. Replace it with your own games directory.\033[37m \xBA\n" + "\xBA \033[32m~/dosgames\033[37m is an example. Replace it with your own games directory.\033[37m \xBA\n" "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n" @@ -1668,6 +1860,13 @@ void DOS_SetupPrograms(void) { MSG_Add("PROGRAM_BOOT_CART_LIST_CMDS","Available PCjr cartridge commandos:%s"); MSG_Add("PROGRAM_BOOT_CART_NO_CMDS","No PCjr cartridge commandos found"); + MSG_Add("PROGRAM_LOADROM_SPECIFY_FILE","Must specify ROM file to load.\n"); + MSG_Add("PROGRAM_LOADROM_CANT_OPEN","ROM file not accessible.\n"); + MSG_Add("PROGRAM_LOADROM_TOO_LARGE","ROM file too large.\n"); + MSG_Add("PROGRAM_LOADROM_INCOMPATIBLE","Video BIOS not supported by machine type.\n"); + MSG_Add("PROGRAM_LOADROM_UNRECOGNIZED","ROM file not recognized.\n"); + MSG_Add("PROGRAM_LOADROM_BASIC_LOADED","BASIC ROM loaded.\n"); + MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_DRIVE","Must specify drive letter to mount image at.\n"); MSG_Add("PROGRAM_IMGMOUNT_SPECIFY2","Must specify drive number (0 or 3) to mount image at (0,1=fda,fdb;2,3=hda,hdb).\n"); MSG_Add("PROGRAM_IMGMOUNT_SPECIFY_GEOMETRY", @@ -1714,8 +1913,10 @@ void DOS_SetupPrograms(void) { PROGRAMS_MakeFile("INTRO.COM",INTRO_ProgramStart); PROGRAMS_MakeFile("BOOT.COM",BOOT_ProgramStart); #if C_DEBUG - PROGRAMS_MakeFile("LDGFXROM.COM", LDGFXROM_ProgramStart); + PROGRAMS_MakeFile("BIOSTEST.COM", BIOSTEST_ProgramStart); #endif + PROGRAMS_MakeFile("LOADROM.COM", LOADROM_ProgramStart); PROGRAMS_MakeFile("IMGMOUNT.COM", IMGMOUNT_ProgramStart); PROGRAMS_MakeFile("KEYB.COM", KEYB_ProgramStart); + } diff --git a/src/dos/dos_tables.cpp b/src/dos/dos_tables.cpp index b7efc8b..6ed87c4 100644 --- a/src/dos/dos_tables.cpp +++ b/src/dos/dos_tables.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -73,10 +73,8 @@ static Bit8u country_info[0x22] = { void DOS_SetupTables(void) { Bit16u seg;Bitu i; - dos.tables.mediaid=RealMake(DOS_GetMemory(4),0); dos.tables.tempdta=RealMake(DOS_GetMemory(4),0); dos.tables.tempdta_fcbdelete=RealMake(DOS_GetMemory(4),0); - for (i=0;i= 2) && (work[len-2] != ':')) { #else if((len > 1) && (work[len-1] == CROSS_FILESPLIT )) { #endif #ifndef HW_RVL - work[len-1] = 0; // Remove trailing slashes except when in root + work[len-1] = 0; // Remove trailing slashes except when in root #endif } } @@ -230,6 +231,72 @@ void DOS_Drive_Cache::AddEntry(const char* path, bool checkExists) { // LOG_DEBUG("DIR: Error: Failed to add %s",path); } } +void DOS_Drive_Cache::AddEntryDirOverlay(const char* path, bool checkExists) { + // Get Last part... + char file [CROSS_LEN]; + char expand [CROSS_LEN]; + char dironly[CROSS_LEN + 1]; + + //When adding a directory, the directory we want to operate inside in is the above it. (which can go wrong if the directory already exists.) + strcpy(dironly,path); + char* post = strrchr(dironly,CROSS_FILESPLIT); + + if (post) { +#if defined (WIN32) + //OS2 ? + if (post > dironly && *(post - 1) == ':' && (post - dironly) == 2) + post++; //move away from X: as need to end up with x:\ . +#else + //Lets hope this is not really used.. (root folder specified as overlay) + if (post == dironly) + post++; //move away from / +#endif + *post = 0; //TODO take care of AddEntryDIR D:\\piet) (so mount d d:\ as base) + *(post + 1) = 0; //As FindDirInfo is skipping over the base directory + } + CFileInfo* dir = FindDirInfo(dironly,expand); + const char* pos = strrchr(path,CROSS_FILESPLIT); + + if (pos) { + strcpy(file,pos + 1); + // Check if directory already exists, then don't add new entry... + if (checkExists) { + Bits index = GetLongName(dir,file); + if (index >= 0) { + //directory already exists, but most likely empty. + dir = dir->fileList[index]; + if (dir->isOverlayDir && dir->fileList.empty()) { + //maybe care about searches ? but this function should only run on cache inits/refreshes. + //add dot entries + CreateEntry(dir,".",true); + CreateEntry(dir,"..",true); + } + return; + } + } + + CreateEntry(dir,file,true); + + + Bits index = GetLongName(dir,file); + if (index>=0) { + Bit32u i; + // Check if there are any open search dir that are affected by this... + if (dir) for (i=0; inextEntry)) + dirSearch[i]->nextEntry++; + } + + dir = dir->fileList[index]; + dir->isOverlayDir = true; + CreateEntry(dir,".",true); + CreateEntry(dir,"..",true); + } + // LOG_DEBUG("DIR: Added Entry %s",path); + } else { + // LOG_DEBUG("DIR: Error: Failed to add %s",path); + } +} void DOS_Drive_Cache::DeleteEntry(const char* path, bool ignoreLastDir) { CacheOut(path,ignoreLastDir); @@ -268,6 +335,7 @@ void DOS_Drive_Cache::CacheOut(const char* path, bool ignoreLastDir) { // LOG_DEBUG("DIR: Caching out %s : dir %s",expand,dir->orgname); // delete file objects... + //Maybe check if it is a file and then only delete the file and possibly the long name. instead of all objects in the dir. for(Bit32u i=0; ifileList.size(); i++) { if (dirSearch[srchNr]==dir->fileList[i]) dirSearch[srchNr] = 0; DeleteFileInfo(dir->fileList[i]); dir->fileList[i] = 0; @@ -279,7 +347,7 @@ void DOS_Drive_Cache::CacheOut(const char* path, bool ignoreLastDir) { } bool DOS_Drive_Cache::IsCachedIn(CFileInfo* curDir) { - return (curDir->fileList.size()>0); + return (curDir->isOverlayDir || curDir->fileList.size()>0); } @@ -288,22 +356,22 @@ bool DOS_Drive_Cache::GetShortName(const char* fullname, char* shortname) { char expand[CROSS_LEN] = {0}; CFileInfo* curDir = FindDirInfo(fullname,expand); + const char* pos = strrchr(fullname,CROSS_FILESPLIT); + if (pos) pos++; else return false; + std::vector::size_type filelist_size = curDir->longNameList.size(); if (GCC_UNLIKELY(filelist_size<=0)) return false; - Bits low = 0; - Bits high = (Bits)(filelist_size-1); - Bits mid, res; - - while (low<=high) { - mid = (low+high)/2; - res = strcmp(fullname,curDir->longNameList[mid]->orgname); - if (res>0) low = mid+1; else - if (res<0) high = mid-1; - else { - strcpy(shortname,curDir->longNameList[mid]->shortname); + // The orgname part of the list is not sorted (shortname is)! So we can only walk through it. + for(Bitu i = 0; i < filelist_size; i++) { +#if defined (WIN32) || defined (OS2) /* Win 32 & OS/2*/ + if (strcasecmp(pos,curDir->longNameList[i]->orgname) == 0) { +#else + if (strcmp(pos,curDir->longNameList[i]->orgname) == 0) { +#endif + strcpy(shortname,curDir->longNameList[i]->shortname); return true; - }; + } } return false; } @@ -677,9 +745,9 @@ bool DOS_Drive_Cache::OpenDir(CFileInfo* dir, const char* expand, Bit16u& id) { if (dirSearch[id]) { // open dir dir_information* dirp = open_directory(expandcopy); - if (dirp) { + if (dirp || dir->isOverlayDir) { // Reset it.. - close_directory(dirp); + if (dirp) close_directory(dirp); strcpy(dirPath,expandcopy); return true; } diff --git a/src/dos/drive_fat.cpp b/src/dos/drive_fat.cpp index 052d976..e75b47c 100644 --- a/src/dos/drive_fat.cpp +++ b/src/dos/drive_fat.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -27,6 +27,7 @@ #include "support.h" #include "cross.h" #include "bios.h" +#include "bios_disk.h" #define IMGTYPE_FLOPPY 0 #define IMGTYPE_ISO 1 @@ -36,9 +37,6 @@ #define FAT16 1 #define FAT32 2 -Bit8u fatSectBuffer[1024]; -Bit32u curFatSect; - class fatFile : public DOS_File { public: fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive); @@ -98,8 +96,6 @@ fatFile::fatFile(const char* /*name*/, Bit32u startCluster, Bit32u fileLen, fatD if(filelength > 0) { Seek(&seekto, DOS_SEEK_SET); - myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); - loadedSector = true; } } @@ -122,8 +118,8 @@ bool fatFile::Read(Bit8u * data, Bit16u *size) { loadedSector = false; return true; } - curSectOff = 0; - myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); + curSectOff = seekpos % myDrive->getSectorSize(); + myDrive->readSector(currentSector, sectorBuffer); loadedSector = true; } @@ -146,7 +142,7 @@ bool fatFile::Read(Bit8u * data, Bit16u *size) { return true; } curSectOff = 0; - myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); + myDrive->readSector(currentSector, sectorBuffer); loadedSector = true; //LOG_MSG("Reading absolute sector at %d for seekpos %d", currentSector, seekpos); } @@ -157,8 +153,6 @@ bool fatFile::Read(Bit8u * data, Bit16u *size) { } bool fatFile::Write(Bit8u * data, Bit16u *size) { - /* TODO: Check for read-only bit */ - if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode DOS_SetError(DOSERR_ACCESS_DENIED); return false; @@ -169,17 +163,42 @@ bool fatFile::Write(Bit8u * data, Bit16u *size) { sizedec = *size; sizecount = 0; + if(seekpos < filelength && *size == 0) { + /* Truncate file to current position */ + myDrive->deleteClustChain(firstCluster, seekpos); + filelength = seekpos; + goto finalizeWrite; + } + + if(seekpos > filelength) { + /* Extend file to current position */ + Bit32u clustSize = myDrive->getClusterSize(); + if(filelength == 0) { + firstCluster = myDrive->getFirstFreeClust(); + if(firstCluster == 0) goto finalizeWrite; // out of space + myDrive->allocateCluster(firstCluster, 0); + filelength = clustSize; + } + filelength = ((filelength - 1) / clustSize + 1) * clustSize; + while(filelength < seekpos) { + if(myDrive->appendCluster(firstCluster) == 0) goto finalizeWrite; // out of space + filelength += clustSize; + } + if(filelength > seekpos) filelength = seekpos; + if(*size == 0) goto finalizeWrite; + } + while(sizedec != 0) { /* Increase filesize if necessary */ if(seekpos >= filelength) { if(filelength == 0) { firstCluster = myDrive->getFirstFreeClust(); + if(firstCluster == 0) goto finalizeWrite; // out of space myDrive->allocateCluster(firstCluster, 0); currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos); - myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); + myDrive->readSector(currentSector, sectorBuffer); loadedSector = true; } - filelength = seekpos+1; if (!loadedSector) { currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos); if(currentSector == 0) { @@ -192,16 +211,16 @@ bool fatFile::Write(Bit8u * data, Bit16u *size) { goto finalizeWrite; } } - curSectOff = 0; - myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); - + curSectOff = seekpos % myDrive->getSectorSize(); + myDrive->readSector(currentSector, sectorBuffer); loadedSector = true; } + filelength = seekpos+1; } sectorBuffer[curSectOff++] = data[sizecount++]; seekpos++; if(curSectOff >= myDrive->getSectorSize()) { - if(loadedSector) myDrive->loadedDisk->Write_AbsoluteSector(currentSector, sectorBuffer); + if(loadedSector) myDrive->writeSector(currentSector, sectorBuffer); currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos); if(currentSector == 0) { @@ -216,13 +235,12 @@ bool fatFile::Write(Bit8u * data, Bit16u *size) { } } curSectOff = 0; - myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); - + myDrive->readSector(currentSector, sectorBuffer); loadedSector = true; } --sizedec; } - if(curSectOff>0 && loadedSector) myDrive->loadedDisk->Write_AbsoluteSector(currentSector, sectorBuffer); + if(curSectOff>0 && loadedSector) myDrive->writeSector(currentSector, sectorBuffer); finalizeWrite: myDrive->directoryBrowse(dirCluster, &tmpentry, dirIndex); @@ -251,7 +269,6 @@ bool fatFile::Seek(Bit32u *pos, Bit32u type) { } // LOG_MSG("Seek to %d with type %d (absolute value %d)", *pos, type, seekto); - if((Bit32u)seekto > filelength) seekto = (Bit32s)filelength; if(seekto<0) seekto = 0; seekpos = (Bit32u)seekto; currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos); @@ -260,7 +277,8 @@ bool fatFile::Seek(Bit32u *pos, Bit32u type) { loadedSector = false; } else { curSectOff = seekpos % myDrive->getSectorSize(); - myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); + myDrive->readSector(currentSector, sectorBuffer); + loadedSector = true; } *pos = seekpos; return true; @@ -268,7 +286,7 @@ bool fatFile::Seek(Bit32u *pos, Bit32u type) { bool fatFile::Close() { /* Flush buffer */ - if (loadedSector) myDrive->loadedDisk->Write_AbsoluteSector(currentSector, sectorBuffer); + if (loadedSector) myDrive->writeSector(currentSector, sectorBuffer); return false; } @@ -307,9 +325,9 @@ Bit32u fatDrive::getClusterValue(Bit32u clustNum) { if(curFatSect != fatsectnum) { /* Load two sectors at once for FAT12 */ - loadedDisk->Read_AbsoluteSector(fatsectnum, &fatSectBuffer[0]); + readSector(fatsectnum, &fatSectBuffer[0]); if (fattype==FAT12) - loadedDisk->Read_AbsoluteSector(fatsectnum+1, &fatSectBuffer[512]); + readSector(fatsectnum+1, &fatSectBuffer[512]); curFatSect = fatsectnum; } @@ -354,9 +372,9 @@ void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) { if(curFatSect != fatsectnum) { /* Load two sectors at once for FAT12 */ - loadedDisk->Read_AbsoluteSector(fatsectnum, &fatSectBuffer[0]); + readSector(fatsectnum, &fatSectBuffer[0]); if (fattype==FAT12) - loadedDisk->Read_AbsoluteSector(fatsectnum+1, &fatSectBuffer[512]); + readSector(fatsectnum+1, &fatSectBuffer[512]); curFatSect = fatsectnum; } @@ -385,10 +403,10 @@ void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) { break; } for(int fc=0;fcWrite_AbsoluteSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]); + writeSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]); if (fattype==FAT12) { if (fatentoff>=511) - loadedDisk->Write_AbsoluteSector(fatsectnum+1+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[512]); + writeSector(fatsectnum+1+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[512]); } } } @@ -497,10 +515,34 @@ bool fatDrive::getDirClustNum(char *dir, Bit32u *clustNum, bool parDir) { return true; } +Bit8u fatDrive::readSector(Bit32u sectnum, void * data) { + if (absolute) return loadedDisk->Read_AbsoluteSector(sectnum, data); + Bit32u cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack; + Bit32u cylinder = sectnum / cylindersize; + sectnum %= cylindersize; + Bit32u head = sectnum / bootbuffer.sectorspertrack; + Bit32u sector = sectnum % bootbuffer.sectorspertrack + 1L; + return loadedDisk->Read_Sector(head, cylinder, sector, data); +} + +Bit8u fatDrive::writeSector(Bit32u sectnum, void * data) { + if (absolute) return loadedDisk->Write_AbsoluteSector(sectnum, data); + Bit32u cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack; + Bit32u cylinder = sectnum / cylindersize; + sectnum %= cylindersize; + Bit32u head = sectnum / bootbuffer.sectorspertrack; + Bit32u sector = sectnum % bootbuffer.sectorspertrack + 1L; + return loadedDisk->Write_Sector(head, cylinder, sector, data); +} + Bit32u fatDrive::getSectorSize(void) { return bootbuffer.bytespersector; } +Bit32u fatDrive::getClusterSize(void) { + return bootbuffer.sectorspercluster * bootbuffer.bytespersector; +} + Bit32u fatDrive::getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos) { return getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector); } @@ -528,6 +570,10 @@ Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSe } if((isEOF) && (skipClust>=1)) { //LOG_MSG("End of cluster chain reached before end of logical sector seek!"); + if (skipClust == 1 && fattype == FAT12) { + //break; + LOG(LOG_DOSMISC,LOG_ERROR)("End of cluster chain reached, but maybe good afterall ?"); + } return 0; } currentClust = testvalue; @@ -537,7 +583,11 @@ Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSe return (getClustFirstSect(currentClust) + sectClust); } -void fatDrive::deleteClustChain(Bit32u startCluster) { +void fatDrive::deleteClustChain(Bit32u startCluster, Bit32u bytePos) { + Bit32u clustSize = getClusterSize(); + Bit32u endClust = (bytePos + clustSize - 1) / clustSize; + Bit32u countClust = 1; + Bit32u testvalue; Bit32u currentClust = startCluster; bool isEOF = false; @@ -547,8 +597,6 @@ void fatDrive::deleteClustChain(Bit32u startCluster) { /* What the crap? Cluster is already empty - BAIL! */ break; } - /* Mark cluster as empty */ - setClusterValue(currentClust, 0); switch(fattype) { case FAT12: if(testvalue >= 0xff8) isEOF = true; @@ -560,8 +608,26 @@ void fatDrive::deleteClustChain(Bit32u startCluster) { if(testvalue >= 0xfffffff8) isEOF = true; break; } + if(countClust == endClust && !isEOF) { + /* Mark cluster as end */ + switch(fattype) { + case FAT12: + setClusterValue(currentClust, 0xfff); + break; + case FAT16: + setClusterValue(currentClust, 0xffff); + break; + case FAT32: + setClusterValue(currentClust, 0xffffffff); + break; + } + } else if(countClust > endClust) { + /* Mark cluster as empty */ + setClusterValue(currentClust, 0); + } if(isEOF) break; currentClust = testvalue; + countClust++; } } @@ -630,6 +696,7 @@ fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, created_successfully = true; FILE *diskfile; Bit32u filesize; + bool is_hdd; struct partTable mbrData; if(imgDTASeg == 0) { @@ -638,19 +705,20 @@ fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, imgDTA = new DOS_DTA(imgDTAPtr); } - diskfile = fopen(sysFilename, "rb+"); + diskfile = fopen_wrap(sysFilename, "rb+"); if(!diskfile) {created_successfully = false;return;} fseek(diskfile, 0L, SEEK_END); filesize = (Bit32u)ftell(diskfile) / 1024L; + is_hdd = (filesize > 2880); /* Load disk image */ - loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880)); + loadedDisk = new imageDisk(diskfile, sysFilename, filesize, is_hdd); if(!loadedDisk) { created_successfully = false; return; } - if(filesize > 2880) { + if(is_hdd) { /* Set user specified harddrive parameters */ loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector); @@ -669,31 +737,102 @@ fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, } } - if(m==4) LOG_MSG("No good partiton found in image."); + if(m==4) LOG_MSG("No good partition found in image."); partSectOff = startSector; } else { + /* Get floppy disk parameters based on image size */ + loadedDisk->Get_Geometry(&headscyl, &cylinders, &cylsector, &bytesector); /* Floppy disks don't have partitions */ partSectOff = 0; } + if (bytesector != 512) { + /* Non-standard sector sizes not implemented */ + created_successfully = false; + return; + } + loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer); + + if (!is_hdd) { + /* Identify floppy format */ + if ((bootbuffer.nearjmp[0] == 0x69 || bootbuffer.nearjmp[0] == 0xe9 || + (bootbuffer.nearjmp[0] == 0xeb && bootbuffer.nearjmp[2] == 0x90)) && + (bootbuffer.mediadescriptor & 0xf0) == 0xf0) { + /* DOS 2.x or later format, BPB assumed valid */ + + if ((bootbuffer.mediadescriptor != 0xf0 && !(bootbuffer.mediadescriptor & 0x1)) && + (bootbuffer.oemname[5] != '3' || bootbuffer.oemname[6] != '.' || bootbuffer.oemname[7] < '2')) { + /* Fix pre-DOS 3.2 single-sided floppy */ + bootbuffer.sectorspercluster = 1; + } + } else { + /* Read media descriptor in FAT */ + Bit8u sectorBuffer[512]; + loadedDisk->Read_AbsoluteSector(1,§orBuffer); + Bit8u mdesc = sectorBuffer[0]; + + if (mdesc >= 0xf8) { + /* DOS 1.x format, create BPB for 160kB floppy */ + bootbuffer.bytespersector = 512; + bootbuffer.sectorspercluster = 1; + bootbuffer.reservedsectors = 1; + bootbuffer.fatcopies = 2; + bootbuffer.rootdirentries = 64; + bootbuffer.totalsectorcount = 320; + bootbuffer.mediadescriptor = mdesc; + bootbuffer.sectorsperfat = 1; + bootbuffer.sectorspertrack = 8; + bootbuffer.headcount = 1; + bootbuffer.magic1 = 0x55; // to silence warning + bootbuffer.magic2 = 0xaa; + if (!(mdesc & 0x2)) { + /* Adjust for 9 sectors per track */ + bootbuffer.totalsectorcount = 360; + bootbuffer.sectorsperfat = 2; + bootbuffer.sectorspertrack = 9; + } + if (mdesc & 0x1) { + /* Adjust for 2 sides */ + bootbuffer.sectorspercluster = 2; + bootbuffer.rootdirentries = 112; + bootbuffer.totalsectorcount *= 2; + bootbuffer.headcount = 2; + } + } else { + /* Unknown format */ + created_successfully = false; + return; + } + } + } + if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) { /* Not a FAT filesystem */ LOG_MSG("Loaded image has no valid magicnumbers at the end!"); } - if(!bootbuffer.sectorsperfat) { - /* FAT32 not implemented yet */ + /* Sanity checks */ + if ((bootbuffer.sectorsperfat == 0) || // FAT32 not implemented yet + (bootbuffer.bytespersector != 512) || // non-standard sector sizes not implemented + (bootbuffer.sectorspercluster == 0) || + (bootbuffer.rootdirentries == 0) || + (bootbuffer.fatcopies == 0) || + (bootbuffer.headcount == 0) || + (bootbuffer.headcount > headscyl) || + (bootbuffer.sectorspertrack == 0) || + (bootbuffer.sectorspertrack > cylsector)) { created_successfully = false; return; } + /* Filesystem must be contiguous to use absolute sectors, otherwise CHS will be used */ + absolute = ((bootbuffer.headcount == headscyl) && (bootbuffer.sectorspertrack == cylsector)); /* Determine FAT format, 12, 16 or 32 */ /* Get size of root dir in sectors */ - /* TODO: Get 32-bit total sector count if needed */ Bit32u RootDirSectors = ((bootbuffer.rootdirentries * 32) + (bootbuffer.bytespersector - 1)) / bootbuffer.bytespersector; Bit32u DataSectors; if(bootbuffer.totalsectorcount != 0) { @@ -724,6 +863,9 @@ fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, memset(fatSectBuffer,0,1024); curFatSect = 0xffffffff; + + strcpy(info, "fatDrive "); + strcat(info, sysFilename); } bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) { @@ -782,6 +924,7 @@ bool fatDrive::FileCreate(DOS_File **file, char *name, Bit16u attributes) { /* Truncate file */ fileEntry.entrysize=0; directoryChange(dirClust, &fileEntry, subEntry); + if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0); } else { /* Can we even get the name of the file itself? */ if(!getEntryName(name, &dirName[0])) return false; @@ -848,13 +991,14 @@ bool fatDrive::FileUnlink(char * name) { fileEntry.entryname[0] = 0xe5; directoryChange(dirClust, &fileEntry, subEntry); - if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust); + if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0); return true; } bool fatDrive::FindFirst(char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) { direntry dummyClust; +#if 0 Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII]; dta.GetSearchParams(attr,pattern); if(attr==DOS_ATTR_VOLUME) { @@ -867,6 +1011,7 @@ bool fatDrive::FindFirst(char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) { } if(attr & DOS_ATTR_VOLUME) //check for root dir or fcb_findfirst LOG(LOG_DOSMISC,LOG_WARN)("findfirst for volumelabel used on fatDrive. Unhandled!!!!!"); +#endif if(!getDirClustNum(_dir, &cwdDirCluster, false)) { DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; @@ -913,7 +1058,11 @@ nextfile: entryoffset = dirPos % 16; if(dirClustNumber==0) { - loadedDisk->Read_AbsoluteSector(firstRootDirSect+logentsector,sectbuf); + if(dirPos >= bootbuffer.rootdirentries) { + DOS_SetError(DOSERR_NO_MORE_FILES); + return false; + } + readSector(firstRootDirSect+logentsector,sectbuf); } else { tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); /* A zero sector number can't happen */ @@ -921,7 +1070,7 @@ nextfile: DOS_SetError(DOSERR_NO_MORE_FILES); return false; } - loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + readSector(tmpsector,sectbuf); } dirPos++; dta.SetDirID(dirPos); @@ -934,26 +1083,37 @@ nextfile: DOS_SetError(DOSERR_NO_MORE_FILES); return false; } - memset(find_name,0,DOS_NAMELENGTH_ASCII); memset(extension,0,4); memcpy(find_name,§buf[entryoffset].entryname[0],8); memcpy(extension,§buf[entryoffset].entryname[8],3); trimString(&find_name[0]); trimString(&extension[0]); - if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY) || extension[0]!=0) { + + //if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY)) + if (extension[0]!=0) { strcat(find_name, "."); strcat(find_name, extension); } - /* Ignore files with volume label. FindFirst should search for those. (return the first one found) */ - if(sectbuf[entryoffset].attrib & 0x8) goto nextfile; - - /* Always find ARCHIVES even if bit is not set Perhaps test is not the best test */ - if(~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM) ) goto nextfile; + /* Compare attributes to search attributes */ + + //TODO What about attrs = DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY ? + if (attrs == DOS_ATTR_VOLUME) { + if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) goto nextfile; + dirCache.SetLabel(find_name, false, true); + } else { + if (~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_VOLUME | DOS_ATTR_SYSTEM | DOS_ATTR_HIDDEN) ) goto nextfile; + } + + + /* Compare name to search pattern */ if(!WildFileCmp(find_name,srch_pattern)) goto nextfile; - dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib); + //dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib); + + dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].modDate, sectbuf[entryoffset].modTime, sectbuf[entryoffset].attrib); + memcpy(foundEntry, §buf[entryoffset], sizeof(direntry)); return true; @@ -1014,12 +1174,12 @@ bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s if(dirClustNumber==0) { if(dirPos >= bootbuffer.rootdirentries) return false; tmpsector = firstRootDirSect+logentsector; - loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + readSector(tmpsector,sectbuf); } else { tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); /* A zero sector number can't happen */ if(tmpsector == 0) return false; - loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + readSector(tmpsector,sectbuf); } dirPos++; @@ -1048,12 +1208,12 @@ bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s if(dirClustNumber==0) { if(dirPos >= bootbuffer.rootdirentries) return false; tmpsector = firstRootDirSect+logentsector; - loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + readSector(tmpsector,sectbuf); } else { tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); /* A zero sector number can't happen */ if(tmpsector == 0) return false; - loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + readSector(tmpsector,sectbuf); } dirPos++; @@ -1064,7 +1224,7 @@ bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s } if(tmpsector != 0) { memcpy(§buf[entryoffset], useEntry, sizeof(direntry)); - loadedDisk->Write_AbsoluteSector(tmpsector, sectbuf); + writeSector(tmpsector, sectbuf); return true; } else { return false; @@ -1086,7 +1246,7 @@ bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) { if(dirClustNumber==0) { if(dirPos >= bootbuffer.rootdirentries) return false; tmpsector = firstRootDirSect+logentsector; - loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + readSector(tmpsector,sectbuf); } else { tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); /* A zero sector number can't happen - we need to allocate more room for this directory*/ @@ -1098,14 +1258,14 @@ bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) { tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */ } - loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + readSector(tmpsector,sectbuf); } dirPos++; /* Deleted file entry or end of directory list */ if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) { sectbuf[entryoffset] = useEntry; - loadedDisk->Write_AbsoluteSector(tmpsector,sectbuf); + writeSector(tmpsector,sectbuf); break; } } @@ -1120,7 +1280,7 @@ void fatDrive::zeroOutCluster(Bit32u clustNumber) { int i; for(i=0;iWrite_AbsoluteSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]); + writeSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]); } } @@ -1217,7 +1377,7 @@ bool fatDrive::RemoveDir(char *dir) { found = true; tmpentry.entryname[0] = 0xe5; directoryChange(dirClust, &tmpentry, fileidx); - deleteClustChain(dummyClust); + deleteClustChain(dummyClust, 0); break; } diff --git a/src/dos/drive_iso.cpp b/src/dos/drive_iso.cpp index 73bbad7..64e95ef 100644 --- a/src/dos/drive_iso.cpp +++ b/src/dos/drive_iso.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -25,6 +25,9 @@ #include "support.h" #include "drives.h" +#define FLAGS1 ((iso) ? de.fileFlags : de.timeZone) +#define FLAGS2 ((iso) ? de->fileFlags : de->timeZone) + using namespace std; class isoFile : public DOS_File { @@ -130,13 +133,22 @@ Bit16u isoFile::GetInformation(void) { return 0x40; // read-only drive } -int MSCDEX_RemoveDrive(char driveLetter); -int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit); -void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit); -bool MSCDEX_HasDrive(char driveLetter); -bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name); +int MSCDEX_RemoveDrive(char driveLetter); +int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit); +void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit); +bool MSCDEX_HasDrive(char driveLetter); +bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name); +Bit8u MSCDEX_GetSubUnit(char driveLetter); -isoDrive::isoDrive(char driveLetter, const char *fileName, Bit8u mediaid, int &error) { +isoDrive::isoDrive(char driveLetter, const char *fileName, Bit8u mediaid, int &error) + :iso(false), + dataCD(false), + mediaid(0), + subUnit(0), + driveLetter('\0') + { + this->fileName[0] = '\0'; + this->discLabel[0] = '\0'; nextFreeDirIterator = 0; memset(dirIterators, 0, sizeof(dirIterators)); memset(sectorHashEntries, 0, sizeof(sectorHashEntries)); @@ -171,6 +183,7 @@ isoDrive::~isoDrive() { } int isoDrive::UpdateMscdex(char driveLetter, const char* path, Bit8u& subUnit) { if (MSCDEX_HasDrive(driveLetter)) { + subUnit = MSCDEX_GetSubUnit(driveLetter); CDROM_Interface_Image* oldCdrom = CDROM_Interface_Image::images[subUnit]; CDROM_Interface* cdrom = new CDROM_Interface_Image(subUnit); char pathCopy[CROSS_LEN]; @@ -198,7 +211,7 @@ bool isoDrive::FileOpen(DOS_File **file, char *name, Bit32u flags) { } isoDirEntry de; - bool success = lookup(&de, name) && !IS_DIR(de.fileFlags); + bool success = lookup(&de, name) && !IS_DIR(FLAGS1); if (success) { FileStat_Block file_stat; @@ -234,7 +247,7 @@ bool isoDrive::MakeDir(char* /*dir*/) { bool isoDrive::TestDir(char *dir) { isoDirEntry de; - return (lookup(&de, dir) && IS_DIR(de.fileFlags)); + return (lookup(&de, dir) && IS_DIR(FLAGS1)); } bool isoDrive::FindFirst(char *dir, DOS_DTA &dta, bool fcb_findfirst) { @@ -255,13 +268,8 @@ bool isoDrive::FindFirst(char *dir, DOS_DTA &dta, bool fcb_findfirst) { dta.GetSearchParams(attr, pattern); if (attr == DOS_ATTR_VOLUME) { - if (strlen(discLabel) != 0) { - dta.SetResult(discLabel, 0, 0, 0, DOS_ATTR_VOLUME); - return true; - } else { - DOS_SetError(DOSERR_NO_MORE_FILES); - return false; - } + dta.SetResult(discLabel, 0, 0, 0, DOS_ATTR_VOLUME); + return true; } else if ((attr & DOS_ATTR_VOLUME) && isRoot && !fcb_findfirst) { if (WildFileCmp(discLabel,pattern)) { // Get Volume Label (DOS_ATTR_VOLUME) and only in basedir and if it matches the searchstring @@ -284,11 +292,11 @@ bool isoDrive::FindNext(DOS_DTA &dta) { isoDirEntry de; while (GetNextDirEntry(dirIterator, &de)) { Bit8u findAttr = 0; - if (IS_DIR(de.fileFlags)) findAttr |= DOS_ATTR_DIRECTORY; + if (IS_DIR(FLAGS1)) findAttr |= DOS_ATTR_DIRECTORY; else findAttr |= DOS_ATTR_ARCHIVE; - if (IS_HIDDEN(de.fileFlags)) findAttr |= DOS_ATTR_HIDDEN; + if (IS_HIDDEN(FLAGS1)) findAttr |= DOS_ATTR_HIDDEN; - if (!IS_ASSOC(de.fileFlags) && !(isRoot && de.ident[0]=='.') && WildFileCmp((char*)de.ident, pattern) + if (!IS_ASSOC(FLAGS1) && !(isRoot && de.ident[0]=='.') && WildFileCmp((char*)de.ident, pattern) && !(~attr & findAttr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM))) { /* file is okay, setup everything to be copied in DTA Block */ @@ -323,8 +331,8 @@ bool isoDrive::GetFileAttr(char *name, Bit16u *attr) { bool success = lookup(&de, name); if (success) { *attr = DOS_ATTR_ARCHIVE | DOS_ATTR_READ_ONLY; - if (IS_HIDDEN(de.fileFlags)) *attr |= DOS_ATTR_HIDDEN; - if (IS_DIR(de.fileFlags)) *attr |= DOS_ATTR_DIRECTORY; + if (IS_HIDDEN(FLAGS1)) *attr |= DOS_ATTR_HIDDEN; + if (IS_DIR(FLAGS1)) *attr |= DOS_ATTR_DIRECTORY; } return success; } @@ -332,14 +340,14 @@ bool isoDrive::GetFileAttr(char *name, Bit16u *attr) { bool isoDrive::AllocationInfo(Bit16u *bytes_sector, Bit8u *sectors_cluster, Bit16u *total_clusters, Bit16u *free_clusters) { *bytes_sector = 2048; *sectors_cluster = 1; // cluster size for cdroms ? - *total_clusters = 60000; + *total_clusters = 65535; *free_clusters = 0; return true; } bool isoDrive::FileExists(const char *name) { isoDirEntry de; - return (lookup(&de, name) && !IS_DIR(de.fileFlags)); + return (lookup(&de, name) && !IS_DIR(FLAGS1)); } bool isoDrive::FileStat(const char *name, FileStat_Block *const stat_block) { @@ -351,7 +359,7 @@ bool isoDrive::FileStat(const char *name, FileStat_Block *const stat_block) { stat_block->time = DOS_PackTime(de.timeHour, de.timeMin, de.timeSec); stat_block->size = DATA_LENGTH(de); stat_block->attr = DOS_ATTR_ARCHIVE | DOS_ATTR_READ_ONLY; - if (IS_DIR(de.fileFlags)) stat_block->attr |= DOS_ATTR_DIRECTORY; + if (IS_DIR(FLAGS1)) stat_block->attr |= DOS_ATTR_DIRECTORY; } return success; @@ -475,7 +483,7 @@ int isoDrive :: readDirEntry(isoDirEntry *de, Bit8u *data) { // modify file identifier for use with dosbox if ((de->length < 33 + de->fileIdentLength)) return -1; - if (IS_DIR(de->fileFlags)) { + if (IS_DIR(FLAGS2)) { if (de->fileIdentLength == 1 && de->ident[0] == 0) strcpy((char*)de->ident, "."); else if (de->fileIdentLength == 1 && de->ident[0] == 1) strcpy((char*)de->ident, ".."); else { @@ -504,11 +512,14 @@ int isoDrive :: readDirEntry(isoDirEntry *de, Bit8u *data) { } bool isoDrive :: loadImage() { - isoPVD pvd; + Bit8u pvd[COOKED_SECTOR_SIZE]; dataCD = false; - readSector((Bit8u*)(&pvd), ISO_FIRST_VD); - if (pvd.type != 1 || strncmp((char*)pvd.standardIdent, "CD001", 5) || pvd.version != 1) return false; - if (readDirEntry(&this->rootEntry, pvd.rootEntry)>0) { + readSector(pvd, ISO_FIRST_VD); + if (pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1) iso = true; + else if (pvd[8] == 1 && !strncmp((char*)(&pvd[9]), "CDROM", 5) && pvd[14] == 1) iso = false; + else return false; + Bit16u offset = iso ? 156 : 180; + if (readDirEntry(&this->rootEntry, &pvd[offset])>0) { dataCD = true; return true; } @@ -529,7 +540,7 @@ bool isoDrive :: lookup(isoDirEntry *de, const char *path) { bool found = false; // current entry must be a directory, abort otherwise - if (IS_DIR(de->fileFlags)) { + if (IS_DIR(FLAGS2)) { // remove the trailing dot if present size_t nameLength = strlen(name); @@ -540,7 +551,7 @@ bool isoDrive :: lookup(isoDirEntry *de, const char *path) { // look for the current path element int dirIterator = GetDirIterator(de); while (!found && GetNextDirEntry(dirIterator, de)) { - if (!IS_ASSOC(de->fileFlags) && (0 == strncasecmp((char*) de->ident, name, ISO_MAX_FILENAME_LENGTH))) { + if (!IS_ASSOC(FLAGS2) && (0 == strncasecmp((char*) de->ident, name, ISO_MAX_FILENAME_LENGTH))) { found = true; } } diff --git a/src/dos/drive_local.cpp b/src/dos/drive_local.cpp index ba4daf9..4b9013b 100644 --- a/src/dos/drive_local.cpp +++ b/src/dos/drive_local.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -30,23 +30,6 @@ #include "cross.h" #include "inout.h" -class localFile : public DOS_File { -public: - localFile(const char* name, FILE * handle); - bool Read(Bit8u * data,Bit16u * size); - bool Write(Bit8u * data,Bit16u * size); - bool Seek(Bit32u * pos,Bit32u type); - bool Close(); - Bit16u GetInformation(void); - bool UpdateDateTimeFromHost(void); - void FlagReadOnlyMedium(void); - void Flush(void); -private: - FILE * fhandle; - bool read_only_medium; - enum { NONE,READ,WRITE } last_action; -}; - bool localDrive::FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/) { //TODO Maybe care for attributes but not likely @@ -56,16 +39,16 @@ bool localDrive::FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/) CROSS_FILENAME(newname); char* temp_name = dirCache.GetExpandName(newname); //Can only be used in till a new drive_cache action is preformed */ /* Test if file exists (so we need to truncate it). don't add to dirCache then */ - bool existing_file=false; + bool existing_file = false; - FILE * test=fopen(temp_name,"rb+"); + FILE * test = fopen_wrap(temp_name,"rb+"); if(test) { fclose(test); existing_file=true; } - FILE * hand=fopen(temp_name,"wb+"); + FILE * hand = fopen_wrap(temp_name,"wb+"); if (!hand){ LOG_MSG("Warning: file creation failed: %s",newname); return false; @@ -112,11 +95,11 @@ bool localDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) { } } - FILE * hand=fopen(newname,type); + FILE * hand = fopen_wrap(newname,type); // Bit32u err=errno; if (!hand) { if((flags&0xf) != OPEN_READ) { - FILE * hmm=fopen(newname,"rb"); + FILE * hmm = fopen_wrap(newname,"rb"); if (hmm) { fclose(hmm); LOG_MSG("Warning: file %s exists and failed to open in write mode.\nPlease Remove write-protection",newname); @@ -139,7 +122,7 @@ FILE * localDrive::GetSystemFilePtr(char const * const name, char const * const CROSS_FILENAME(newname); dirCache.ExpandName(newname); - return fopen(newname,type); + return fopen_wrap(newname,type); } bool localDrive::GetSystemFilename(char *sysName, char const * const dosName) { @@ -162,7 +145,7 @@ bool localDrive::FileUnlink(char * name) { struct stat buffer; if(stat(fullname,&buffer)) return false; // File not found. - FILE* file_writable = fopen(fullname,"rb+"); + FILE* file_writable = fopen_wrap(fullname,"rb+"); if(!file_writable) return false; //No acces ? ERROR MESSAGE NOT SET. FIXME ? fclose(file_writable); @@ -467,7 +450,8 @@ bool localFile::Read(Bit8u * data,Bit16u * size) { } bool localFile::Write(Bit8u * data,Bit16u * size) { - if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode + Bit32u lastflags = this->flags & 0xf; + if (lastflags == OPEN_READ || lastflags == OPEN_READ_NO_MOD) { // check if file opened in read-only mode DOS_SetError(DOSERR_ACCESS_DENIED); return false; } @@ -575,7 +559,10 @@ bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name); cdromDrive::cdromDrive(const char driveLetter, const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid, int& error) - :localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid) { + :localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid), + subUnit(0), + driveLetter('\0') +{ // Init mscdex error = MSCDEX_AddDrive(driveLetter,startdir,subUnit); strcpy(info, "CDRom "); diff --git a/src/dos/drive_overlay.cpp b/src/dos/drive_overlay.cpp new file mode 100644 index 0000000..f728c49 --- /dev/null +++ b/src/dos/drive_overlay.cpp @@ -0,0 +1,1193 @@ +/* + * Copyright (C) 2002-2019 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "dosbox.h" +#include "dos_inc.h" +#include "drives.h" +#include "support.h" +#include "cross.h" +#include "inout.h" +#include "timer.h" + +#include +#include + +#include +#include +#include +#include +#include + +#define OVERLAY_DIR 1 +bool logoverlay = false; +using namespace std; + +#if defined (WIN32) || defined (OS2) /* Win 32 & OS/2*/ +#define CROSS_DOSFILENAME(blah) +#else +//Convert back to DOS PATH +#define CROSS_DOSFILENAME(blah) strreplace(blah,'/','\\') +#endif + + +/* + * design principles/limitations/requirements: + * 1) All filenames inside the overlay directories are UPPERCASE and conform to the 8.3 standard except for the special DBOVERLAY files. + * 2) Renaming directories is currently not supported. + * + * Point 2 is still being worked on. + */ + +/* New rename for base directories: + * Alter shortname in the drive_cache: take care of order and long names. + * update stored deleted files list in overlay. + */ + +//TODO recheck directories under linux with the filename_cache (as one adds the dos name (and runs cross_filename on the other)) + + +//TODO Check: Maybe handle file redirection in ccc (opening the new file), (call update datetime host there ?) + + +/* For rename/delete(unlink)/makedir/removedir we need to rebuild the cache. (shouldn't be needed, + * but cacheout/delete entry currently throw away the cached folder and rebuild it on read. + * so we have to ensure the rebuilding is controlled through the overlay. + * In order to not reread the overlay directory contents, the information in there is cached and updated when + * it changes (when deleting a file or adding one) + */ + + +//directories that exist only in overlay can not be added to the drive_cache currently. +//Either upgrade addentry to support directories. (without actually caching stuff in! (code in testing)) +//Or create an empty directory in local drive base. + +bool Overlay_Drive::RemoveDir(char * dir) { + //DOS_RemoveDir checks if directory exists. +#if OVERLAY_DIR + if (logoverlay) LOG_MSG("Overlay: trying to remove directory: %s",dir); +#else + E_Exit("Overlay: trying to remove directory: %s",dir); +#endif + /* Overlay: Check if folder is empty (findfirst/next, skipping . and .. and breaking on first file found ?), if so, then it is not too tricky. */ + if (is_dir_only_in_overlay(dir)) { + //The simple case + char odir[CROSS_LEN]; + strcpy(odir,overlaydir); + strcat(odir,dir); + CROSS_FILENAME(odir); + int temp=rmdir(odir); + if (temp==0) { + remove_DOSdir_from_cache(dir); + char newdir[CROSS_LEN]; + strcpy(newdir,basedir); + strcat(newdir,dir); + CROSS_FILENAME(newdir); + dirCache.DeleteEntry(newdir,true); + update_cache(false); + } + return (temp==0); + } else { + Bit16u olderror = dos.errorcode; //FindFirst/Next always set an errorcode, while RemoveDir itself shouldn't touch it if successful + DOS_DTA dta(dos.tables.tempdta); + char stardotstar[4] = {'*', '.', '*', 0}; + dta.SetupSearch(0,(0xff & ~DOS_ATTR_VOLUME),stardotstar); //Fake drive as we don't use it. + bool ret = this->FindFirst(dir,dta,false);// DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME); + if (!ret) { + //Path not found. Should not be possible due to removedir doing a testdir, but lets be correct + DOS_SetError(DOSERR_PATH_NOT_FOUND); + return false; + } + bool empty = true; + do { + char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr; + dta.GetResult(name,size,date,time,attr); + if (logoverlay) LOG_MSG("RemoveDir found %s",name); + if (empty && strcmp(".",name ) && strcmp("..",name)) + empty = false; //Neither . or .. so directory not empty. + } while ( (ret=this->FindNext(dta)) ); + //Always exhaust list, so drive_cache entry gets invalidated/reused. + //FindNext is done, restore error code to old value. DOS_RemoveDir will set the right one if needed. + dos.errorcode = olderror; + + if (!empty) return false; + if (logoverlay) LOG_MSG("directory empty! Hide it."); + //Directory is empty, mark it as deleted and create DBOVERLAY file. + //Ensure that overlap folder can not be created. + add_deleted_path(dir,true); + return true; + } +} + +bool Overlay_Drive::MakeDir(char * dir) { + //DOS_MakeDir tries first, before checking if the directory already exists, so doing it here as well, so that case is handled. + if (TestDir(dir)) return false; + if (overlap_folder == dir) return false; //TODO Test +#if OVERLAY_DIR + if (logoverlay) LOG_MSG("Overlay trying to make directory: %s",dir); +#else + E_Exit("Overlay trying to make directory: %s",dir); +#endif + /* Overlay: Create in Overlay only and add it to drive_cache + some entries else the drive_cache will try to access it. Needs an AddEntry for directories. */ + + //Check if leading dir is marked as deleted. + if (check_if_leading_is_deleted(dir)) return false; + + //Check if directory itself is marked as deleted + if (is_deleted_path(dir) && localDrive::TestDir(dir)) { + //Was deleted before and exists (last one is safety check) + remove_deleted_path(dir,true); + return true; + } + char newdir[CROSS_LEN]; + strcpy(newdir,overlaydir); + strcat(newdir,dir); + CROSS_FILENAME(newdir); +#if defined (WIN32) /* MS Visual C++ */ + int temp=mkdir(newdir); +#else + int temp=mkdir(newdir,0700); +#endif + if (temp==0) { + char fakename[CROSS_LEN]; + strcpy(fakename,basedir); + strcat(fakename,dir); + CROSS_FILENAME(fakename); + dirCache.AddEntryDirOverlay(fakename,true); + add_DOSdir_to_cache(dir); + } + + return (temp==0);// || ((temp!=0) && (errno==EEXIST)); +} + +bool Overlay_Drive::TestDir(char * dir) { + //First check if directory exist exclusively in the overlay. + //Currently using the update_cache cache, alternatively access the directory itself. + + //Directories are stored without a trailing backslash + char tempdir[CROSS_LEN]; + strcpy(tempdir,dir); + size_t templen = strlen(dir); + if (templen && tempdir[templen-1] == '\\') tempdir[templen-1] = 0; + +#if OVERLAY_DIR + if (is_dir_only_in_overlay(tempdir)) return true; +#endif + + //Next Check if the directory is marked as deleted or one of its leading directories is. + //(it still might exists in the localDrive) + + if (is_deleted_path(tempdir)) return false; + + // Not exclusive to overlay nor marked as deleted. Pass on to LocalDrive + return localDrive::TestDir(dir); +} + + +class OverlayFile: public localFile { +public: + OverlayFile(const char* name, FILE * handle):localFile(name,handle){ + overlay_active = false; + if (logoverlay) LOG_MSG("constructing OverlayFile: %s",name); + } + bool Write(Bit8u * data,Bit16u * size) { + Bit32u f = flags&0xf; + if (!overlay_active && (f == OPEN_READWRITE || f == OPEN_WRITE)) { + if (logoverlay) LOG_MSG("write detected, switching file for %s",GetName()); + if (*data == 0) { + if (logoverlay) LOG_MSG("OPTIMISE: truncate on switch!!!!"); + } + Bit32u a = GetTicks(); + bool r = create_copy(); + if (GetTicks()-a >2) { + if (logoverlay) LOG_MSG("OPTIMISE: switching took %d",GetTicks()-a); + } + if (!r) return false; + overlay_active = true; + + } + return localFile::Write(data,size); + } + bool create_copy(); +//private: + bool overlay_active; +}; + +//Create leading directories of a file being overlayed if they exist in the original (localDrive). +//This function is used to create copies of existing files, so all leading directories exist in the original. + +FILE* Overlay_Drive::create_file_in_overlay(char* dos_filename, char const* mode) { + + if (logoverlay) LOG_MSG("create_file_in_overlay called %s %s",dos_filename,mode); + char newname[CROSS_LEN]; + strcpy(newname,overlaydir); //TODO GOG make part of class and join in + strcat(newname,dos_filename); //HERE we need to convert it to Linux TODO + CROSS_FILENAME(newname); + + FILE* f = fopen_wrap(newname,mode); + //Check if a directories are part of the name: + char* dir = strrchr(dos_filename,'\\'); + if (!f && dir && *dir) { + if (logoverlay) LOG_MSG("Overlay: warning creating a file inside a directory %s",dos_filename); + //ensure they exist, else make them in the overlay if they exist in the original.... + Sync_leading_dirs(dos_filename); + //try again + f = fopen_wrap(newname,mode); + } + + return f; +} + +#ifndef BUFSIZ +#define BUFSIZ 2048 +#endif + +//bool OverlayFile::create_copy(DOS_File * file, char* newname) +bool OverlayFile::create_copy() { + //test if open/valid/etc + //ensure file position + if (logoverlay) LOG_MSG("create_copy called %s",GetName()); + + FILE* lhandle = this->fhandle; + fseek(lhandle,ftell(lhandle),SEEK_SET); + int location_in_old_file = ftell(lhandle); + fseek(lhandle,0L,SEEK_SET); + + FILE* newhandle = NULL; + Bit8u drive_set = GetDrive(); + if (drive_set != 0xff && drive_set < DOS_DRIVES && Drives[drive_set]){ + Overlay_Drive* od = dynamic_cast(Drives[drive_set]); + if (od) { + newhandle = od->create_file_in_overlay(GetName(),"wb+"); //todo check wb+ + } + } +// newhandle = create_file(newname,"wb+"); + if (!newhandle) return false; + char buffer[BUFSIZ]; + size_t s; + while ( (s = fread(buffer,1,BUFSIZ,lhandle)) ) fwrite(buffer, 1, s, newhandle); + fclose(lhandle); + //Set copied file handle to position of the old one + fseek(newhandle,location_in_old_file,SEEK_SET); + this->fhandle = newhandle; + //Flags ? + if (logoverlay) LOG_MSG("success"); + return true; +} + + + +static OverlayFile* ccc(DOS_File* file) { + localFile* l = dynamic_cast(file); + if (!l) E_Exit("overlay input file is not a localFile"); + //Create an overlayFile + OverlayFile* ret = new OverlayFile(l->GetName(),l->fhandle); + ret->flags = l->flags; + ret->refCtr = l->refCtr; + delete l; + return ret; +} + +Overlay_Drive::Overlay_Drive(const char * startdir,const char* overlay, Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid,Bit8u &error) +:localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid),special_prefix("DBOVERLAY") { + optimize_cache_v1 = true; //Try to not reread overlay files on deletes. Ideally drive_cache should be improved to handle deletes properly. + //Currently this flag does nothing, as the current behavior is to not reread due to caching everything. +#if defined (WIN32) + if (strcasecmp(startdir,overlay) == 0) { +#else + if (strcmp(startdir,overlay) == 0) { +#endif + //overlay directory can not be the base directory + error = 2; + return; + } + + std::string s(startdir); + std::string o(overlay); + bool s_absolute = Cross::IsPathAbsolute(s); + bool o_absolute = Cross::IsPathAbsolute(o); + error = 0; + if (s_absolute != o_absolute) { + error = 1; + return; + } + strcpy(overlaydir,overlay); + char dirname[CROSS_LEN] = { 0 }; + //Determine if overlaydir is part of the startdir. + convert_overlay_to_DOSname_in_base(dirname); + + + if(strlen(dirname) && dirname[strlen(dirname)-1] == '\\') dirname[strlen(dirname)-1] = 0; + + //add_deleted_path(dirname); //update_cache will add the overlap_folder + overlap_folder = dirname; + + update_cache(true); +} + +void Overlay_Drive::convert_overlay_to_DOSname_in_base(char* dirname ) +{ + dirname[0] = 0;//ensure good return string + if (strlen(overlaydir) >= strlen(basedir) ) { + //Needs to be longer at least. +#if defined (WIN32) +//OS2 ? + if (strncasecmp(overlaydir,basedir,strlen(basedir)) == 0) { +#else + if (strncmp(overlaydir,basedir,strlen(basedir)) == 0) { +#endif + //Beginning is the same. + char t[CROSS_LEN]; + strcpy(t,overlaydir+strlen(basedir)); + + char* p = t; + char* b = t; + + while ( (p =strchr(p,CROSS_FILESPLIT)) ) { + char directoryname[CROSS_LEN]={0}; + char dosboxdirname[CROSS_LEN]={0}; + strcpy(directoryname,dirname); + strncat(directoryname,b,p-b); + + char d[CROSS_LEN]; + strcpy(d,basedir); + strcat(d,directoryname); + CROSS_FILENAME(d); + //Try to find the corresponding directoryname in DOSBox. + if(!dirCache.GetShortName(d,dosboxdirname) ) { + //Not a long name, assume it is a short name instead + strncpy(dosboxdirname,b,p-b); + upcase(dosboxdirname); + } + + + strcat(dirname,dosboxdirname); + strcat(dirname,"\\"); + + if (logoverlay) LOG_MSG("HIDE directory: %s",dirname); + + + b=++p; + + } + } + } +} + +bool Overlay_Drive::FileOpen(DOS_File * * file,char * name,Bit32u flags) { + const char* type; + switch (flags&0xf) { + case OPEN_READ: type = "rb" ; break; + case OPEN_WRITE: type = "rb+"; break; + case OPEN_READWRITE: type = "rb+"; break; + case OPEN_READ_NO_MOD: type = "rb" ; break; //No modification of dates. LORD4.07 uses this + default: + DOS_SetError(DOSERR_ACCESS_CODE_INVALID); + return false; + } + + //Flush the buffer of handles for the same file. (Betrayal in Antara) + Bit8u i,drive=DOS_DRIVES; + localFile *lfp; + for (i=0;iIsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) { + lfp=dynamic_cast(Files[i]); + if (lfp) lfp->Flush(); + } + } + + + //Todo check name first against local tree + //if name exists, use that one instead! + //overlay file. + char newname[CROSS_LEN]; + strcpy(newname,overlaydir); + strcat(newname,name); + CROSS_FILENAME(newname); + + FILE * hand = fopen_wrap(newname,type); + bool fileopened = false; + if (hand) { + if (logoverlay) LOG_MSG("overlay file opened %s",newname); + *file=new localFile(name,hand); + (*file)->flags=flags; + fileopened = true; + } else { + ; //TODO error handling!!!! (maybe check if it exists and read only (should not happen with overlays) + } + bool overlayed = fileopened; + + //File not present in overlay, try normal drive + //TODO take care of file being marked deleted. + + if (!fileopened && !is_deleted_file(name)) fileopened = localDrive::FileOpen(file,name, OPEN_READ); + + + if (fileopened) { + if (logoverlay) LOG_MSG("file opened %s",name); + //Convert file to OverlayFile + OverlayFile* f = ccc(*file); + f->flags = flags; //ccc copies the flags of the localfile, which were not correct in this case + f->overlay_active = overlayed; //No need to switch if already in overlayed. + *file = f; + } + return fileopened; +} + + +bool Overlay_Drive::FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/) { + //TODO Check if it exists in the dirCache ? // fix addentry ? or just double check (ld and overlay) + //AddEntry looks sound to me.. + + //check if leading part of filename is a deleted directory + if (check_if_leading_is_deleted(name)) return false; + + FILE* f = create_file_in_overlay(name,"wb+"); + if(!f) { + if (logoverlay) LOG_MSG("File creation in overlay system failed %s",name); + return false; + } + *file = new localFile(name,f); + (*file)->flags = OPEN_READWRITE; + OverlayFile* of = ccc(*file); + of->overlay_active = true; + of->flags = OPEN_READWRITE; + *file = of; + //create fake name for the drive cache + char fakename[CROSS_LEN]; + strcpy(fakename,basedir); + strcat(fakename,name); + CROSS_FILENAME(fakename); + dirCache.AddEntry(fakename,true); //add it. + add_DOSname_to_cache(name); + remove_deleted_file(name,true); + return true; +} +void Overlay_Drive::add_DOSname_to_cache(const char* name) { + for (std::vector::const_iterator itc = DOSnames_cache.begin(); itc != DOSnames_cache.end();itc++){ + if (name == (*itc)) return; + } + DOSnames_cache.push_back(name); +} +void Overlay_Drive::remove_DOSname_from_cache(const char* name) { + for (std::vector::iterator it = DOSnames_cache.begin(); it != DOSnames_cache.end();it++) { + if (name == (*it)) { DOSnames_cache.erase(it); return;} + } + +} + +bool Overlay_Drive::Sync_leading_dirs(const char* dos_filename){ + const char* lastdir = strrchr(dos_filename,'\\'); + //If there are no directories, return success. + if (!lastdir) return true; + + const char* leaddir = dos_filename; + while ( (leaddir=strchr(leaddir,'\\')) != 0) { + char dirname[CROSS_LEN] = {0}; + strncpy(dirname,dos_filename,leaddir-dos_filename); + + if (logoverlay) LOG_MSG("syncdir: %s",dirname); + //Test if directory exist in base. + char dirnamebase[CROSS_LEN] ={0}; + strcpy(dirnamebase,basedir); + strcat(dirnamebase,dirname); + CROSS_FILENAME(dirnamebase); + struct stat basetest; + if (stat(dirCache.GetExpandName(dirnamebase),&basetest) == 0 && basetest.st_mode & S_IFDIR) { + if (logoverlay) LOG_MSG("base exists: %s",dirnamebase); + //Directory exists in base folder. + //Ensure it exists in overlay as well + + struct stat overlaytest; + char dirnameoverlay[CROSS_LEN] ={0}; + strcpy(dirnameoverlay,overlaydir); + strcat(dirnameoverlay,dirname); + CROSS_FILENAME(dirnameoverlay); + if (stat(dirnameoverlay,&overlaytest) == 0 ) { + //item exist. Check if it is a folder, if not a folder =>fail! + if ((overlaytest.st_mode & S_IFDIR) ==0) return false; + } else { + //folder does not exist, make it + if (logoverlay) LOG_MSG("creating %s",dirnameoverlay); +#if defined (WIN32) /* MS Visual C++ */ + int temp = mkdir(dirnameoverlay); +#else + int temp = mkdir(dirnameoverlay,0700); +#endif + if (temp != 0) return false; + } + } + leaddir = leaddir + 1; //Move to next + } + + return true; +} +void Overlay_Drive::update_cache(bool read_directory_contents) { + Bit32u a = GetTicks(); + std::vector specials; + std::vector dirnames; + std::vector filenames; + if (read_directory_contents) { + //Clear all lists + DOSnames_cache.clear(); + DOSdirs_cache.clear(); + deleted_files_in_base.clear(); + deleted_paths_in_base.clear(); + //Ensure hiding of the folder that contains the overlay, if it is part of the base folder. + add_deleted_path(overlap_folder.c_str(), false); + } + + //Needs later to support stored renames and removals of files existing in the localDrive plane. + //and by taking in account if the file names are actually already renamed. + //and taking in account that a file could have gotten an overlay version and then both need to be removed. + // + //Also what about sequences were a base file gets copied to a working save game and then removed/renamed... + //copy should be safe as then the link with the original doesn't exist. + //however the working safe can be rather complicated after a rename and delete.. + + //Currently directories existing only in the overlay can not be added to drive cache: + //1. possible workaround create empty directory in base. Drawback would break the no-touching-of-base. + //2. double up Addentry to support directories, (and adding . and .. to the newly directory so it counts as cachedin.. and won't be recached, as otherwise + // cache will realize we are faking it. + //Working on solution 2. + + //Random TODO: Does the root drive under DOS have . and .. ? + + //This function needs to be called after any localDrive function calling cacheout/deleteentry, as those throw away directories. + //either do this with a parameter stating the part that needs to be rebuild,(directory) or clear the cache by default and do it all. + + std::vector::iterator i; + std::string::size_type const prefix_lengh = special_prefix.length(); + if (read_directory_contents) { + dir_information* dirp = open_directory(overlaydir); + if (dirp == NULL) return; + // Read complete directory + char dir_name[CROSS_LEN]; + bool is_directory; + if (read_directory_first(dirp, dir_name, is_directory)) { + if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(dir_name); + else if (is_directory) dirnames.push_back(dir_name); + else filenames.push_back(dir_name); + while (read_directory_next(dirp, dir_name, is_directory)) { + if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(dir_name); + else if (is_directory) dirnames.push_back(dir_name); + else filenames.push_back(dir_name); + } + } + close_directory(dirp); + //parse directories to add them. + + + + for (i = dirnames.begin(); i != dirnames.end();i++) { + if ((*i) == ".") continue; + if ((*i) == "..") continue; + std::string testi(*i); + std::string::size_type ll = testi.length(); + //TODO: Use the dirname\. and dirname\.. for creating fake directories in the driveCache. + if( ll >2 && testi[ll-1] == '.' && testi[ll-2] == CROSS_FILESPLIT) continue; + if( ll >3 && testi[ll-1] == '.' && testi[ll-2] == '.' && testi[ll-3] == CROSS_FILESPLIT) continue; + +#if OVERLAY_DIR + char tdir[CROSS_LEN]; + strcpy(tdir,(*i).c_str()); + CROSS_DOSFILENAME(tdir); + bool dir_exists_in_base = localDrive::TestDir(tdir); +#endif + + char dir[CROSS_LEN]; + strcpy(dir,overlaydir); + strcat(dir,(*i).c_str()); + char dirpush[CROSS_LEN]; + strcpy(dirpush,(*i).c_str()); + static char end[2] = {CROSS_FILESPLIT,0}; + strcat(dirpush,end); //Linux ? + dir_information* dirp = open_directory(dir); + if (dirp == NULL) continue; + +#if OVERLAY_DIR + //Good directory, add to DOSdirs_cache if not existing in localDrive. tested earlier to prevent problems with opendir + if (!dir_exists_in_base) add_DOSdir_to_cache(tdir); +#endif + + std::string backupi(*i); + // Read complete directory + char dir_name[CROSS_LEN]; + bool is_directory; + if (read_directory_first(dirp, dir_name, is_directory)) { + if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(string(dirpush)+dir_name); + else if (is_directory) dirnames.push_back(string(dirpush)+dir_name); + else filenames.push_back(string(dirpush)+dir_name); + while (read_directory_next(dirp, dir_name, is_directory)) { + if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(string(dirpush)+dir_name); + else if (is_directory) dirnames.push_back(string(dirpush)+dir_name); + else filenames.push_back(string(dirpush)+dir_name); + } + } + close_directory(dirp); + for(i = dirnames.begin(); i != dirnames.end();i++) { + if ( (*i) == backupi) break; //find current directory again, for the next round. + } + } + } + + + if (read_directory_contents) { + for( i = filenames.begin(); i != filenames.end(); i++) { + char dosname[CROSS_LEN]; + strcpy(dosname,(*i).c_str()); + upcase(dosname); //Should not be really needed, as uppercase in the overlay is a requirement... + CROSS_DOSFILENAME(dosname); + if (logoverlay) LOG_MSG("update cache add dosname %s",dosname); + DOSnames_cache.push_back(dosname); + } + } + +#if OVERLAY_DIR + for (i = DOSdirs_cache.begin(); i !=DOSdirs_cache.end(); i++) { + char fakename[CROSS_LEN]; + strcpy(fakename,basedir); + strcat(fakename,(*i).c_str()); + CROSS_FILENAME(fakename); + dirCache.AddEntryDirOverlay(fakename,true); + } +#endif + + for (i = DOSnames_cache.begin(); i != DOSnames_cache.end(); i++) { + char fakename[CROSS_LEN]; + strcpy(fakename,basedir); + strcat(fakename,(*i).c_str()); + CROSS_FILENAME(fakename); + dirCache.AddEntry(fakename,true); + } + + if (read_directory_contents) { + for (i = specials.begin(); i != specials.end();i++) { + //Specials look like this DBOVERLAY_YYY_FILENAME.EXT or DIRNAME[\/]DBOVERLAY_YYY_FILENAME.EXT where + //YYY is the operation involved. Currently only DEL is supported. + //DEL = file marked as deleted, (but exists in localDrive!) + std::string name(*i); + std::string special_dir(""); + std::string special_file(""); + std::string special_operation(""); + std::string::size_type s = name.find(special_prefix); + if (s == std::string::npos) continue; + if (s) { + special_dir = name.substr(0,s); + name.erase(0,s); + } + name.erase(0,special_prefix.length()+1); //Erase DBOVERLAY_ + s = name.find("_"); + if (s == std::string::npos ||s == 0) continue; + special_operation = name.substr(0,s); + name.erase(0,s + 1); + special_file = name; + if (special_file.length() == 0) continue; + if (special_operation == "DEL") { + name = special_dir + special_file; + //CROSS_DOSFILENAME for strings: + while ( (s = name.find('/')) != std::string::npos) name.replace(s,1,"\\"); + + add_deleted_file(name.c_str(),false); + } else if (special_operation == "RMD") { + name = special_dir + special_file; + //CROSS_DOSFILENAME for strings: + while ( (s = name.find('/')) != std::string::npos) name.replace(s,1,"\\"); + add_deleted_path(name.c_str(),false); + + } else { + if (logoverlay) LOG_MSG("unsupported operation %s on %s",special_operation.c_str(),(*i).c_str()); + } + + } + } + if (logoverlay) LOG_MSG("OPTIMISE: update cache took %d",GetTicks()-a); +} + +bool Overlay_Drive::FindNext(DOS_DTA & dta) { + + char * dir_ent; + struct stat stat_block; + char full_name[CROSS_LEN]; + char dir_entcopy[CROSS_LEN]; + + Bit8u srch_attr;char srch_pattern[DOS_NAMELENGTH_ASCII]; + Bit8u find_attr; + + dta.GetSearchParams(srch_attr,srch_pattern); + Bit16u id = dta.GetDirID(); + +again: + if (!dirCache.FindNext(id,dir_ent)) { + DOS_SetError(DOSERR_NO_MORE_FILES); + return false; + } + if(!WildFileCmp(dir_ent,srch_pattern)) goto again; + + strcpy(full_name,srchInfo[id].srch_dir); + strcat(full_name,dir_ent); + + //GetExpandName might indirectly destroy dir_ent (by caching in a new directory + //and due to its design dir_ent might be lost.) + //Copying dir_ent first + strcpy(dir_entcopy,dir_ent); + + //First try overlay: + char ovname[CROSS_LEN]; + char relativename[CROSS_LEN]; + strcpy(relativename,srchInfo[id].srch_dir); + //strip off basedir: //TODO cleanup + strcpy(ovname,overlaydir); + char* prel = full_name + strlen(basedir); + + + +#if 0 + //Check hidden/deleted directories first. TODO is this really needed. If the directory exist in the overlay things are weird anyway. + //the deleted paths are added to the deleted_files list. + if (is_deleted_dir(prel)) { + LOG_MSG("skipping early out deleted dir %s",prel); + goto again; + } +#endif + + strcat(ovname,prel); + bool statok = ( stat(ovname,&stat_block)==0); + + if (logoverlay) LOG_MSG("listing %s",dir_entcopy); + if (statok) { + if (logoverlay) LOG_MSG("using overlay data for %s : %s",full_name, ovname); + } else { + char preldos[CROSS_LEN]; + strcpy(preldos,prel); + CROSS_DOSFILENAME(preldos); + if (is_deleted_file(preldos)) { //dir.. maybe lower or keep it as is TODO + if (logoverlay) LOG_MSG("skipping deleted file %s %s %s",preldos,full_name,ovname); + goto again; + } + if (stat(dirCache.GetExpandName(full_name),&stat_block)!=0) { + if (logoverlay) LOG_MSG("stat failed for %s . This should not happen.",dirCache.GetExpandName(full_name)); + goto again;//No symlinks and such + } + } + + if(stat_block.st_mode & S_IFDIR) find_attr=DOS_ATTR_DIRECTORY; + else find_attr=DOS_ATTR_ARCHIVE; + if (~srch_attr & find_attr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM)) goto again; + + + /* file is okay, setup everything to be copied in DTA Block */ + char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size; + + if(strlen(dir_entcopy)tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); + find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); + } else { + find_time=6; + find_date=4; + } + dta.SetResult(find_name,find_size,find_date,find_time,find_attr); + return true; +} + + + +bool Overlay_Drive::FileUnlink(char * name) { +//TODO check the basedir for file existence in order if we need to add the file to deleted file list. + Bit32u a = GetTicks(); + if (logoverlay) LOG_MSG("calling unlink on %s",name); + char basename[CROSS_LEN]; + strcpy(basename,basedir); + strcat(basename,name); + CROSS_FILENAME(basename); + + + char overlayname[CROSS_LEN]; + strcpy(overlayname,overlaydir); + strcat(overlayname,name); + CROSS_FILENAME(overlayname); +// char *fullname = dirCache.GetExpandName(newname); + if (unlink(overlayname)) { + //Unlink failed for some reason try finding it. + struct stat buffer; + if(stat(overlayname,&buffer)) { + //file not found in overlay, check the basedrive + //Check if file not already deleted + if (is_deleted_file(name)) return false; + + + char *fullname = dirCache.GetExpandName(basename); + if (stat(fullname,&buffer)) return false; // File not found in either, return file false. + //File does exist in normal drive. + //Maybe do something with the drive_cache. + add_deleted_file(name,true); + return true; +// E_Exit("trying to remove existing non-overlay file %s",name); + } + FILE* file_writable = fopen_wrap(overlayname,"rb+"); + if(!file_writable) return false; //No access ? ERROR MESSAGE NOT SET. FIXME ? + fclose(file_writable); + + //File exists and can technically be deleted, nevertheless it failed. + //This means that the file is probably open by some process. + //See if We have it open. + bool found_file = false; + for(Bitu i = 0;i < DOS_FILES;i++){ + if(Files[i] && Files[i]->IsName(name)) { + Bitu max = DOS_FILES; + while(Files[i]->IsOpen() && max--) { + Files[i]->Close(); + if (Files[i]->RemoveRef()<=0) break; + } + found_file=true; + } + } + if(!found_file) return false; + if (unlink(overlayname) == 0) { //Overlay file removed + //Mark basefile as deleted if it exists: + if (localDrive::FileExists(name)) add_deleted_file(name,true); + remove_DOSname_from_cache(name); //Should be an else ? although better safe than sorry. + //Handle this better + dirCache.DeleteEntry(basename); + update_cache(false); + //Check if it exists in the base dir as well + + return true; + } + return false; + } else { //Removed from overlay. + //TODO IF it exists in the basedir: and more locations above. + if (localDrive::FileExists(name)) add_deleted_file(name,true); + remove_DOSname_from_cache(name); + //TODODO remove from the update_cache cache as well + //Handle this better + //Check if it exists in the base dir as well + dirCache.DeleteEntry(basename); + + update_cache(false); + if (logoverlay) LOG_MSG("OPTIMISE: unlink took %d",GetTicks()-a); + return true; + } +} + + +bool Overlay_Drive::GetFileAttr(char * name,Bit16u * attr) { + char overlayname[CROSS_LEN]; + strcpy(overlayname,overlaydir); + strcat(overlayname,name); + CROSS_FILENAME(overlayname); + + struct stat status; + if (stat(overlayname,&status)==0) { + *attr=DOS_ATTR_ARCHIVE; + if(status.st_mode & S_IFDIR) *attr|=DOS_ATTR_DIRECTORY; + return true; + } + //Maybe check for deleted path as well + if (is_deleted_file(name)) { + *attr = 0; + return false; + } + return localDrive::GetFileAttr(name,attr); + +} + + +void Overlay_Drive::add_deleted_file(const char* name,bool create_on_disk) { + if (logoverlay) LOG_MSG("add del file %s",name); + if (!is_deleted_file(name)) { + deleted_files_in_base.push_back(name); + if (create_on_disk) add_special_file_to_disk(name, "DEL"); + + } +} + +void Overlay_Drive::add_special_file_to_disk(const char* dosname, const char* operation) { + std::string name = create_filename_of_special_operation(dosname, operation); + char overlayname[CROSS_LEN]; + strcpy(overlayname,overlaydir); + strcat(overlayname,name.c_str()); + CROSS_FILENAME(overlayname); + FILE* f = fopen_wrap(overlayname,"wb+"); + if (!f) { + Sync_leading_dirs(dosname); + f = fopen_wrap(overlayname,"wb+"); + } + if (!f) E_Exit("Failed creation of %s",overlayname); + char buf[5] = {'e','m','p','t','y'}; + fwrite(buf,5,1,f); + fclose(f); +} + +void Overlay_Drive::remove_special_file_from_disk(const char* dosname, const char* operation) { + std::string name = create_filename_of_special_operation(dosname,operation); + char overlayname[CROSS_LEN]; + strcpy(overlayname,overlaydir); + strcat(overlayname,name.c_str()); + CROSS_FILENAME(overlayname); + if(unlink(overlayname) != 0) E_Exit("Failed removal of %s",overlayname); +} + +std::string Overlay_Drive::create_filename_of_special_operation(const char* dosname, const char* operation) { + std::string res(dosname); + std::string::size_type s = res.rfind("\\"); //CHECK DOS or host endings.... on update_cache + if (s == std::string::npos) s = 0; else s++; + std::string oper = special_prefix +"_" +operation +"_"; + res.insert(s,oper); + return res; +} + + +bool Overlay_Drive::is_dir_only_in_overlay(const char* name) { + if (!name || !*name) return false; + if (DOSdirs_cache.empty()) return false; + for(std::vector::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it++) { + if (*it == name) return true; + } + return false; +} + +bool Overlay_Drive::is_deleted_file(const char* name) { + if (!name || !*name) return false; + if (deleted_files_in_base.empty()) return false; + for(std::vector::iterator it = deleted_files_in_base.begin(); it != deleted_files_in_base.end(); it++) { + if (*it == name) return true; + } + return false; +} + +void Overlay_Drive::add_DOSdir_to_cache(const char* name) { + if (!name || !*name ) return; //Skip empty file. + LOG_MSG("Adding name to overlay_only_dir_cache %s",name); + if (!is_dir_only_in_overlay(name)) { + DOSdirs_cache.push_back(name); + } +} + +void Overlay_Drive::remove_DOSdir_from_cache(const char* name) { + for(std::vector::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it++) { + if ( *it == name) { + DOSdirs_cache.erase(it); + return; + } + } +} + +void Overlay_Drive::remove_deleted_file(const char* name,bool create_on_disk) { + for(std::vector::iterator it = deleted_files_in_base.begin(); it != deleted_files_in_base.end(); it++) { + if (*it == name) { + deleted_files_in_base.erase(it); + if (create_on_disk) remove_special_file_from_disk(name, "DEL"); + return; + } + } +} +void Overlay_Drive::add_deleted_path(const char* name, bool create_on_disk) { + if (!name || !*name ) return; //Skip empty file. + if (logoverlay) LOG_MSG("add del path %s",name); + if (!is_deleted_path(name)) { + deleted_paths_in_base.push_back(name); + //Add it to deleted files as well, so it gets skipped in FindNext. + //Maybe revise that. + if (create_on_disk) add_special_file_to_disk(name,"RMD"); + add_deleted_file(name,false); + } +} +bool Overlay_Drive::is_deleted_path(const char* name) { + if (!name || !*name) return false; + if (deleted_paths_in_base.empty()) return false; + std::string sname(name); + std::string::size_type namelen = sname.length();; + for(std::vector::iterator it = deleted_paths_in_base.begin(); it != deleted_paths_in_base.end(); it++) { + std::string::size_type blockedlen = (*it).length(); + if (namelen < blockedlen) continue; + //See if input starts with name. + std::string::size_type n = sname.find(*it); + if (n == 0 && ((namelen == blockedlen) || *(name+blockedlen) =='\\' )) return true; + } + return false; +} + +void Overlay_Drive::remove_deleted_path(const char* name, bool create_on_disk) { + for(std::vector::iterator it = deleted_paths_in_base.begin(); it != deleted_paths_in_base.end(); it++) { + if (*it == name) { + deleted_paths_in_base.erase(it); + remove_deleted_file(name,false); //Rethink maybe. + if (create_on_disk) remove_special_file_from_disk(name,"RMD"); + break; + } + } +} +bool Overlay_Drive::check_if_leading_is_deleted(const char* name){ + const char* dname = strrchr(name,'\\'); + if (dname != NULL) { + char dirname[CROSS_LEN]; + strncpy(dirname,name,dname - name); + dirname[dname - name] = 0; + if (is_deleted_path(dirname)) return true; + } + return false; +} + +bool Overlay_Drive::FileExists(const char* name) { + char overlayname[CROSS_LEN]; + strcpy(overlayname,overlaydir); + strcat(overlayname,name); + CROSS_FILENAME(overlayname); + struct stat temp_stat; + if(stat(overlayname,&temp_stat)==0 && (temp_stat.st_mode & S_IFDIR)==0) return true; + + if (is_deleted_file(name)) return false; + + return localDrive::FileExists(name); +} + +#if 1 +bool Overlay_Drive::Rename(char * oldname,char * newname) { + //TODO with cache function! +//Tricky function. +//Renaming directories is currently not supported, due the drive_cache not handling that smoothly. +//So oldname is directory => Exit! +//If oldname is on overlay => simple rename. +//if oldname is on base => copy file to overlay with new name and mark old file as deleted. +//More advanced version. keep track of the file being renamed in order to detect that the file is being renamed back. + Bit16u attr=0; + if (!GetFileAttr(oldname,&attr)) E_Exit("rename, but source doesn't exist, should not happen %s",oldname); + if (attr&DOS_ATTR_DIRECTORY) { + //See if the directory exists only in the overlay, then it should be possible. +#if OVERLAY_DIR + if (localDrive::TestDir(oldname)) E_Exit("Overlay: renaming base directory %s to %s not yet supported", oldname,newname); +#endif + E_Exit("renaming directory %s to %s . Not yet supported in Overlay",oldname,newname); //TODO + } + + Bit32u a = GetTicks(); + //First generate overlay names. + char overlaynameold[CROSS_LEN]; + strcpy(overlaynameold,overlaydir); + strcat(overlaynameold,oldname); + CROSS_FILENAME(overlaynameold); + + char overlaynamenew[CROSS_LEN]; + strcpy(overlaynamenew,overlaydir); + strcat(overlaynamenew,newname); + CROSS_FILENAME(overlaynamenew); + + //No need to check if the original is marked as deleted, as GetFileAttr would fail if it did. + + //Check if overlay source file exists + struct stat tempstat; + int temp = -1; + if (stat(overlaynameold,&tempstat) ==0) { + //Simple rename + temp = rename(overlaynameold,overlaynamenew); + //TODO CHECK if base has a file with same oldname!!!!! if it does mark it as deleted!! + if (localDrive::FileExists(oldname)) add_deleted_file(oldname,true); + } else { + Bit32u aa = GetTicks(); + //File exists in the basedrive. Make a copy and mark old one as deleted. + char newold[CROSS_LEN]; + strcpy(newold,basedir); + strcat(newold,oldname); + CROSS_FILENAME(newold); + dirCache.ExpandName(newold); + FILE* o = fopen_wrap(newold,"rb"); + if (!o) return false; + FILE* n = create_file_in_overlay(newname,"wb+"); + if (!n) {fclose(o); return false;} + char buffer[BUFSIZ]; + size_t s; + while ( (s = fread(buffer,1,BUFSIZ,o)) ) fwrite(buffer, 1, s, n); + fclose(o); fclose(n); + + //File copied. + //Mark old file as deleted + add_deleted_file(oldname,true); + temp =0; //success + if (logoverlay) LOG_MSG("OPTIMISE: update rename with copy took %d",GetTicks()-aa); + + } + if (temp ==0) { + //handle the drive_cache (a bit better) + //Ensure that the file is not marked as deleted anymore. + if (is_deleted_file(newname)) remove_deleted_file(newname,true); + dirCache.EmptyCache(); + update_cache(true); + if (logoverlay) LOG_MSG("OPTIMISE: rename took %d",GetTicks()-a); + + } + return (temp==0); + +} +#endif + +bool Overlay_Drive::FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst) { + if (logoverlay) LOG_MSG("FindFirst in %s",_dir); + + if (is_deleted_path(_dir)) { + //No accidental listing of files in there. + DOS_SetError(DOSERR_PATH_NOT_FOUND); + return false; + } + + return localDrive::FindFirst(_dir,dta,fcb_findfirst); +} + +bool Overlay_Drive::FileStat(const char* name, FileStat_Block * const stat_block) { + char overlayname[CROSS_LEN]; + strcpy(overlayname,overlaydir); + strcat(overlayname,name); + CROSS_FILENAME(overlayname); + struct stat temp_stat; + if(stat(overlayname,&temp_stat) != 0) { + if (is_deleted_file(name)) return false; + return localDrive::FileStat(name,stat_block); + } + /* Convert the stat to a FileStat */ + struct tm *time; + if((time=localtime(&temp_stat.st_mtime))!=0) { + stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); + stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); + } else { + // ... But this function is not used at the moment. + } + stat_block->size=(Bit32u)temp_stat.st_size; + return true; +} + +Bits Overlay_Drive::UnMount(void) { + delete this; + return 0; +} +void Overlay_Drive::EmptyCache(void){ + localDrive::EmptyCache(); + update_cache(true);//lets rebuild it. +} + diff --git a/src/dos/drive_virtual.cpp b/src/dos/drive_virtual.cpp index 43bfe8d..b026128 100644 --- a/src/dos/drive_virtual.cpp +++ b/src/dos/drive_virtual.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include @@ -205,11 +205,11 @@ bool Virtual_Drive::FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst) { Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII]; dta.GetSearchParams(attr,pattern); if (attr == DOS_ATTR_VOLUME) { - dta.SetResult("DOSBOX",0,0,0,DOS_ATTR_VOLUME); + dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME); return true; } else if ((attr & DOS_ATTR_VOLUME) && !fcb_findfirst) { - if (WildFileCmp("DOSBOX",pattern)) { - dta.SetResult("DOSBOX",0,0,0,DOS_ATTR_VOLUME); + if (WildFileCmp(GetLabel(),pattern)) { + dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME); return true; } } @@ -271,3 +271,6 @@ Bits Virtual_Drive::UnMount(void) { return 1; } +char const* Virtual_Drive::GetLabel(void) { + return "DOSBOX"; +} diff --git a/src/dos/drives.cpp b/src/dos/drives.cpp index 2f55d69..6b312bd 100644 --- a/src/dos/drives.cpp +++ b/src/dos/drives.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -132,7 +132,7 @@ void DriveManager::InitializeDrive(int drive) { driveInfo.currentDisk = 0; DOS_Drive* disk = driveInfo.disks[driveInfo.currentDisk]; Drives[currentDrive] = disk; - disk->Activate(); + if (driveInfo.disks.size() > 1) disk->Activate(); } } @@ -169,26 +169,28 @@ void DriveManager::CycleDisk(bool pressed) { } */ -void DriveManager::CycleAllDisks(void) { - for (int idrive=0; idrive 1) { - // cycle disk - int currentDisk = driveInfos[idrive].currentDisk; - DOS_Drive* oldDisk = driveInfos[idrive].disks[currentDisk]; - currentDisk = (currentDisk + 1) % numDisks; - DOS_Drive* newDisk = driveInfos[idrive].disks[currentDisk]; - driveInfos[idrive].currentDisk = currentDisk; - - // copy working directory, acquire system resources and finally switch to next drive - strcpy(newDisk->curdir, oldDisk->curdir); - newDisk->Activate(); - Drives[idrive] = newDisk; - LOG_MSG("Drive %c: disk %d of %d now active", 'A'+idrive, currentDisk+1, numDisks); - } +void DriveManager::CycleDisks(int drive, bool notify) { + int numDisks = (int)driveInfos[drive].disks.size(); + if (numDisks > 1) { + // cycle disk + int currentDisk = driveInfos[drive].currentDisk; + DOS_Drive* oldDisk = driveInfos[drive].disks[currentDisk]; + currentDisk = (currentDisk + 1) % numDisks; + DOS_Drive* newDisk = driveInfos[drive].disks[currentDisk]; + driveInfos[drive].currentDisk = currentDisk; + + // copy working directory, acquire system resources and finally switch to next drive + strcpy(newDisk->curdir, oldDisk->curdir); + newDisk->Activate(); + Drives[drive] = newDisk; + if (notify) LOG_MSG("Drive %c: disk %d of %d now active", 'A'+drive, currentDisk+1, numDisks); } } +void DriveManager::CycleAllDisks(void) { + for (int idrive=0; idrive +#include #include #include "dos_system.h" #include "shell.h" /* for DOS_Shell */ -#include "bios_disk.h" /* for fatDrive */ bool WildFileCmp(const char * file, const char * wild); void Set_Label(char const * const input, char * const output, bool cdrom); @@ -36,6 +36,7 @@ public: static int UnmountDrive(int drive); // static void CycleDrive(bool pressed); // static void CycleDisk(bool pressed); + static void CycleDisks(int drive, bool notify); static void CycleAllDisks(void); static void Init(Section* sec); @@ -70,13 +71,17 @@ public: virtual bool isRemote(void); virtual bool isRemovable(void); virtual Bits UnMount(void); -private: + const char* getBasedir() {return basedir;}; +protected: char basedir[CROSS_LEN]; - friend void DOS_Shell::CMD_SUBST(char* args); +private: + friend void DOS_Shell::CMD_SUBST(char* args); +protected: struct { char srch_dir[CROSS_LEN]; } srchInfo[MAX_OPENDIRS]; +private: struct { Bit16u bytes_sector; Bit8u sectors_cluster; @@ -142,7 +147,8 @@ struct partTable { #ifdef _MSC_VER #pragma pack () #endif - +//Forward +class imageDisk; class fatDrive : public DOS_Drive { public: fatDrive(const char * sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit32u startSector); @@ -164,12 +170,15 @@ public: virtual bool isRemovable(void); virtual Bits UnMount(void); public: + Bit8u readSector(Bit32u sectnum, void * data); + Bit8u writeSector(Bit32u sectnum, void * data); Bit32u getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos); Bit32u getSectorSize(void); + Bit32u getClusterSize(void); Bit32u getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector); bool allocateCluster(Bit32u useCluster, Bit32u prevCluster); Bit32u appendCluster(Bit32u startCluster); - void deleteClustChain(Bit32u startCluster); + void deleteClustChain(Bit32u startCluster, Bit32u bytePos); Bit32u getFirstFreeClust(void); bool directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum, Bit32s start=0); bool directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum); @@ -199,6 +208,7 @@ private: } allocation; bootstrap bootbuffer; + bool absolute; Bit8u fattype; Bit32u CountOfClusters; Bit32u partSectOff; @@ -207,6 +217,9 @@ private: Bit32u cwdDirCluster; Bit32u dirPosition; /* Position in directory search */ + + Bit8u fatSectBuffer[1024]; + Bit32u curFatSect; }; @@ -360,6 +373,7 @@ private: Bit8u data[ISO_FRAMESIZE]; } sectorHashEntries[ISO_MAX_HASH_TABLE_SIZE]; + bool iso; bool dataCD; isoDirEntry rootEntry; Bit8u mediaid; @@ -392,10 +406,63 @@ public: bool isRemote(void); virtual bool isRemovable(void); virtual Bits UnMount(void); + virtual char const* GetLabel(void); private: VFILE_Block * search_file; }; +class Overlay_Drive: public localDrive { +public: + Overlay_Drive(const char * startdir,const char* overlay, Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid,Bit8u &error); + virtual bool FileOpen(DOS_File * * file,char * name,Bit32u flags); + virtual bool FileCreate(DOS_File * * file,char * name,Bit16u /*attributes*/); + virtual bool FindFirst(char * _dir,DOS_DTA & dta,bool fcb_findfirst); + virtual bool FindNext(DOS_DTA & dta); + virtual bool FileUnlink(char * name); + virtual bool GetFileAttr(char * name,Bit16u * attr); + virtual bool FileExists(const char* name); + virtual bool Rename(char * oldname,char * newname); + virtual bool FileStat(const char* name, FileStat_Block * const stat_block); + virtual void EmptyCache(void); + + FILE* create_file_in_overlay(char* dos_filename, char const* mode); + virtual Bits UnMount(void); + virtual bool TestDir(char * dir); + virtual bool RemoveDir(char * dir); + virtual bool MakeDir(char * dir); +private: + char overlaydir[CROSS_LEN]; + bool optimize_cache_v1; + bool Sync_leading_dirs(const char* dos_filename); + void add_DOSname_to_cache(const char* name); + void remove_DOSname_from_cache(const char* name); + void add_DOSdir_to_cache(const char* name); + void remove_DOSdir_from_cache(const char* name); + void update_cache(bool read_directory_contents = false); + + std::vector deleted_files_in_base; //Set is probably better, or some other solution (involving the disk). + std::vector deleted_paths_in_base; //Currently only used to hide the overlay folder. + std::string overlap_folder; + void add_deleted_file(const char* name, bool create_on_disk); + void remove_deleted_file(const char* name, bool create_on_disk); + bool is_deleted_file(const char* name); + void add_deleted_path(const char* name, bool create_on_disk); + void remove_deleted_path(const char* name, bool create_on_disk); + bool is_deleted_path(const char* name); + bool check_if_leading_is_deleted(const char* name); + + bool is_dir_only_in_overlay(const char* name); //cached + + + void remove_special_file_from_disk(const char* dosname, const char* operation); + void add_special_file_to_disk(const char* dosname, const char* operation); + std::string create_filename_of_special_operation(const char* dosname, const char* operation); + void convert_overlay_to_DOSname_in_base(char* dirname ); + //For caching the update_cache routine. + std::vector DOSnames_cache; //Also set is probably better. + std::vector DOSdirs_cache; //Can not blindly change its type. it is important that subdirs come after the parent directory. + const std::string special_prefix; +}; #endif diff --git a/src/dosbox.cpp b/src/dosbox.cpp index f2cbade..47af6a7 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -88,7 +88,7 @@ void MPU401_Init(Section*); void PCSPEAKER_Init(Section*); void TANDYSOUND_Init(Section*); void DISNEY_Init(Section*); -void SERIAL_Init(Section*); +void SERIAL_Init(Section*); #if C_IPX @@ -128,6 +128,7 @@ static Bit32u ticksAdded; Bit32s ticksDone; Bit32u ticksScheduled; bool ticksLocked; +void increaseticks(); static Bitu Normal_Loop(void) { Bits ret; @@ -148,91 +149,161 @@ static Bitu Normal_Loop(void) { if (ticksRemain>0) { TIMER_AddTick(); ticksRemain--; - } else goto increaseticks; + } else {increaseticks();return 0;} } } -increaseticks: - if (GCC_UNLIKELY(ticksLocked)) { +} + +//For trying other delays +#define wrap_delay(a) SDL_Delay(a) + +void increaseticks() { //Make it return ticksRemain and set it in the function above to remove the global variable. + if (GCC_UNLIKELY(ticksLocked)) { // For Fast Forward Mode ticksRemain=5; /* Reset any auto cycle guessing for this frame */ ticksLast = GetTicks(); ticksAdded = 0; ticksDone = 0; ticksScheduled = 0; - } else { - Bit32u ticksNew; - ticksNew=GetTicks(); - ticksScheduled += ticksAdded; - if (ticksNew > ticksLast) { - ticksRemain = ticksNew-ticksLast; - ticksLast = ticksNew; - ticksDone += ticksRemain; - if ( ticksRemain > 20 ) { - ticksRemain = 20; - } - ticksAdded = ticksRemain; - if (CPU_CycleAutoAdjust && !CPU_SkipCycleAutoAdjust) { - if (ticksScheduled >= 250 || ticksDone >= 250 || (ticksAdded > 15 && ticksScheduled >= 5) ) { - if(ticksDone < 1) ticksDone = 1; // Protect against div by zero - /* ratio we are aiming for is around 90% usage*/ - Bit32s ratio = (ticksScheduled * (CPU_CyclePercUsed*90*1024/100/100)) / ticksDone; - Bit32s new_cmax = CPU_CycleMax; - Bit64s cproc = (Bit64s)CPU_CycleMax * (Bit64s)ticksScheduled; - if (cproc > 0) { - /* ignore the cycles added due to the io delay code in order - to have smoother auto cycle adjustments */ - double ratioremoved = (double) CPU_IODelayRemoved / (double) cproc; - if (ratioremoved < 1.0) { - ratio = (Bit32s)((double)ratio * (1 - ratioremoved)); - /* Don't allow very high ratio which can cause us to lock as we don't scale down - * for very low ratios. High ratio might result because of timing resolution */ - if (ticksScheduled >= 250 && ticksDone < 10 && ratio > 20480) - ratio = 20480; - Bit64s cmax_scaled = (Bit64s)CPU_CycleMax * (Bit64s)ratio; - if (ratio <= 1024) - new_cmax = (Bit32s)(cmax_scaled / (Bit64s)1024); - else - new_cmax = (Bit32s)(1 + (CPU_CycleMax >> 1) + cmax_scaled / (Bit64s)2048); - } - } + return; + } + + static Bit32s lastsleepDone = -1; + static Bitu sleep1count = 0; - if (new_cmax10) { - /* ratios below 12% along with a large time since the last update - has taken place are most likely caused by heavy load through a - different application, the cycles adjusting is skipped as well */ - if ((ratio>120) || (ticksDone<700)) { - CPU_CycleMax = new_cmax; - if (CPU_CycleLimit > 0) { - if (CPU_CycleMax>CPU_CycleLimit) CPU_CycleMax = CPU_CycleLimit; - } - } - } - CPU_IODelayRemoved = 0; - ticksDone = 0; - ticksScheduled = 0; - } else if (ticksAdded > 15) { - /* ticksAdded > 15 but ticksScheduled < 5, lower the cycles - but do not reset the scheduled/done ticks to take them into - account during the next auto cycle adjustment */ - CPU_CycleMax /= 3; - if (CPU_CycleMax < CPU_CYCLES_LOWER_LIMIT) - CPU_CycleMax = CPU_CYCLES_LOWER_LIMIT; + if (!CPU_CycleAutoAdjust || CPU_SkipCycleAutoAdjust || sleep1count < 3) { + wrap_delay(1); + } else { + /* Certain configurations always give an exact sleepingtime of 1, this causes problems due to the fact that + dosbox keeps track of full blocks. + This code introduces some randomness to the time slept, which improves stability on those configurations + */ + static const Bit32u sleeppattern[] = { 2, 2, 3, 2, 2, 4, 2}; + static Bit32u sleepindex = 0; + if (ticksDone != lastsleepDone) sleepindex = 0; + wrap_delay(sleeppattern[sleepindex++]); + sleepindex %= sizeof(sleeppattern) / sizeof(sleeppattern[0]); + } + Bit32s timeslept = GetTicks() - ticksNew; + // Count how many times in the current block (of 250 ms) the time slept was 1 ms + if (CPU_CycleAutoAdjust && !CPU_SkipCycleAutoAdjust && timeslept == 1) sleep1count++; + lastsleepDone = ticksDone; + + // Update ticksDone with the time spent sleeping + ticksDone -= timeslept; + if (ticksDone < 0) + ticksDone = 0; + return; //0 + + // If we do work this tick and sleep till the next tick, then ticksDone is decreased, + // despite the fact that work was done as well in this tick. Maybe make it depend on an extra parameter. + // What do we know: ticksRemain = 0 (condition to enter this function) + // ticksNew = time before sleeping + + // maybe keep track of sleeped time in this frame, and use sleeped and done as indicators. (and take care of the fact there + // are frames that have both. + } + + //TicksNew > ticksLast + ticksRemain = ticksNew-ticksLast; + ticksLast = ticksNew; + ticksDone += ticksRemain; + if ( ticksRemain > 20 ) { +// LOG(LOG_MISC,LOG_ERROR)("large remain %d",ticksRemain); + ticksRemain = 20; + } + ticksAdded = ticksRemain; + + // Is the system in auto cycle mode guessing ? If not just exit. (It can be temporary disabled) + if (!CPU_CycleAutoAdjust || CPU_SkipCycleAutoAdjust) return; + + if (ticksScheduled >= 250 || ticksDone >= 250 || (ticksAdded > 15 && ticksScheduled >= 5) ) { + if(ticksDone < 1) ticksDone = 1; // Protect against div by zero + /* ratio we are aiming for is around 90% usage*/ + Bit32s ratio = (ticksScheduled * (CPU_CyclePercUsed*90*1024/100/100)) / ticksDone; + Bit32s new_cmax = CPU_CycleMax; + Bit64s cproc = (Bit64s)CPU_CycleMax * (Bit64s)ticksScheduled; + double ratioremoved = 0.0; //increase scope for logging + if (cproc > 0) { + /* ignore the cycles added due to the IO delay code in order + to have smoother auto cycle adjustments */ + ratioremoved = (double) CPU_IODelayRemoved / (double) cproc; + if (ratioremoved < 1.0) { + double ratio_not_removed = 1 - ratioremoved; + ratio = (Bit32s)((double)ratio * ratio_not_removed); + + /* Don't allow very high ratio which can cause us to lock as we don't scale down + * for very low ratios. High ratio might result because of timing resolution */ + if (ticksScheduled >= 250 && ticksDone < 10 && ratio > 16384) + ratio = 16384; + + // Limit the ratio even more when the cycles are already way above the realmode default. + if (ticksScheduled >= 250 && ticksDone < 10 && ratio > 5120 && CPU_CycleMax > 50000) + ratio = 5120; + + // When downscaling multiple times in a row, ensure a minimum amount of downscaling + if (ticksAdded > 15 && ticksScheduled >= 5 && ticksScheduled <= 20 && ratio > 800) + ratio = 800; + + if (ratio <= 1024) { + // ratio_not_removed = 1.0; //enabling this restores the old formula + double r = (1.0 + ratio_not_removed) /(ratio_not_removed + 1024.0/(static_cast(ratio))); + new_cmax = 1 + static_cast(CPU_CycleMax * r); + } else { + Bit64s ratio_with_removed = (Bit64s) ((((double)ratio - 1024.0) * ratio_not_removed) + 1024.0); + Bit64s cmax_scaled = (Bit64s)CPU_CycleMax * ratio_with_removed; + new_cmax = (Bit32s)(1 + (CPU_CycleMax >> 1) + cmax_scaled / (Bit64s)2048); } } - } else { - ticksAdded = 0; - SDL_Delay(1); - ticksDone -= GetTicks() - ticksNew; - if (ticksDone < 0) - ticksDone = 0; } - } - return 0; + + if (new_cmax < CPU_CYCLES_LOWER_LIMIT) + new_cmax = CPU_CYCLES_LOWER_LIMIT; + /* + LOG(LOG_MISC,LOG_ERROR)("cyclelog: current %06d cmax %06d ratio %05d done %03d sched %03d Add %d rr %4.2f", + CPU_CycleMax, + new_cmax, + ratio, + ticksDone, + ticksScheduled, + ticksAdded, + ratioremoved); + */ + + /* ratios below 1% are considered to be dropouts due to + temporary load imbalance, the cycles adjusting is skipped */ + if (ratio > 10) { + /* ratios below 12% along with a large time since the last update + has taken place are most likely caused by heavy load through a + different application, the cycles adjusting is skipped as well */ + if ((ratio > 120) || (ticksDone < 700)) { + CPU_CycleMax = new_cmax; + if (CPU_CycleLimit > 0) { + if (CPU_CycleMax > CPU_CycleLimit) CPU_CycleMax = CPU_CycleLimit; + } else if (CPU_CycleMax > 2000000) CPU_CycleMax = 2000000; //Hardcoded limit, if no limit was specified. + } + } + + //Reset cycleguessing parameters. + CPU_IODelayRemoved = 0; + ticksDone = 0; + ticksScheduled = 0; + lastsleepDone = -1; + sleep1count = 0; + } else if (ticksAdded > 15) { + /* ticksAdded > 15 but ticksScheduled < 5, lower the cycles + but do not reset the scheduled/done ticks to take them into + account during the next auto cycle adjustment */ + CPU_CycleMax /= 3; + if (CPU_CycleMax < CPU_CYCLES_LOWER_LIMIT) + CPU_CycleMax = CPU_CYCLES_LOWER_LIMIT; + } //if (ticksScheduled >= 250 || ticksDone >= 250 || (ticksAdded > 15 && ticksScheduled >= 5) ) } void DOSBOX_SetLoop(LoopHandler * handler) { @@ -289,7 +360,7 @@ static void DOSBOX_RealInit(Section * sec) { } std::string mtype(section->Get_string("machine")); - svgaCard = SVGA_None; + svgaCard = SVGA_None; machine = MCH_VGA; int10.vesa_nolfb = false; int10.vesa_oldvbe = false; @@ -355,10 +426,10 @@ void DOSBOX_Init(void) { Pstring = secprop->Add_path("captures",Property::Changeable::Always,"capture"); Pstring->Set_help("Directory where things like wave, midi, screenshot get captured."); -#if C_DEBUG +#if C_DEBUG LOG_StartUp(); #endif - + secprop->AddInitFunction(&IO_Init);//done secprop->AddInitFunction(&PAGING_Init);//done secprop->AddInitFunction(&MEM_Init);//done @@ -367,9 +438,9 @@ void DOSBOX_Init(void) { Pint->SetMinMax(1,63); Pint->Set_help( "Amount of memory DOSBox has in megabytes.\n" - " This value is best left at its default to avoid problems with some games,\n" - " though few games might require a higher value.\n" - " There is generally no speed advantage when raising this value."); + "This value is best left at its default to avoid problems with some games,\n" + "though few games might require a higher value.\n" + "There is generally no speed advantage when raising this value."); secprop->AddInitFunction(&CALLBACK_Init); secprop->AddInitFunction(&PIC_Init);//done secprop->AddInitFunction(&PROGRAMS_Init); @@ -382,15 +453,17 @@ void DOSBOX_Init(void) { Pint->Set_help("How many frames DOSBox skips before drawing one."); Pbool = secprop->Add_bool("aspect",Property::Changeable::Always,false); - Pbool->Set_help("Do aspect correction, if your output method doesn't support scaling this can slow things down!."); + Pbool->Set_help("Do aspect correction, if your output method doesn't support scaling this can slow things down!"); Pmulti = secprop->Add_multi("scaler",Property::Changeable::Always," "); Pmulti->SetValue("normal2x"); Pmulti->Set_help("Scaler used to enlarge/enhance low resolution modes. If 'forced' is appended,\n" - "then the scaler will be used even if the result might not be desired."); + "then the scaler will be used even if the result might not be desired.\n" + "To fit a scaler in the resolution used at full screen may require a border or side bars,\n" + "to fill the screen entirely, depending on your hardware, a different scaler/fullresolution might work."); Pstring = Pmulti->GetSection()->Add_string("type",Property::Changeable::Always,"normal2x"); - const char *scalers[] = { + const char *scalers[] = { "none", "normal2x", "normal3x", #if RENDER_USE_ADVANCED_SCALERS>2 "advmame2x", "advmame3x", "advinterp2x", "advinterp3x", "hq2x", "hq3x", "2xsai", "super2xsai", "supereagle", @@ -440,7 +513,7 @@ void DOSBOX_Init(void) { Pstring->Set_values(cyclest); Pstring = Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::Always,""); - + Pint = secprop->Add_int("cycleup",Property::Changeable::Always,10); Pint->SetMinMax(1,1000000); Pint->Set_help("Amount of cycles to decrease/increase with keycombos.(CTRL-F11/CTRL-F12)"); @@ -448,7 +521,7 @@ void DOSBOX_Init(void) { Pint = secprop->Add_int("cycledown",Property::Changeable::Always,20); Pint->SetMinMax(1,1000000); Pint->Set_help("Setting it lower than 100 will be a percentage."); - + #if C_FPU secprop->AddInitFunction(&FPU_Init); #endif @@ -486,13 +559,13 @@ void DOSBOX_Init(void) { Pint->Set_values(blocksizes); Pint->Set_help("Mixer block size, larger blocks might help sound stuttering but sound will also be more lagged."); - Pint = secprop->Add_int("prebuffer",Property::Changeable::OnlyAtStart,20); + Pint = secprop->Add_int("prebuffer",Property::Changeable::OnlyAtStart,25); Pint->SetMinMax(0,100); Pint->Set_help("How many milliseconds of data to keep on top of the blocksize."); secprop=control->AddSection_prop("midi",&MIDI_Init,true);//done secprop->AddInitFunction(&MPU401_Init,true);//done - + const char* mputypes[] = { "intelligent", "uart", "none",0}; // FIXME: add some way to offer the actually available choices. const char *devices[] = { "default", "win32", "alsa", "oss", "coreaudio", "coremidi","none", 0}; @@ -505,18 +578,18 @@ void DOSBOX_Init(void) { Pstring->Set_help("Device that will receive the MIDI data from MPU-401."); Pstring = secprop->Add_string("midiconfig",Property::Changeable::WhenIdle,""); - Pstring->Set_help("Special configuration options for the device driver. This is usually the id of the device you want to use.\n" - " or in the case of coreaudio, you can specify a soundfont here.\n" - " When using a Roland MT-32 rev. 0 as midi output device, some games may require a delay in order to prevent 'buffer overflow' issues.\n" - " In that case, add 'delaysysex', for example: midiconfig=2 delaysysex\n" - " See the README/Manual for more details."); + Pstring->Set_help("Special configuration options for the device driver. This is usually the id or part of the name of the device you want to use (find the id/name with mixer/listmidi).\n" + "Or in the case of coreaudio, you can specify a soundfont here.\n" + "When using a Roland MT-32 rev. 0 as midi output device, some games may require a delay in order to prevent 'buffer overflow' issues.\n" + "In that case, add 'delaysysex', for example: midiconfig=2 delaysysex\n" + "See the README/Manual for more details."); #if C_DEBUG secprop=control->AddSection_prop("debug",&DEBUG_Init); #endif secprop=control->AddSection_prop("sblaster",&SBLASTER_Init,true);//done - + const char* sbtypes[] = { "sb1", "sb2", "sbpro1", "sbpro2", "sb16", "gb", "none", 0 }; Pstring = secprop->Add_string("sbtype",Property::Changeable::WhenIdle,"sb16"); Pstring->Set_values(sbtypes); @@ -541,12 +614,12 @@ void DOSBOX_Init(void) { Pbool = secprop->Add_bool("sbmixer",Property::Changeable::WhenIdle,true); Pbool->Set_help("Allow the soundblaster mixer to modify the DOSBox mixer."); - const char* oplmodes[]={ "auto", "cms", "opl2", "dualopl2", "opl3", "none", 0}; + const char* oplmodes[]={ "auto", "cms", "opl2", "dualopl2", "opl3", "opl3gold", "none", 0}; Pstring = secprop->Add_string("oplmode",Property::Changeable::WhenIdle,"auto"); Pstring->Set_values(oplmodes); Pstring->Set_help("Type of OPL emulation. On 'auto' the mode is determined by sblaster type. All OPL modes are Adlib-compatible, except for 'cms'."); - const char* oplemus[]={ "default", "compat", "fast", 0}; + const char* oplemus[]={ "default", "compat", "fast", "mame", 0}; Pstring = secprop->Add_string("oplemu",Property::Changeable::WhenIdle,"default"); Pstring->Set_values(oplemus); Pstring->Set_help("Provider for the OPL emulation. compat might provide better quality (see oplrate as well)."); @@ -561,7 +634,7 @@ void DOSBOX_Init(void) { secprop=control->AddSection_prop("gus",&GUS_Init,true); //done - Pbool = secprop->Add_bool("gus",Property::Changeable::WhenIdle,false); + Pbool = secprop->Add_bool("gus",Property::Changeable::WhenIdle,false); Pbool->Set_help("Enable the Gravis Ultrasound emulation."); #ifdef HW_RVL @@ -608,7 +681,7 @@ void DOSBOX_Init(void) { Pstring = secprop->Add_string("tandy",Property::Changeable::WhenIdle,"auto"); Pstring->Set_values(tandys); Pstring->Set_help("Enable Tandy Sound System emulation. For 'auto', emulation is present only if machine is set to 'tandy'."); - + #ifdef HW_RVL Pint = secprop->Add_int("tandyrate",Property::Changeable::WhenIdle,22050); #else @@ -618,14 +691,14 @@ void DOSBOX_Init(void) { Pint->Set_help("Sample rate of the Tandy 3-Voice generation."); secprop->AddInitFunction(&DISNEY_Init,true);//done - + Pbool = secprop->Add_bool("disney",Property::Changeable::WhenIdle,true); Pbool->Set_help("Enable Disney Sound Source emulation. (Covox Voice Master and Speech Thing compatible)."); secprop=control->AddSection_prop("joystick",&BIOS_Init,false);//done secprop->AddInitFunction(&INT10_Init); secprop->AddInitFunction(&MOUSE_Init); //Must be after int10 as it uses CurMode - secprop->AddInitFunction(&JOYSTICK_Init); + secprop->AddInitFunction(&JOYSTICK_Init,true); const char* joytypes[] = { "auto", "2axis", "4axis", "4axis_2", "fcs", "ch", "none",0}; Pstring = secprop->Add_string("joysticktype",Property::Changeable::WhenIdle,"auto"); Pstring->Set_values(joytypes); @@ -644,17 +717,25 @@ void DOSBOX_Init(void) { Pbool = secprop->Add_bool("autofire",Property::Changeable::WhenIdle,false); Pbool->Set_help("continuously fires as long as you keep the button pressed."); - + Pbool = secprop->Add_bool("swap34",Property::Changeable::WhenIdle,false); - Pbool->Set_help("swap the 3rd and the 4th axis. can be useful for certain joysticks."); + Pbool->Set_help("swap the 3rd and the 4th axis. Can be useful for certain joysticks."); Pbool = secprop->Add_bool("buttonwrap",Property::Changeable::WhenIdle,false); Pbool->Set_help("enable button wrapping at the number of emulated buttons."); + + Pbool = secprop->Add_bool("circularinput",Property::Changeable::WhenIdle,false); + Pbool->Set_help("enable translation of circular input to square output.\n" + "Try enabling this if your left analog stick can only move in a circle."); + + Pint = secprop->Add_int("deadzone",Property::Changeable::WhenIdle,10); + Pint->SetMinMax(0,100); + Pint->Set_help("the percentage of motion to ignore. 100 turns the stick into a digital one."); secprop=control->AddSection_prop("serial",&SERIAL_Init,true); const char* serials[] = { "dummy", "disabled", "modem", "nullmodem", "directserial",0 }; - + Pmulti_remain = secprop->Add_multiremain("serial1",Property::Changeable::WhenIdle," "); Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"dummy"); Pmulti_remain->SetValue("dummy"); diff --git a/src/fpu/fpu.cpp b/src/fpu/fpu.cpp index f7ac61d..aa8f062 100644 --- a/src/fpu/fpu.cpp +++ b/src/fpu/fpu.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/fpu/fpu_instructions.h b/src/fpu/fpu_instructions.h index a5f7e3a..9dc97c7 100644 --- a/src/fpu/fpu_instructions.h +++ b/src/fpu/fpu_instructions.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,11 +11,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifndef DOSBOX_FPU_H +#include "fpu.h" +#endif static void FPU_FINIT(void) { @@ -41,21 +44,54 @@ static void FPU_FNOP(void){ return; } -static void FPU_PUSH(double in){ +static void FPU_PREP_PUSH(void){ TOP = (TOP - 1) &7; - //actually check if empty +#if DB_FPU_STACK_CHECK_PUSH > DB_FPU_STACK_CHECK_NONE + if (GCC_UNLIKELY(fpu.tags[TOP] != TAG_Empty)) { +#if DB_FPU_STACK_CHECK_PUSH == DB_FPU_STACK_CHECK_EXIT + E_Exit("FPU stack overflow"); +#else + if (fpu.cw&1) { // Masked ? + fpu.sw &= 0x1; //Invalid Operation + fpu.sw &= 0x40; //Stack Fault + FPU_SET_C1(1); //Register is used. + //No need to set 0x80 as the exception is masked. + LOG(LOG_FPU,LOG_ERROR)("Masked stack overflow encountered!"); + } else { + E_Exit("FPU stack overflow"); //Exit as this is bad + } +#endif + } +#endif fpu.tags[TOP] = TAG_Valid; +} + +static void FPU_PUSH(double in){ + FPU_PREP_PUSH(); fpu.regs[TOP].d = in; // LOG(LOG_FPU,LOG_ERROR)("Pushed at %d %g to the stack",newtop,in); return; } -static void FPU_PREP_PUSH(void){ - TOP = (TOP - 1) &7; - fpu.tags[TOP] = TAG_Valid; -} static void FPU_FPOP(void){ +#if DB_FPU_STACK_CHECK_POP > DB_FPU_STACK_CHECK_NONE + if (GCC_UNLIKELY(fpu.tags[TOP] != TAG_Empty)) { +#if DB_FPU_STACK_CHECK_POP == DB_FPU_STACK_CHECK_EXIT + E_Exit("FPU stack underflow"); +#else + if (fpu.cw&1) { // Masked ? + fpu.sw &= 0x1; //Invalid Operation + fpu.sw &= 0x40; //Stack Fault + FPU_SET_C1(0); //Register is free. + //No need to set 0x80 as the exception is masked. + LOG(LOG_FPU,LOG_ERROR)("Masked stack underflow encountered!"); + } else { + LOG_MSG("Unmasked Stack underflow!"); //Also log in release mode + } +#endif + } +#endif fpu.tags[TOP]=TAG_Empty; //maybe set zero in it as well TOP = ((TOP+1)&7); diff --git a/src/fpu/fpu_instructions_x86.h b/src/fpu/fpu_instructions_x86.h index 7a7402a..31e51fc 100644 --- a/src/fpu/fpu_instructions_x86.h +++ b/src/fpu/fpu_instructions_x86.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,12 +11,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - +#ifndef DOSBOX_FPU_H +#include "fpu.h" +#endif // #define WEAK_EXCEPTIONS @@ -312,23 +314,7 @@ #endif // handles fdiv,fdivr -#ifdef WEAK_EXCEPTIONS -#define FPUD_ARITH3(op) \ - Bit16u save_cw; \ - __asm { \ - __asm fnstcw save_cw \ - __asm mov eax, op1 \ - __asm shl eax, 4 \ - __asm fldcw fpu.cw_mask_all \ - __asm mov ebx, op2 \ - __asm shl ebx, 4 \ - __asm fld TBYTE PTR fpu.p_regs[eax].m1 \ - __asm fld TBYTE PTR fpu.p_regs[ebx].m1 \ - __asm op st(1), st(0) \ - __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \ - __asm fldcw save_cw \ - } -#else +// (This is identical to FPUD_ARITH1 but without a WEAK_EXCEPTIONS variant) #define FPUD_ARITH3(op) \ Bit16u new_sw,save_cw; \ __asm { \ @@ -347,24 +333,9 @@ __asm fldcw save_cw \ } \ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); -#endif // handles fdiv,fdivr -#ifdef WEAK_EXCEPTIONS -#define FPUD_ARITH3_EA(op) \ - Bit16u save_cw; \ - __asm { \ - __asm fnstcw save_cw \ - __asm mov eax, op1 \ - __asm fldcw fpu.cw_mask_all \ - __asm shl eax, 4 \ - __asm fld TBYTE PTR fpu.p_regs[eax].m1 \ - __asm fxch \ - __asm op st(1), st(0) \ - __asm fstp TBYTE PTR fpu.p_regs[eax].m1 \ - __asm fldcw save_cw \ - } -#else +// (This is identical to FPUD_ARITH1_EA but without a WEAK_EXCEPTIONS variant) #define FPUD_ARITH3_EA(op) \ Bit16u new_sw,save_cw; \ __asm { \ @@ -381,10 +352,9 @@ __asm fldcw save_cw \ } \ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); -#endif // handles fprem,fprem1,fscale -#define FPUD_REMINDER(op) \ +#define FPUD_REMAINDER(op) \ Bit16u new_sw; \ __asm { \ __asm mov eax, TOP \ @@ -532,6 +502,8 @@ #else +// !defined _MSC_VER + #ifdef WEAK_EXCEPTIONS #define clx #else @@ -541,55 +513,44 @@ #ifdef WEAK_EXCEPTIONS #define FPUD_LOAD(op,szI,szA) \ __asm__ volatile ( \ - "movl $128, %%eax \n" \ - "shl $4, %0 \n" \ - #op #szA " (%1, %%eax) \n" \ - "fstpt (%1, %0) " \ - : \ - : "r" (store_to), "r" (fpu.p_regs) \ - : "eax", "memory" \ + #op #szA " %1 \n" \ + "fstpt %0 " \ + : "=m" (fpu.p_regs[store_to]) \ + : "m" (fpu.p_regs[8]) \ ); #else #define FPUD_LOAD(op,szI,szA) \ Bit16u new_sw; \ __asm__ volatile ( \ - "movl $8, %%eax \n" \ - "shl $4, %%eax \n" \ - "shl $4, %1 \n" \ "fclex \n" \ - #op #szA " (%2, %%eax) \n" \ + #op #szA " %2 \n" \ "fnstsw %0 \n" \ - "fstpt (%2, %1) " \ - : "=m" (new_sw) \ - : "r" (store_to), "r" (fpu.p_regs) \ - : "eax", "memory" \ + "fstpt %1 " \ + : "=&am" (new_sw), "=m" (fpu.p_regs[store_to]) \ + : "m" (fpu.p_regs[8]) \ ); \ - fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); + fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); #endif #ifdef WEAK_EXCEPTIONS #define FPUD_LOAD_EA(op,szI,szA) \ __asm__ volatile ( \ - "movl $128, %%eax \n" \ - #op #szA " (%0, %%eax) \n" \ + #op #szA " %0 \n" \ : \ - : "r" (fpu.p_regs) \ - : "eax", "memory" \ + : "m" (fpu.p_regs[8]) \ ); #else #define FPUD_LOAD_EA(op,szI,szA) \ Bit16u new_sw; \ __asm__ volatile ( \ - "movl $8, %%eax \n" \ - "shl $4, %%eax \n" \ "fclex \n" \ - #op #szA " (%1, %%eax) \n" \ + #op #szA " %1 \n" \ "fnstsw %0 \n" \ - : "=m" (new_sw) \ - : "r" (fpu.p_regs) \ - : "eax", "memory" \ + : "=&am" (new_sw) \ + : "m" (fpu.p_regs[8]) \ + : \ ); \ - fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); + fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); #endif #ifdef WEAK_EXCEPTIONS @@ -597,15 +558,12 @@ Bit16u save_cw; \ __asm__ volatile ( \ "fnstcw %0 \n" \ - "shll $4, %1 \n" \ "fldcw %3 \n" \ - "movl $128, %%eax \n" \ - "fldt (%2, %1) \n" \ - #op #szA " (%2, %%eax) \n" \ + "fldt %2 \n" \ + #op #szA " %1 \n" \ "fldcw %0 " \ - : "=m" (save_cw) \ - : "r" (TOP), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "eax", "memory" \ + : "=m" (save_cw), "=m" (fpu.p_regs[8]) \ + : "m" (fpu.p_regs[TOP]), "m" (fpu.cw_mask_all) \ ); #else #define FPUD_STORE(op,szI,szA) \ @@ -613,18 +571,14 @@ __asm__ volatile ( \ "fnstcw %1 \n" \ "fldcw %4 \n" \ - "shll $4, %2 \n" \ - "movl $8, %%eax \n" \ - "shl $4, %%eax \n" \ - "fldt (%3, %2) \n" \ - clx" \n" \ - #op #szA " (%3, %%eax) \n" \ + "fldt %3 \n" \ + "fclex \n" \ + #op #szA " %2 \n" \ "fnstsw %0 \n" \ "fldcw %1 " \ - : "=m" (new_sw), "=m" (save_cw) \ - : "r" (TOP), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "eax", "memory" \ - ); \ + : "=&am" (new_sw), "=m" (save_cw), "=m" (fpu.p_regs[8]) \ + : "m" (fpu.p_regs[TOP]), "m" (fpu.cw_mask_all) \ + ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); #endif @@ -632,15 +586,12 @@ #define FPUD_TRIG(op) \ Bit16u new_sw; \ __asm__ volatile ( \ - "shll $4, %1 \n" \ - "fldt (%2, %1) \n" \ + "fldt %1 \n" \ clx" \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%2, %1) " \ - : "=m" (new_sw) \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "memory" \ + "fstpt %1 " \ + : "=&am" (new_sw), "+m" (fpu.p_regs[TOP]) \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); @@ -648,24 +599,20 @@ #define FPUD_SINCOS() \ Bit16u new_sw; \ __asm__ volatile ( \ - "movl %1, %%eax \n" \ - "shll $4, %1 \n" \ - "decl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "fldt (%2, %1) \n" \ + "fldt %1 \n" \ clx" \n" \ "fsincos \n" \ "fnstsw %0 \n" \ - "fstpt (%2, %%eax) \n" \ + "fstpt %2 \n" \ "movw %0, %%ax \n" \ "sahf \n" \ - "jp argument_too_large1 \n" \ - "fstpt (%2, %1) \n" \ - "argument_too_large1: " \ - : "=m" (new_sw) \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "cc", "memory" \ + "jp 1f \n" \ + "fstpt %1 \n" \ + "1: " \ + : "=m" (new_sw), "+m" (fpu.p_regs[TOP]), \ + "=m" (fpu.p_regs[(TOP-1)&7]) \ + : \ + : "ax", "cc" \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \ if ((new_sw&0x0400)==0) FPU_PREP_PUSH(); @@ -674,24 +621,20 @@ #define FPUD_PTAN() \ Bit16u new_sw; \ __asm__ volatile ( \ - "movl %1, %%eax \n" \ - "shll $4, %1 \n" \ - "decl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "fldt (%2, %1) \n" \ + "fldt %1 \n" \ clx" \n" \ "fptan \n" \ "fnstsw %0 \n" \ - "fstpt (%2, %%eax) \n" \ + "fstpt %2 \n" \ "movw %0, %%ax \n" \ "sahf \n" \ - "jp argument_too_large2 \n" \ - "fstpt (%2, %1) \n" \ - "argument_too_large2: " \ - : "=m" (new_sw) \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "cc", "memory" \ + "jp 1f \n" \ + "fstpt %1 \n" \ + "1: " \ + : "=m" (new_sw), "+m" (fpu.p_regs[TOP]), \ + "=m" (fpu.p_regs[(TOP-1)&7]) \ + : \ + : "ax", "cc" \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \ if ((new_sw&0x0400)==0) FPU_PREP_PUSH(); @@ -700,40 +643,27 @@ #ifdef WEAK_EXCEPTIONS #define FPUD_XTRACT \ __asm__ volatile ( \ - "movl %0, %%eax \n" \ - "shll $4, %0 \n" \ - "decl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "fldt (%1, %0) \n" \ + "fldt %0 \n" \ "fxtract \n" \ - "fstpt (%1, %%eax) \n" \ - "fstpt (%1, %0) " \ - : \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "memory" \ + "fstpt %1 \n" \ + "fstpt %0 " \ + : "+m" (fpu.p_regs[TOP]), "=m" (fpu.p_regs[(TOP-1)&7]) \ ); \ FPU_PREP_PUSH(); #else #define FPUD_XTRACT \ Bit16u new_sw; \ __asm__ volatile ( \ - "movl %1, %%eax \n" \ - "shll $4, %1 \n" \ - "decl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "fldt (%2, %1) \n" \ + "fldt %1 \n" \ "fclex \n" \ "fxtract \n" \ "fnstsw %0 \n" \ - "fstpt (%2, %%eax) \n" \ - "fstpt (%2, %1) " \ - : "=m" (new_sw) \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "memory" \ + "fstpt %2 \n" \ + "fstpt %1 " \ + : "=&am" (new_sw), "+m" (fpu.p_regs[TOP]), \ + "=m" (fpu.p_regs[(TOP-1)&7]) \ ); \ - fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); \ + fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \ FPU_PREP_PUSH(); #endif @@ -743,36 +673,30 @@ Bit16u save_cw; \ __asm__ volatile ( \ "fnstcw %0 \n" \ - "fldcw %4 \n" \ - "shll $4, %2 \n" \ - "shll $4, %1 \n" \ - "fldt (%3, %2) \n" \ - "fldt (%3, %1) \n" \ + "fldcw %3 \n" \ + "fldt %2 \n" \ + "fldt %1 \n" \ #op" \n" \ - "fstpt (%3, %1) \n" \ + "fstpt %1 \n" \ "fldcw %0 " \ - : "=m" (save_cw) \ - : "r" (op1), "r" (op2), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ + : "=m" (save_cw), "+m" (fpu.p_regs[op1]) \ + : "m" (fpu.p_regs[op2]), "m" (fpu.cw_mask_all) \ ); #else #define FPUD_ARITH1(op) \ Bit16u new_sw,save_cw; \ __asm__ volatile ( \ "fnstcw %1 \n" \ - "fldcw %5 \n" \ - "shll $4, %3 \n" \ - "shll $4, %2 \n" \ - "fldt (%4, %3) \n" \ - "fldt (%4, %2) \n" \ - clx" \n" \ + "fldcw %4 \n" \ + "fldt %3 \n" \ + "fldt %2 \n" \ + "fclex \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%4, %2) \n" \ + "fstpt %2 \n" \ "fldcw %1 " \ - : "=m" (new_sw), "=m" (save_cw) \ - : "r" (op1), "r" (op2), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ + : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[op1]) \ + : "m" (fpu.p_regs[op2]), "m" (fpu.cw_mask_all) \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); #endif @@ -783,32 +707,28 @@ Bit16u save_cw; \ __asm__ volatile ( \ "fnstcw %0 \n" \ - "fldcw %3 \n" \ - "shll $4, %1 \n" \ - "fldt (%2, %1) \n" \ + "fldcw %2 \n" \ + "fldt %1 \n" \ #op" \n" \ - "fstpt (%2, %1) \n" \ + "fstpt %1 \n" \ "fldcw %0 " \ - : "=m" (save_cw) \ - : "r" (op1), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ + : "=m" (save_cw), "+m" (fpu.p_regs[op1]) \ + : "m" (fpu.cw_mask_all) \ ); #else #define FPUD_ARITH1_EA(op) \ Bit16u new_sw,save_cw; \ __asm__ volatile ( \ "fnstcw %1 \n" \ - "fldcw %4 \n" \ - "shll $4, %2 \n" \ - "fldt (%3, %2) \n" \ - clx" \n" \ + "fldcw %3 \n" \ + "fldt %2 \n" \ + "fclex \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%3, %2) \n" \ + "fstpt %2 \n" \ "fldcw %1 " \ - : "=m" (new_sw), "=m" (save_cw) \ - : "r" (op1), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ + : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[op1]) \ + : "m" (fpu.cw_mask_all) \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); #endif @@ -819,131 +739,82 @@ Bit16u save_cw; \ __asm__ volatile ( \ "fnstcw %0 \n" \ - "fldcw %3 \n" \ - "shll $4, %1 \n" \ - "fldt (%2, %1) \n" \ + "fldcw %2 \n" \ + "fldt %1 \n" \ #op" \n" \ - "fstpt (%2, %1) \n" \ + "fstpt %1 \n" \ "fldcw %0 " \ - : "=m" (save_cw) \ - : "r" (TOP), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ + : "=m" (save_cw), "+m" (fpu.p_regs[TOP]) \ + : "m" (fpu.cw_mask_all) \ ); #else #define FPUD_ARITH2(op) \ Bit16u new_sw,save_cw; \ __asm__ volatile ( \ "fnstcw %1 \n" \ - "fldcw %4 \n" \ - "shll $4, %2 \n" \ - "fldt (%3, %2) \n" \ - clx" \n" \ + "fldcw %3 \n" \ + "fldt %2 \n" \ + "fclex \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%3, %2) \n" \ + "fstpt %2 \n" \ "fldcw %1 " \ - : "=m" (new_sw), "=m" (save_cw) \ - : "r" (TOP), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ + : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[TOP]) \ + : "m" (fpu.cw_mask_all) \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); #endif // handles fdiv,fdivr -#ifdef WEAK_EXCEPTIONS -#define FPUD_ARITH3(op) \ - Bit16u save_cw; \ - __asm__ volatile ( \ - "fnstcw %0 \n" \ - "fldcw %4 \n" \ - "shll $4, %2 \n" \ - "shll $4, %1 \n" \ - "fldt (%3, %2) \n" \ - "fldt (%3, %1) \n" \ - #op" \n" \ - "fstpt (%3, %1) \n" \ - "fldcw %0 " \ - : "=m" (save_cw) \ - : "r" (op1), "r" (op2), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ - ); -#else +// (This is identical to FPUD_ARITH1 but without a WEAK_EXCEPTIONS variant) #define FPUD_ARITH3(op) \ Bit16u new_sw,save_cw; \ __asm__ volatile ( \ "fnstcw %1 \n" \ - "fldcw %5 \n" \ - "shll $4, %3 \n" \ - "shll $4, %2 \n" \ - "fldt (%4, %3) \n" \ - "fldt (%4, %2) \n" \ - "fclex \n" \ + "fldcw %4 \n" \ + "fldt %3 \n" \ + "fldt %2 \n" \ + "fclex \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%4, %2) \n" \ + "fstpt %2 \n" \ "fldcw %1 " \ - : "=m" (new_sw), "=m" (save_cw) \ - : "r" (op1), "r" (op2), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ + : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[op1]) \ + : "m" (fpu.p_regs[op2]), "m" (fpu.cw_mask_all) \ ); \ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); -#endif // handles fdiv,fdivr -#ifdef WEAK_EXCEPTIONS -#define FPUD_ARITH3_EA(op) \ - Bit16u save_cw; \ - __asm__ volatile ( \ - "fnstcw %0 \n" \ - "fldcw %3 \n" \ - "shll $4, %1 \n" \ - "fldt (%2, %1) \n" \ - #op" \n" \ - "fstpt (%2, %1) \n" \ - "fldcw %0 " \ - : "=m" (save_cw) \ - : "r" (op1), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ - ); -#else +// (This is identical to FPUD_ARITH1_EA but without a WEAK_EXCEPTIONS variant) #define FPUD_ARITH3_EA(op) \ Bit16u new_sw,save_cw; \ __asm__ volatile ( \ "fnstcw %1 \n" \ - "fldcw %4 \n" \ - "shll $4, %2 \n" \ - "fldt (%3, %2) \n" \ - "fclex \n" \ + "fldcw %3 \n" \ + "fldt %2 \n" \ + "fclex \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%3, %2) \n" \ + "fstpt %2 \n" \ "fldcw %1 " \ - : "=m" (new_sw), "=m" (save_cw) \ - : "r" (op1), "r" (fpu.p_regs), "m" (fpu.cw_mask_all) \ - : "memory" \ + : "=&am" (new_sw), "=m" (save_cw), "+m" (fpu.p_regs[op1]) \ + : "m" (fpu.cw_mask_all) \ ); \ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); -#endif // handles fprem,fprem1,fscale -#define FPUD_REMINDER(op) \ +#define FPUD_REMAINDER(op) \ Bit16u new_sw; \ __asm__ volatile ( \ - "movl %1, %%eax \n" \ - "incl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "shll $4, %1 \n" \ - "fldt (%2, %%eax) \n" \ - "fldt (%2, %1) \n" \ + "fldt %2 \n" \ + "fldt %1 \n" \ "fclex \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%2, %1) \n" \ + "fstpt %1 \n" \ "fstp %%st(0) " \ - : "=m" (new_sw) \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "memory" \ + : "=&am" (new_sw), "+m" (fpu.p_regs[TOP]) \ + : "m" (fpu.p_regs[(TOP+1)&7]) \ ); \ fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); @@ -951,16 +822,13 @@ #define FPUD_COMPARE(op) \ Bit16u new_sw; \ __asm__ volatile ( \ - "shll $4, %2 \n" \ - "shll $4, %1 \n" \ - "fldt (%3, %2) \n" \ - "fldt (%3, %1) \n" \ + "fldt %2 \n" \ + "fldt %1 \n" \ clx" \n" \ #op" \n" \ "fnstsw %0 " \ - : "=m" (new_sw) \ - : "r" (op1), "r" (op2), "r" (fpu.p_regs) \ - : "memory" \ + : "=&am" (new_sw) \ + : "m" (fpu.p_regs[op1]), "m" (fpu.p_regs[op2]) \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); @@ -968,14 +836,12 @@ #define FPUD_COMPARE_EA(op) \ Bit16u new_sw; \ __asm__ volatile ( \ - "shll $4, %1 \n" \ - "fldt (%2, %1) \n" \ + "fldt %1 \n" \ clx" \n" \ #op" \n" \ "fnstsw %0 " \ - : "=m" (new_sw) \ - : "r" (op1), "r" (fpu.p_regs) \ - : "memory" \ + : "=&am" (new_sw) \ + : "m" (fpu.p_regs[op1]) \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); @@ -983,15 +849,13 @@ #define FPUD_EXAMINE(op) \ Bit16u new_sw; \ __asm__ volatile ( \ - "shll $4, %1 \n" \ - "fldt (%2, %1) \n" \ + "fldt %1 \n" \ clx" \n" \ #op" \n" \ "fnstsw %0 \n" \ "fstp %%st(0) " \ - : "=m" (new_sw) \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "memory" \ + : "=&am" (new_sw) \ + : "m" (fpu.p_regs[TOP]) \ ); \ fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); @@ -999,40 +863,28 @@ #ifdef WEAK_EXCEPTIONS #define FPUD_WITH_POP(op) \ __asm__ volatile ( \ - "movl %0, %%eax \n" \ - "incl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "shll $4, %0 \n" \ - "fldt (%1, %%eax) \n" \ - "fldt (%1, %0) \n" \ + "fldt %0 \n" \ + "fldt %1 \n" \ #op" \n" \ - "fstpt (%1, %%eax) \n" \ - : \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "memory" \ + "fstpt %0 \n" \ + : "+m" (fpu.p_regs[(TOP+1)&7]) \ + : "m" (fpu.p_regs[TOP]) \ ); \ FPU_FPOP(); #else #define FPUD_WITH_POP(op) \ Bit16u new_sw; \ __asm__ volatile ( \ - "movl %1, %%eax \n" \ - "incl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "shll $4, %1 \n" \ - "fldt (%2, %%eax) \n" \ - "fldt (%2, %1) \n" \ + "fldt %1 \n" \ + "fldt %2 \n" \ "fclex \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%2, %%eax) \n" \ - : "=m" (new_sw) \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "memory" \ + "fstpt %1 \n" \ + : "=&am" (new_sw), "+m" (fpu.p_regs[(TOP+1)&7]) \ + : "m" (fpu.p_regs[TOP]) \ ); \ - fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); \ + fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \ FPU_FPOP(); #endif @@ -1040,40 +892,28 @@ #ifdef WEAK_EXCEPTIONS #define FPUD_FYL2X(op) \ __asm__ volatile ( \ - "movl %0, %%eax \n" \ - "incl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "shll $4, %0 \n" \ - "fldt (%1, %%eax) \n" \ - "fldt (%1, %0) \n" \ + "fldt %0 \n" \ + "fldt %1 \n" \ #op" \n" \ - "fstpt (%1, %%eax) \n" \ - : \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "memory" \ + "fstpt %0 \n" \ + : "+m" (fpu.p_regs[(TOP+1)&7]) \ + : "m" (fpu.p_regs[TOP]) \ ); \ FPU_FPOP(); #else #define FPUD_FYL2X(op) \ Bit16u new_sw; \ __asm__ volatile ( \ - "movl %1, %%eax \n" \ - "incl %%eax \n" \ - "andl $7, %%eax \n" \ - "shll $4, %%eax \n" \ - "shll $4, %1 \n" \ - "fldt (%2, %%eax) \n" \ - "fldt (%2, %1) \n" \ + "fldt %1 \n" \ + "fldt %2 \n" \ "fclex \n" \ #op" \n" \ "fnstsw %0 \n" \ - "fstpt (%2, %%eax) \n" \ - : "=m" (new_sw) \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "eax", "memory" \ + "fstpt %1 \n" \ + : "=&am" (new_sw), "+m" (fpu.p_regs[(TOP+1)&7]) \ + : "m" (fpu.p_regs[TOP]) \ ); \ - fpu.sw=(new_sw&0xffbf)|(fpu.sw&0x80ff); \ + fpu.sw=(new_sw&exc_mask)|(fpu.sw&0x80ff); \ FPU_FPOP(); #endif @@ -1081,13 +921,10 @@ #define FPUD_LOAD_CONST(op) \ FPU_PREP_PUSH(); \ __asm__ volatile ( \ - "shll $4, %0 \n" \ clx" \n" \ #op" \n" \ - "fstpt (%1, %0) \n" \ - : \ - : "r" (TOP), "r" (fpu.p_regs) \ - : "memory" \ + "fstpt %0 \n" \ + : "=m" (fpu.p_regs[TOP]) \ ); #endif @@ -1122,11 +959,45 @@ static void FPU_FNOP(void){ static void FPU_PREP_PUSH(void){ TOP = (TOP - 1) &7; - fpu.tags[TOP]=TAG_Valid; +#if DB_FPU_STACK_CHECK_PUSH > DB_FPU_STACK_CHECK_NONE + if (GCC_UNLIKELY(fpu.tags[TOP] != TAG_Empty)) { +#if DB_FPU_STACK_CHECK_PUSH == DB_FPU_STACK_CHECK_EXIT + E_Exit("FPU stack overflow"); +#else + if (fpu.cw&1) { // Masked ? + fpu.sw &= 0x1; //Invalid Operation + fpu.sw &= 0x40; //Stack Fault + FPU_SET_C1(1); //Register is used. + //No need to set 0x80 as the exception is masked. + LOG(LOG_FPU,LOG_ERROR)("Masked stack overflow encountered!"); + } else { + E_Exit("FPU stack overflow"); //Exit as this is bad + } +#endif + } +#endif + fpu.tags[TOP] = TAG_Valid; } static void FPU_FPOP(void){ - fpu.tags[TOP]=TAG_Empty; +#if DB_FPU_STACK_CHECK_POP > DB_FPU_STACK_CHECK_NONE + if (GCC_UNLIKELY(fpu.tags[TOP] == TAG_Empty)) { +#if DB_FPU_STACK_CHECK_POP == DB_FPU_STACK_CHECK_EXIT + E_Exit("FPU stack underflow"); +#else + if (fpu.cw&1) { // Masked ? + fpu.sw &= 0x1; //Invalid Operation + fpu.sw &= 0x40; //Stack Fault + FPU_SET_C1(0); //Register is free. + //No need to set 0x80 as the exception is masked. + LOG(LOG_FPU,LOG_ERROR)("Masked stack underflow encountered!"); + } else { + LOG_MSG("Unmasked Stack underflow!"); + } +#endif + } +#endif + fpu.tags[TOP] = TAG_Empty; TOP = ((TOP+1)&7); } @@ -1161,12 +1032,12 @@ static void FPU_FLD_F80(PhysPt addr) { static void FPU_FLD_I16(PhysPt addr,Bitu store_to) { fpu.p_regs[8].m1 = (Bit32u)mem_readw(addr); - FPUD_LOAD(fild,WORD,) + FPUD_LOAD(fild,WORD,s) } static void FPU_FLD_I16_EA(PhysPt addr) { fpu.p_regs[8].m1 = (Bit32u)mem_readw(addr); - FPUD_LOAD_EA(fild,WORD,) + FPUD_LOAD_EA(fild,WORD,s) } static void FPU_FLD_I32(PhysPt addr,Bitu store_to) { @@ -1211,7 +1082,7 @@ static void FPU_FST_F80(PhysPt addr) { } static void FPU_FST_I16(PhysPt addr) { - FPUD_STORE(fistp,WORD,) + FPUD_STORE(fistp,WORD,s) mem_writew(addr,(Bit16u)fpu.p_regs[8].m1); } @@ -1353,11 +1224,11 @@ static void FPU_FRNDINT(void){ } static void FPU_FPREM(void){ - FPUD_REMINDER(fprem) + FPUD_REMAINDER(fprem) } static void FPU_FPREM1(void){ - FPUD_REMINDER(fprem1) + FPUD_REMAINDER(fprem1) } static void FPU_FXAM(void){ @@ -1382,7 +1253,7 @@ static void FPU_FYL2XP1(void){ } static void FPU_FSCALE(void){ - FPUD_REMINDER(fscale) + FPUD_REMAINDER(fscale) } diff --git a/src/gui/dosbox_logo.h b/src/gui/dosbox_logo.h index 5d99a58..73925ff 100644 --- a/src/gui/dosbox_logo.h +++ b/src/gui/dosbox_logo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/gui/midi.cpp b/src/gui/midi.cpp index 252cc7d..28b65f4 100644 --- a/src/gui/midi.cpp +++ b/src/gui/midi.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include @@ -25,6 +25,7 @@ #include "SDL.h" #include "dosbox.h" +#include "midi.h" #include "cross.h" #include "support.h" #include "setup.h" @@ -33,7 +34,6 @@ #include "hardware.h" #include "timer.h" -#define SYSEX_SIZE 1024 #define RAWBUF 1024 Bit8u MIDI_evt_len[256] = { @@ -59,33 +59,27 @@ Bit8u MIDI_evt_len[256] = { 0,2,3,2, 0,0,1,0, 1,0,1,1, 1,0,1,0 // 0xf0 }; -class MidiHandler; +MidiHandler * handler_list = 0; -MidiHandler * handler_list=0; - -class MidiHandler { -public: - MidiHandler() { - next=handler_list; - handler_list=this; - }; - virtual bool Open(const char * /*conf*/) { return true; }; - virtual void Close(void) {}; - virtual void PlayMsg(Bit8u * /*msg*/) {}; - virtual void PlaySysex(Bit8u * /*sysex*/,Bitu /*len*/) {}; - virtual const char * GetName(void) { return "none"; }; - virtual ~MidiHandler() { }; - MidiHandler * next; +MidiHandler::MidiHandler(){ + next = handler_list; + handler_list = this; }; MidiHandler Midi_none; -/* Include different midi drivers, lowest ones get checked first for default */ +/* Include different midi drivers, lowest ones get checked first for default. + Each header provides an independent midi interface. */ #if defined(MACOSX) +#if defined(C_SUPPORTS_COREMIDI) #include "midi_coremidi.h" +#endif + +#if defined(C_SUPPORTS_COREAUDIO) #include "midi_coreaudio.h" +#endif #elif defined (WIN32) @@ -103,21 +97,7 @@ MidiHandler Midi_none; #endif -static struct { - Bitu status; - Bitu cmd_len; - Bitu cmd_pos; - Bit8u cmd_buf[8]; - Bit8u rt_buf[8]; - struct { - Bit8u buf[SYSEX_SIZE]; - Bitu used; - Bitu delay; - Bit32u start; - } sysex; - bool available; - MidiHandler * handler; -} midi; +DB_Midi midi; void MIDI_RawOutByte(Bit8u data) { if (midi.sysex.start) { @@ -130,10 +110,10 @@ void MIDI_RawOutByte(Bit8u data) { midi.rt_buf[0]=data; midi.handler->PlayMsg(midi.rt_buf); return; - } + } /* Test for a active sysex tranfer */ if (midi.status==0xf0) { - if (!(data&0x80)) { + if (!(data&0x80)) { if (midi.sysex.used<(SYSEX_SIZE-1)) midi.sysex.buf[midi.sysex.used++] = data; return; } else { @@ -156,7 +136,7 @@ void MIDI_RawOutByte(Bit8u data) { } } - LOG(LOG_ALL,LOG_NORMAL)("Sysex message size %d",midi.sysex.used); + LOG(LOG_ALL,LOG_NORMAL)("Sysex message size %d", static_cast(midi.sysex.used)); if (CaptureState & CAPTURE_MIDI) { CAPTURE_AddMidi( true, midi.sysex.used-1, &midi.sysex.buf[1]); } @@ -201,9 +181,9 @@ public: if (fullconf.find("delaysysex") != std::string::npos) { midi.sysex.start = GetTicks(); fullconf.erase(fullconf.find("delaysysex")); - LOG_MSG("MIDI:Using delayed SysEx processing"); + LOG_MSG("MIDI: Using delayed SysEx processing"); } - std::remove(fullconf.begin(), fullconf.end(), ' '); + trim(fullconf); const char * conf = fullconf.c_str(); midi.status=0x00; midi.cmd_pos=0; @@ -213,24 +193,24 @@ public: while (handler) { if (!strcasecmp(dev,handler->GetName())) { if (!handler->Open(conf)) { - LOG_MSG("MIDI:Can't open device:%s with config:%s.",dev,conf); + LOG_MSG("MIDI: Can't open device:%s with config:%s.",dev,conf); goto getdefault; } midi.handler=handler; - midi.available=true; - LOG_MSG("MIDI:Opened device:%s",handler->GetName()); + midi.available=true; + LOG_MSG("MIDI: Opened device:%s",handler->GetName()); return; } handler=handler->next; } - LOG_MSG("MIDI:Can't find device:%s, finding default handler.",dev); -getdefault: + LOG_MSG("MIDI: Can't find device:%s, finding default handler.",dev); +getdefault: handler=handler_list; while (handler) { if (handler->Open(conf)) { - midi.available=true; + midi.available=true; midi.handler=handler; - LOG_MSG("MIDI:Opened device:%s",handler->GetName()); + LOG_MSG("MIDI: Opened device:%s",handler->GetName()); return; } handler=handler->next; diff --git a/src/gui/midi_alsa.h b/src/gui/midi_alsa.h index 6e11afa..9bf4b4f 100644 --- a/src/gui/midi_alsa.h +++ b/src/gui/midi_alsa.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -94,6 +94,10 @@ public: snd_seq_ev_set_noteon(&ev, chanID, msg[1], msg[2]); send_event(1); break; + case 0xA0: + snd_seq_ev_set_keypress(&ev, chanID, msg[1], msg[2]); + send_event(1); + break; case 0xB0: snd_seq_ev_set_controller(&ev, chanID, msg[1], msg[2]); send_event(1); @@ -113,7 +117,8 @@ public: } break; default: - LOG(LOG_MISC,LOG_WARN)("ALSA:Unknown Command: %08lx", (long)msg); + //Maybe filter out FC as it leads for at least one user to crash, but the entire midi stream has not yet been checked. + LOG(LOG_MISC,LOG_WARN)("ALSA:Unknown Command: %02X %02X %02X", msg[0],msg[1],msg[2]); send_event(1); break; } diff --git a/src/gui/midi_coreaudio.h b/src/gui/midi_coreaudio.h index ee6c752..95d3902 100644 --- a/src/gui/midi_coreaudio.h +++ b/src/gui/midi_coreaudio.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -28,6 +28,31 @@ do { \ goto bail; \ } while (false) +// With the release of Mac OS X 10.5 in October 2007, Apple deprecated the +// AUGraphNewNode & AUGraphGetNodeInfo APIs in favor of the new AUGraphAddNode & +// AUGraphNodeInfo APIs. While it is easy to switch to those, it breaks +// compatibility with all pre-10.5 systems. +// +// Since 10.5 was the last system to support PowerPC, we use the old, deprecated +// APIs on PowerPC based systems by default. On all other systems (such as Mac +// OS X running on Intel hardware, or iOS running on ARM), we use the new API by +// default. +// +// This leaves Mac OS X 10.4 running on x86 processors as the only system +// combination that this code will not support by default. It seems quite +// reasonable to assume that anybody with an Intel system has since then moved +// on to a newer Mac OS X release. But if for some reason you absolutely need to +// build an x86 version of this code using the old, deprecated API, you can +// simply do so by manually enable the USE_DEPRECATED_COREAUDIO_API switch (e.g. +// by adding setting it suitably in CPPFLAGS). +#if !defined(USE_DEPRECATED_COREAUDIO_API) +# if TARGET_CPU_PPC || TARGET_CPU_PPC64 +# define USE_DEPRECATED_COREAUDIO_API 1 +# else +# define USE_DEPRECATED_COREAUDIO_API 0 +# endif +#endif + class MidiHandler_coreaudio : public MidiHandler { private: AUGraph m_auGraph; @@ -46,7 +71,12 @@ public: RequireNoErr(NewAUGraph(&m_auGraph)); AUNode outputNode, synthNode; + // OS X 10.5 SDK doesn't know AudioComponentDescription desc; +#if USE_DEPRECATED_COREAUDIO_API || (MAC_OS_X_VERSION_MAX_ALLOWED <= 1050) ComponentDescription desc; +#else + AudioComponentDescription desc; +#endif // The default output device desc.componentType = kAudioUnitType_Output; @@ -54,13 +84,21 @@ public: desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; +#if USE_DEPRECATED_COREAUDIO_API RequireNoErr(AUGraphNewNode(m_auGraph, &desc, 0, NULL, &outputNode)); +#else + RequireNoErr(AUGraphAddNode(m_auGraph, &desc, &outputNode)); +#endif // The built-in default (softsynth) music device desc.componentType = kAudioUnitType_MusicDevice; desc.componentSubType = kAudioUnitSubType_DLSSynth; desc.componentManufacturer = kAudioUnitManufacturer_Apple; +#if USE_DEPRECATED_COREAUDIO_API RequireNoErr(AUGraphNewNode(m_auGraph, &desc, 0, NULL, &synthNode)); +#else + RequireNoErr(AUGraphAddNode(m_auGraph, &desc, &synthNode)); +#endif // Connect the softsynth to the default output RequireNoErr(AUGraphConnectNodeInput(m_auGraph, synthNode, 0, outputNode, 0)); @@ -70,24 +108,57 @@ public: RequireNoErr(AUGraphInitialize(m_auGraph)); // Get the music device from the graph. +#if USE_DEPRECATED_COREAUDIO_API RequireNoErr(AUGraphGetNodeInfo(m_auGraph, synthNode, NULL, NULL, NULL, &m_synth)); +#else + RequireNoErr(AUGraphNodeInfo(m_auGraph, synthNode, NULL, &m_synth)); +#endif - // Optionally load a soundfont - if (conf && conf[0]) { - soundfont = conf; - FSRef soundfontRef; - RequireNoErr(FSPathMakeRef((const UInt8*)soundfont, &soundfontRef, NULL)); - RequireNoErr(AudioUnitSetProperty( - m_synth, - kMusicDeviceProperty_SoundBankFSRef, - kAudioUnitScope_Global, - 0, - &soundfontRef, - sizeof(soundfontRef) - )); - LOG_MSG("MIDI:coreaudio: loaded soundfont: %s",soundfont); - } - + // Optionally load a soundfont + if (conf && conf[0]) { + soundfont = conf; + OSErr err; +#if USE_DEPRECATED_COREAUDIO_API + FSRef soundfontRef; + err = FSPathMakeRef((const UInt8*)soundfont, &soundfontRef, NULL); + if (!err) { + err = AudioUnitSetProperty( + m_synth, + kMusicDeviceProperty_SoundBankFSRef, + kAudioUnitScope_Global, + 0, + &soundfontRef, + sizeof(soundfontRef) + ); + } +#else + // kMusicDeviceProperty_SoundBankFSRef is present on 10.6+, but + // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a future prooof replacement + CFURLRef url = CFURLCreateFromFileSystemRepresentation( + kCFAllocatorDefault, + (const UInt8*)soundfont, + strlen(soundfont), false + ); + if (url) { + err = AudioUnitSetProperty( + m_synth, kMusicDeviceProperty_SoundBankURL, + kAudioUnitScope_Global, 0, &url, sizeof(url) + ); + CFRelease(url); + } else { + LOG_MSG("Failed to allocate CFURLRef from %s",soundfont); + } +#endif + if (!err) { + LOG_MSG("MIDI:coreaudio: loaded soundfont: %s",soundfont); + } else { + LOG_MSG("Error loading CoreAudio SoundFont %s",soundfont); + // after trying and failing to load a soundfont it's better + // to fail initializing the CoreAudio driver or it might crash + return false; + } + } + // Finally: Start the graph! RequireNoErr(AUGraphStart(m_auGraph)); diff --git a/src/gui/midi_coremidi.h b/src/gui/midi_coremidi.h index 460b44f..9ad046b 100644 --- a/src/gui/midi_coremidi.h +++ b/src/gui/midi_coremidi.h @@ -9,12 +9,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include +#include +#include class MidiHandler_coremidi : public MidiHandler { private: @@ -25,14 +27,37 @@ private: public: MidiHandler_coremidi() {m_pCurPacket = 0;} const char * GetName(void) { return "coremidi"; } - bool Open(const char * conf) { - + bool Open(const char * conf) { // Get the MIDIEndPoint m_endpoint = 0; OSStatus result; Bitu numDests = MIDIGetNumberOfDestinations(); - Bitu destId = 0; - if(conf && conf[0]) destId = atoi(conf); + Bitu destId = numDests; + if(conf && *conf) { + std::string strconf(conf); + std::istringstream configmidi(strconf); + configmidi >> destId; + if (configmidi.fail() && numDests) { + lowcase(strconf); + for(Bitu i = 0; i= numDests) destId = 0; if (destId < numDests) { m_endpoint = MIDIGetDestination(destId); @@ -67,7 +92,8 @@ public: MIDIClientDispose(m_client); // Dispose the endpoint - MIDIEndpointDispose(m_endpoint); + // Not, as it is for Endpoints created by us +// MIDIEndpointDispose(m_endpoint); } void PlayMsg(Bit8u * msg) { @@ -99,6 +125,21 @@ public: // Send the MIDIPacketList MIDISend(m_port,m_endpoint,packetList); } + void ListAll(Program* base) { + Bitu numDests = MIDIGetNumberOfDestinations(); + for(Bitu i = 0; i < numDests; i++){ + MIDIEndpointRef dest = MIDIGetDestination(i); + if (!dest) continue; + CFStringRef midiname = 0; + if(MIDIObjectGetStringProperty(dest, kMIDIPropertyDisplayName, &midiname) == noErr) { + const char * s = CFStringGetCStringPtr(midiname, kCFStringEncodingMacRoman); + if (s) base->WriteOut("%02d\t%s\n",i,s); + } + //This is for EndPoints created by us. + //MIDIEndpointDispose(dest); + } + + } }; MidiHandler_coremidi Midi_coremidi; diff --git a/src/gui/midi_oss.h b/src/gui/midi_oss.h index 2571dae..00c1cad 100644 --- a/src/gui/midi_oss.h +++ b/src/gui/midi_oss.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include diff --git a/src/gui/midi_win32.h b/src/gui/midi_win32.h index 0535bf1..b7180f6 100644 --- a/src/gui/midi_win32.h +++ b/src/gui/midi_win32.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -41,16 +41,31 @@ public: if(conf && *conf) { std::string strconf(conf); std::istringstream configmidi(strconf); - unsigned int nummer = midiOutGetNumDevs(); + unsigned int total = midiOutGetNumDevs(); + unsigned int nummer = total; configmidi >> nummer; - if(nummer < midiOutGetNumDevs()){ + if (configmidi.fail() && total) { + lowcase(strconf); + for(unsigned int i = 0; i< total;i++) { + MIDIOUTCAPS mididev; + midiOutGetDevCaps(i, &mididev, sizeof(MIDIOUTCAPS)); + std::string devname(mididev.szPname); + lowcase(devname); + if (devname.find(strconf) != std::string::npos) { + nummer = i; + break; + } + } + } + + if (nummer < total) { MIDIOUTCAPS mididev; midiOutGetDevCaps(nummer, &mididev, sizeof(MIDIOUTCAPS)); - LOG_MSG("MIDI:win32 selected %s",mididev.szPname); - res = midiOutOpen(&m_out, nummer, (DWORD)m_event, 0, CALLBACK_EVENT); + LOG_MSG("MIDI: win32 selected %s",mididev.szPname); + res = midiOutOpen(&m_out, nummer, (DWORD_PTR)m_event, 0, CALLBACK_EVENT); } } else { - res = midiOutOpen(&m_out, MIDI_MAPPER, (DWORD)m_event, 0, CALLBACK_EVENT); + res = midiOutOpen(&m_out, MIDI_MAPPER, (DWORD_PTR)m_event, 0, CALLBACK_EVENT); } if (res != MMSYSERR_NOERROR) return false; isOpen=true; @@ -70,9 +85,9 @@ public: if (WaitForSingleObject (m_event, 2000) == WAIT_TIMEOUT) { LOG(LOG_MISC,LOG_ERROR)("Can't send midi message"); return; - } + } midiOutUnprepareHeader (m_out, &m_hdr, sizeof (m_hdr)); - + m_hdr.lpData = (char *) sysex; m_hdr.dwBufferLength = len ; m_hdr.dwBytesRecorded = len ; @@ -87,8 +102,16 @@ public: return; } } + void ListAll(Program* base) { + unsigned int total = midiOutGetNumDevs(); + for(unsigned int i = 0;i < total;i++) { + MIDIOUTCAPS mididev; + midiOutGetDevCaps(i, &mididev, sizeof(MIDIOUTCAPS)); + base->WriteOut("%2d\t \"%s\"\n",i,mididev.szPname); + } + } }; -MidiHandler_win32 Midi_win32; +MidiHandler_win32 Midi_win32; diff --git a/src/gui/render.cpp b/src/gui/render.cpp index 2bb29cf..6db94cf 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -376,11 +376,11 @@ forcenormal: } switch (render.src.bpp) { case 8: - render.src.start = ( render.src.width * 1) / sizeof(Bitu); - if (gfx_flags & GFX_CAN_8) - gfx_flags |= GFX_LOVE_8; - else - gfx_flags |= GFX_LOVE_32; + render.src.start = ( render.src.width * 1) / sizeof(Bitu); + if (gfx_flags & GFX_CAN_8) + gfx_flags |= GFX_LOVE_8; + else + gfx_flags |= GFX_LOVE_32; break; case 15: render.src.start = ( render.src.width * 2) / sizeof(Bitu); @@ -519,7 +519,7 @@ void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double ratio,bool return; } if ( ratio > 1 ) { - double target = height * ratio + 0.1; + double target = height * ratio + 0.025; ratio = target / height; } else { //This would alter the width of the screen, we don't care about rounding errors here @@ -534,7 +534,7 @@ void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double ratio,bool RENDER_Reset( ); } -extern void GFX_SetTitle(Bit32s cycles, Bits frameskip,bool paused); +extern void GFX_SetTitle(Bit32s cycles, int frameskip,bool paused); static void IncreaseFrameSkip(bool pressed) { if (!pressed) return; @@ -581,9 +581,9 @@ void RENDER_Init(Section * sec) { std::string cline; std::string scaler; //Check for commandline paramters and parse them through the configclass so they get checked against allowed values - if (control->cmdline->FindString("-scaler",cline,false)) { + if (control->cmdline->FindString("-scaler",cline,true)) { section->HandleInputline(std::string("scaler=") + cline); - } else if (control->cmdline->FindString("-forcescaler",cline,false)) { + } else if (control->cmdline->FindString("-forcescaler",cline,true)) { section->HandleInputline(std::string("scaler=") + cline + " forced"); } diff --git a/src/gui/render_loops.h b/src/gui/render_loops.h index 6a4e7b6..635da6b 100644 --- a/src/gui/render_loops.h +++ b/src/gui/render_loops.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if defined (SCALERLINEAR) diff --git a/src/gui/render_scalers.cpp b/src/gui/render_scalers.cpp index 25f3ed3..04fe41f 100644 --- a/src/gui/render_scalers.cpp +++ b/src/gui/render_scalers.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/gui/render_scalers.h b/src/gui/render_scalers.h index f55d0b4..0d394d0 100644 --- a/src/gui/render_scalers.h +++ b/src/gui/render_scalers.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _RENDER_SCALERS_H diff --git a/src/gui/render_simple.h b/src/gui/render_simple.h index 063c2a7..f6e587d 100644 --- a/src/gui/render_simple.h +++ b/src/gui/render_simple.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/gui/render_templates.h b/src/gui/render_templates.h index c659b2f..3064393 100644 --- a/src/gui/render_templates.h +++ b/src/gui/render_templates.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if DBPP == 8 @@ -382,9 +382,9 @@ static void conc3d(Cache,SBPP,DBPP) (const void * s) { line0[0]=P; \ line0[1]=P; \ line0[2]=P; \ - line1[0]=0; \ - line1[1]=0; \ - line1[2]=0; \ + line1[0]=P; \ + line1[1]=P; \ + line1[2]=P; \ line2[0]=0; \ line2[1]=0; \ line2[2]=0; diff --git a/src/gui/render_templates_hq.h b/src/gui/render_templates_hq.h index 8153af2..3bfd65e 100644 --- a/src/gui/render_templates_hq.h +++ b/src/gui/render_templates_hq.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* @@ -41,17 +41,17 @@ static inline bool diffYUV(Bit32u yuv1, Bit32u yuv2) Bit32u mask; diff = ((yuv1 & Ymask) - (yuv2 & Ymask)); - mask = diff >> 31; // -1 if value < 0, 0 otherwise + mask = ((Bit32s)diff) >> 31; // ~1/-1 if value < 0, 0 otherwise diff = (diff ^ mask) - mask; //-1: ~value + 1; 0: value if (diff > trY) return true; diff = ((yuv1 & Umask) - (yuv2 & Umask)); - mask = diff >> 31; // -1 if value < 0, 0 otherwise + mask = ((Bit32s)diff)>> 31; // ~1/-1 if value < 0, 0 otherwise diff = (diff ^ mask) - mask; //-1: ~value + 1; 0: value if (diff > trU) return true; diff = ((yuv1 & Vmask) - (yuv2 & Vmask)); - mask = diff >> 31; // -1 if value < 0, 0 otherwise + mask = ((Bit32s)diff) >> 31; // ~1/-1 if value < 0, 0 otherwise diff = (diff ^ mask) - mask; //-1: ~value + 1; 0: value if (diff > trV) return true; diff --git a/src/gui/render_templates_hq2x.h b/src/gui/render_templates_hq2x.h index 94879cb..bcec357 100644 --- a/src/gui/render_templates_hq2x.h +++ b/src/gui/render_templates_hq2x.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* diff --git a/src/gui/render_templates_hq3x.h b/src/gui/render_templates_hq3x.h index e8fa21d..e067cb8 100644 --- a/src/gui/render_templates_hq3x.h +++ b/src/gui/render_templates_hq3x.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* diff --git a/src/gui/render_templates_sai.h b/src/gui/render_templates_sai.h index 78006cc..9510336 100644 --- a/src/gui/render_templates_sai.h +++ b/src/gui/render_templates_sai.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ static inline int conc2d(GetResult,SBPP)(PTYPE A, PTYPE B, PTYPE C, PTYPE D) { diff --git a/src/gui/sdl_gui.cpp b/src/gui/sdl_gui.cpp index 8d869d3..a68216f 100644 --- a/src/gui/sdl_gui.cpp +++ b/src/gui/sdl_gui.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -38,9 +38,8 @@ #include extern Bit8u int10_font_14[256 * 14]; -extern Program * first_shell; extern bool MSG_Write(const char *); -extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, bool paused); +extern void GFX_SetTitle(Bit32s cycles, int frameskip, bool paused); static int cursor, saved_bpp; static int old_unicode; @@ -155,7 +154,6 @@ static GUI::ScreenSDL *UI_Startup(GUI::ScreenSDL *screen) { #else SDL_Surface* sdlscreen = SDL_SetVideoMode(w, h, 32, SDL_SWSURFACE|(fs?SDL_FULLSCREEN:0)); #endif - if (sdlscreen == NULL) E_Exit("Could not initialize video mode %ix%ix32 for UI: %s", w, h, SDL_GetError()); // fade out @@ -459,10 +457,9 @@ public: if (arg == "OK") section->data = *(std::string*)content->getText(); if (arg == "OK" || arg == "Cancel") close(); else if (arg == "Append Shell Commands") { - DOS_Shell *s = static_cast(first_shell); - std::list::reverse_iterator i = s->l_history.rbegin(); + std::list::reverse_iterator i = first_shell->l_history.rbegin(); std::string lines = *(std::string*)content->getText(); - while (i != s->l_history.rend()) { + while (i != first_shell->l_history.rend()) { lines += "\n"; lines += *i; ++i; @@ -574,7 +571,7 @@ public: Section_prop *section = static_cast(sec); new SectionEditor(getScreen(), 50, 30, section); } else if (arg == "About") { - new GUI::MessageBox(getScreen(), 200, 150, 280, "About DOSBox", "\nDOSBox 0.74\nAn emulator for old DOS Games\n\nCopyright 2002-2011\nThe DOSBox Team"); + new GUI::MessageBox(getScreen(), 200, 150, 280, "About DOSBox", "\nDOSBox 0.74\nAn emulator for old DOS Games\n\nCopyright 2002-2019\nThe DOSBox Team"); } else if (arg == "Introduction") { new GUI::MessageBox(getScreen(), 20, 50, 600, "Introduction", MSG_Get("PROGRAM_INTRO")); } else if (arg == "Getting Started") { diff --git a/src/gui/sdl_mapper.cpp b/src/gui/sdl_mapper.cpp index 82d1baa..471777c 100644 --- a/src/gui/sdl_mapper.cpp +++ b/src/gui/sdl_mapper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -36,13 +36,15 @@ #include "support.h" #include "mapper.h" #include "setup.h" +#include "pic.h" enum { CLR_BLACK=0, - CLR_WHITE=1, - CLR_RED=2, - CLR_BLUE=3, - CLR_GREEN=4 + CLR_GREY=1, + CLR_WHITE=2, + CLR_RED=3, + CLR_BLUE=4, + CLR_GREEN=5 }; enum BB_Types { @@ -352,7 +354,7 @@ static SDLKey sdlkey_map[]={ /* 4 extra keys that don't really exist, but are needed for * round-trip mapping (dosbox uses RMETA only for hotkeys, it's * not really mapped to an emulated key) */ - SDLK_RMETA, SDLK_RSHIFT, SDLK_RALT, SDLK_RCTRL, + SDLK_RMETA, SDLK_RSHIFT, SDLK_RALT, SDLK_RCTRL }; #define MAX_SCANCODES (0x80+4) /* Make sure that the table above has the expected size. This @@ -361,7 +363,7 @@ typedef char assert_right_size [MAX_SCANCODES == (sizeof(sdlkey_map)/sizeof(sdlk #else // !MACOSX -#define MAX_SCANCODES 212 +#define MAX_SCANCODES 0xdf static SDLKey sdlkey_map[MAX_SCANCODES]={SDLK_UNKNOWN,SDLK_ESCAPE, SDLK_1,SDLK_2,SDLK_3,SDLK_4,SDLK_5,SDLK_6,SDLK_7,SDLK_8,SDLK_9,SDLK_0, /* 0x0c: */ @@ -380,13 +382,28 @@ static SDLKey sdlkey_map[MAX_SCANCODES]={SDLK_UNKNOWN,SDLK_ESCAPE, SDLK_KP7,SDLK_KP8,SDLK_KP9,SDLK_KP_MINUS,SDLK_KP4,SDLK_KP5,SDLK_KP6,SDLK_KP_PLUS, SDLK_KP1,SDLK_KP2,SDLK_KP3,SDLK_KP0,SDLK_KP_PERIOD, SDLK_UNKNOWN,SDLK_UNKNOWN, - SDLK_LESS,SDLK_F11,SDLK_F12, - Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z, - Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z, - Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z, - /* 0xb7: */ - Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z - /* 0xd4: ... */ + SDLK_LESS,SDLK_F11,SDLK_F12, Z, Z, Z, Z, Z, Z, Z, + /* 0x60: */ + Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, + /* 0x70: */ + Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, + /* 0x80: */ + Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, + /* 0x90: */ + Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, + /* 0xA0: */ + Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, + /* 0xB0: */ + Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, + /* 0xC0: */ + Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, + /* 0xD0: */ + Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z//,Z,Z, + /* 0xE0: */ + //Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, + /* 0xF0: */ +// Z,Z,Z,Z, Z,Z,Z,Z, Z,Z,Z,Z, Z,Z//,Z,Z + }; #endif @@ -394,6 +411,7 @@ static SDLKey sdlkey_map[MAX_SCANCODES]={SDLK_UNKNOWN,SDLK_ESCAPE, SDLKey MapSDLCode(Bitu skey) { +// LOG_MSG("MapSDLCode %d %X",skey,skey); if (usescancodes) { if (skeyjaxis.value)<25000) return 0; return CreateAxisBind(event->jaxis.axis,event->jaxis.value>0); } else if (event->type==SDL_JOYBUTTONDOWN) { - if (event->button.which!=stick) return 0; + if (event->jbutton.which!=stick) return 0; #if defined (REDUCE_JOYSTICK_POLLING) return CreateButtonBind(event->jbutton.button%button_wrap); #else @@ -1245,7 +1265,6 @@ void CBindGroup::ActivateBindList(CBindList * list,Bits value,bool ev_trigger) { } } for (it=list->begin();it!=list->end();it++) { - /*BUG:CRASH if keymapper key is removed*/ if (validmod==(*it)->mods) (*it)->ActivateBind(value,ev_trigger); } } @@ -1299,6 +1318,7 @@ public: virtual bool OnTop(Bitu _x,Bitu _y) { return ( enabled && (_x>=x) && (_x=y) && (_ySetColor(event->bindlist.begin()==event->bindlist.end() ? CLR_GREY : CLR_WHITE); + } void Click(void) { - if (last_clicked) last_clicked->SetColor(CLR_WHITE); + if (last_clicked) last_clicked->BindColor(); this->SetColor(CLR_GREEN); SetActiveEvent(event); last_clicked=this; @@ -1667,7 +1690,7 @@ static void SetActiveEvent(CEvent * event) { } static void DrawButtons(void) { - SDL_FillRect(mapper.surface,0,0); + SDL_FillRect(mapper.surface,0,CLR_BLACK); SDL_LockSurface(mapper.surface); for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) { (*but_it)->Draw(); @@ -1730,7 +1753,7 @@ struct KeyBlock { KBD_KEYS key; }; static KeyBlock combo_f[12]={ - {"F1","f1",KBD_f1}, {"F2","f2",KBD_f2}, {"F3","f3",KBD_f3}, + {"F1","f1",KBD_f1}, {"F2","f2",KBD_f2}, {"F3","f3",KBD_f3}, {"F4","f4",KBD_f4}, {"F5","f5",KBD_f5}, {"F6","f6",KBD_f6}, {"F7","f7",KBD_f7}, {"F8","f8",KBD_f8}, {"F9","f9",KBD_f9}, {"F10","f10",KBD_f10}, {"F11","f11",KBD_f11}, {"F12","f12",KBD_f12}, @@ -1740,32 +1763,32 @@ static KeyBlock combo_1[14]={ {"`~","grave",KBD_grave}, {"1!","1",KBD_1}, {"2@","2",KBD_2}, {"3#","3",KBD_3}, {"4$","4",KBD_4}, {"5%","5",KBD_5}, {"6^","6",KBD_6}, {"7&","7",KBD_7}, {"8*","8",KBD_8}, - {"9(","9",KBD_9}, {"0)","0",KBD_0}, {"-_","minus",KBD_minus}, + {"9(","9",KBD_9}, {"0)","0",KBD_0}, {"-_","minus",KBD_minus}, {"=+","equals",KBD_equals}, {"\x1B","bspace",KBD_backspace}, }; static KeyBlock combo_2[12]={ - {"q","q",KBD_q}, {"w","w",KBD_w}, {"e","e",KBD_e}, - {"r","r",KBD_r}, {"t","t",KBD_t}, {"y","y",KBD_y}, - {"u","u",KBD_u}, {"i","i",KBD_i}, {"o","o",KBD_o}, - {"p","p",KBD_p}, {"[","lbracket",KBD_leftbracket}, - {"]","rbracket",KBD_rightbracket}, + {"Q","q",KBD_q}, {"W","w",KBD_w}, {"E","e",KBD_e}, + {"R","r",KBD_r}, {"T","t",KBD_t}, {"Y","y",KBD_y}, + {"U","u",KBD_u}, {"I","i",KBD_i}, {"O","o",KBD_o}, + {"P","p",KBD_p}, {"[{","lbracket",KBD_leftbracket}, + {"]}","rbracket",KBD_rightbracket}, }; static KeyBlock combo_3[12]={ - {"a","a",KBD_a}, {"s","s",KBD_s}, {"d","d",KBD_d}, - {"f","f",KBD_f}, {"g","g",KBD_g}, {"h","h",KBD_h}, - {"j","j",KBD_j}, {"k","k",KBD_k}, {"l","l",KBD_l}, - {";","semicolon",KBD_semicolon}, {"'","quote",KBD_quote}, - {"\\","backslash",KBD_backslash}, + {"A","a",KBD_a}, {"S","s",KBD_s}, {"D","d",KBD_d}, + {"F","f",KBD_f}, {"G","g",KBD_g}, {"H","h",KBD_h}, + {"J","j",KBD_j}, {"K","k",KBD_k}, {"L","l",KBD_l}, + {";:","semicolon",KBD_semicolon}, {"'\"","quote",KBD_quote}, + {"\\|","backslash",KBD_backslash}, }; static KeyBlock combo_4[11]={ - {"<","lessthan",KBD_extra_lt_gt}, - {"z","z",KBD_z}, {"x","x",KBD_x}, {"c","c",KBD_c}, - {"v","v",KBD_v}, {"b","b",KBD_b}, {"n","n",KBD_n}, - {"m","m",KBD_m}, {",","comma",KBD_comma}, - {".","period",KBD_period}, {"/","slash",KBD_slash}, + {"<>","lessthan",KBD_extra_lt_gt}, + {"Z","z",KBD_z}, {"X","x",KBD_x}, {"C","c",KBD_c}, + {"V","v",KBD_v}, {"B","b",KBD_b}, {"N","n",KBD_n}, + {"M","m",KBD_m}, {",<","comma",KBD_comma}, + {".>","period",KBD_period}, {"/?","slash",KBD_slash}, }; static CKeyEvent * caps_lock_event=NULL; @@ -1910,14 +1933,17 @@ static void CreateLayout(void) { AddJHatButton(PX(XO+8+2),PY(YO+1),BW,BH,"RGT",0,0,1); /* Labels for the joystick */ + CTextButton * btn; if (joytype ==JOY_2AXIS) { new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Joystick 1"); new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Joystick 2"); - new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled"); + btn=new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled"); + btn->SetColor(CLR_GREY); } else if(joytype ==JOY_4AXIS || joytype == JOY_4AXIS_2) { new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Axis 1/2"); new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Axis 3/4"); - new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled"); + btn=new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled"); + btn->SetColor(CLR_GREY); } else if(joytype == JOY_CH) { new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Axis 1/2"); new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Axis 3/4"); @@ -1927,9 +1953,12 @@ static void CreateLayout(void) { new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Axis 3"); new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Hat/D-pad"); } else if(joytype == JOY_NONE) { - new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Disabled"); - new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Disabled"); - new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled"); + btn=new CTextButton(PX(XO+0),PY(YO-1),3*BW,20,"Disabled"); + btn->SetColor(CLR_GREY); + btn=new CTextButton(PX(XO+4),PY(YO-1),3*BW,20,"Disabled"); + btn->SetColor(CLR_GREY); + btn=new CTextButton(PX(XO+8),PY(YO-1),3*BW,20,"Disabled"); + btn->SetColor(CLR_GREY); } @@ -1951,7 +1980,7 @@ static void CreateLayout(void) { // new CTextButton(PX(6),0,124,20,"Keyboard Layout"); // new CTextButton(PX(17),0,124,20,"Joystick Layout"); - bind_but.action=new CCaptionButton(180,330,0,0); + bind_but.action=new CCaptionButton(180,350,0,0); bind_but.event_title=new CCaptionButton(0,350,0,0); bind_but.bind_title=new CCaptionButton(0,365,0,0); @@ -1974,12 +2003,13 @@ static void CreateLayout(void) { bind_but.bind_title->Change("Bind Title"); } -static SDL_Color map_pal[5]={ +static SDL_Color map_pal[6]={ {0x00,0x00,0x00,0x00}, //0=black - {0xff,0xff,0xff,0x00}, //1=white - {0xff,0x00,0x00,0x00}, //2=red - {0x10,0x30,0xff,0x00}, //3=blue - {0x00,0xff,0x20,0x00} //4=green + {0x7f,0x7f,0x7f,0x00}, //1=grey + {0xff,0xff,0xff,0x00}, //2=white + {0xff,0x00,0x00,0x00}, //3=red + {0x10,0x30,0xff,0x00}, //4=blue + {0x00,0xff,0x20,0x00} //5=green }; static void CreateStringBind(char * line) { @@ -2303,14 +2333,20 @@ void MAPPER_LosingFocus(void) { } } -void MAPPER_Run(bool pressed) { - if (pressed) - return; +void MAPPER_RunEvent(Bitu /*val*/) { KEYBOARD_ClrBuffer(); //Clear buffer GFX_LosingFocus(); //Release any keys pressed (buffer gets filled again). MAPPER_RunInternal(); } +void MAPPER_Run(bool pressed) { + if (pressed) + return; + PIC_AddEvent(MAPPER_RunEvent,0); //In case mapper deletes the key object that ran it +} + +SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags); + void MAPPER_RunInternal() { int cursor = SDL_ShowCursor(SDL_QUERY); SDL_ShowCursor(SDL_ENABLE); @@ -2322,13 +2358,13 @@ void MAPPER_RunInternal() { /* Be sure that there is no update in progress */ GFX_EndUpdate( 0 ); - mapper.surface=SDL_SetVideoMode(640,480,8,0); + mapper.surface=SDL_SetVideoMode_Wrap(640,480,8,0); if (mapper.surface == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError()); /* Set some palette entries */ - SDL_SetPalette(mapper.surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, 5); + SDL_SetPalette(mapper.surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, 6); if (last_clicked) { - last_clicked->SetColor(CLR_WHITE); + last_clicked->BindColor(); last_clicked=NULL; } /* Go in the event loop */ @@ -2359,6 +2395,9 @@ void MAPPER_Init(void) { CreateLayout(); CreateBindGroups(); if (!MAPPER_LoadBinds()) CreateDefaultBinds(); + for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) { + (*but_it)->BindColor(); + } if (SDL_GetModState()&KMOD_CAPS) { for (CBindList_it bit=caps_lock_event->bindlist.begin();bit!=caps_lock_event->bindlist.end();bit++) { #if SDL_VERSION_ATLEAST(1, 2, 14) @@ -2389,17 +2428,7 @@ void MAPPER_StartUp(Section * sec) { Section_prop * section=static_cast(sec); mapper.sticks.num=0; mapper.sticks.num_groups=0; - Bitu i; - for (i=0; i<16; i++) { - virtual_joysticks[0].button_pressed[i]=false; - virtual_joysticks[1].button_pressed[i]=false; - virtual_joysticks[0].hat_pressed[i]=false; - virtual_joysticks[1].hat_pressed[i]=false; - } - for (i=0; i<8; i++) { - virtual_joysticks[0].axis_pos[i]=0; - virtual_joysticks[0].axis_pos[i]=0; - } + memset(&virtual_joysticks,0,sizeof(virtual_joysticks)); usescancodes = false; @@ -2430,6 +2459,8 @@ void MAPPER_StartUp(Section * sec) { sdlkey_map[0x41]=SDLK_KP6; #elif !defined (WIN32) /* => Linux & BSDs */ bool evdev_input = false; +#ifdef SDL_VIDEO_DRIVER_X11 +//SDL needs to be compiled to use it, else the next makes no sense. #ifdef C_X11_XKB SDL_SysWMinfo info; SDL_VERSION(&info.version); @@ -2448,6 +2479,7 @@ void MAPPER_StartUp(Section * sec) { XkbFreeClientMap(desc,0,True); } } +#endif #endif if (evdev_input) { sdlkey_map[0x67]=SDLK_UP; @@ -2466,6 +2498,11 @@ void MAPPER_StartUp(Section * sec) { sdlkey_map[0x77]=SDLK_PAUSE; sdlkey_map[0x63]=SDLK_PRINT; sdlkey_map[0x64]=SDLK_RALT; + + //Win-keys + sdlkey_map[0x7d]=SDLK_LSUPER; + sdlkey_map[0x7e]=SDLK_RSUPER; + sdlkey_map[0x7f]=SDLK_MENU; } else { sdlkey_map[0x5a]=SDLK_UP; sdlkey_map[0x60]=SDLK_DOWN; @@ -2501,6 +2538,12 @@ void MAPPER_StartUp(Section * sec) { sdlkey_map[0xc5]=SDLK_PAUSE; sdlkey_map[0xb7]=SDLK_PRINT; sdlkey_map[0xb8]=SDLK_RALT; + + //Win-keys + sdlkey_map[0xdb]=SDLK_LMETA; + sdlkey_map[0xdc]=SDLK_RMETA; + sdlkey_map[0xdd]=SDLK_MENU; + #endif Bitu i; diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index 2a70a10..845d01e 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -66,34 +66,30 @@ #define APIENTRYP APIENTRY * #endif -#ifdef __WIN32__ -#define NVIDIA_PixelDataRange 1 - -#ifndef WGL_NV_allocate_memory -#define WGL_NV_allocate_memory 1 -typedef void * (APIENTRY * PFNWGLALLOCATEMEMORYNVPROC) (int size, float readfreq, float writefreq, float priority); -typedef void (APIENTRY * PFNWGLFREEMEMORYNVPROC) (void *pointer); +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF #endif -PFNWGLALLOCATEMEMORYNVPROC db_glAllocateMemoryNV = NULL; -PFNWGLFREEMEMORYNVPROC db_glFreeMemoryNV = NULL; - -#else - +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); #endif -#if defined(NVIDIA_PixelDataRange) - -#ifndef GL_NV_pixel_data_range -#define GL_NV_pixel_data_range 1 -#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 -typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, GLvoid *pointer); -typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); -#endif - -PFNGLPIXELDATARANGENVPROC glPixelDataRangeNV = NULL; - -#endif +PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; +PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; +PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; +PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; +PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL; +PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL; #endif //C_OPENGL @@ -106,7 +102,7 @@ extern char** environ; #define WIN32_LEAN_AND_MEAN #endif #include -#if (HAVE_DDRAW_H) +#if C_DDRAW #include struct private_hwdata { LPDIRECTDRAWSURFACE3 dd_surface; @@ -184,20 +180,19 @@ struct SDL_Block { struct { Bitu pitch; void * framebuf; + GLuint buffer; GLuint texture; GLuint displaylist; GLint max_texsize; bool bilinear; bool packed_pixel; bool paletted_texture; -#if defined(NVIDIA_PixelDataRange) - bool pixel_data_range; -#endif + bool pixel_buffer_object; } opengl; #endif struct { SDL_Surface * surface; -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW RECT rect; #endif } blit; @@ -214,12 +209,15 @@ struct SDL_Block { bool autoenable; bool requestlock; bool locked; - Bitu sensitivity; + int xsensitivity; + int ysensitivity; } mouse; SDL_Rect updateRects[1024]; Bitu num_joysticks; #if defined (WIN32) bool using_windib; + // Time when sdl regains focus (alt-tab) in windowed mode + Bit32u focus_ticks; #endif // state of alt-keys for certain special handlings Bit8u laltstate; @@ -228,25 +226,82 @@ struct SDL_Block { static SDL_Block sdl; +#define SETMODE_SAVES 1 //Don't set Video Mode if nothing changes. +#define SETMODE_SAVES_CLEAR 1 //Clear the screen, when the Video Mode is reused +SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags){ +#if SETMODE_SAVES + static int i_height = 0; + static int i_width = 0; + static int i_bpp = 0; + static Bit32u i_flags = 0; + if (sdl.surface != NULL && height == i_height && width == i_width && bpp == i_bpp && flags == i_flags) { + // I don't see a difference, so disabled for now, as the code isn't finished either +#if SETMODE_SAVES_CLEAR + //TODO clear it. +#if C_OPENGL + if ((flags & SDL_OPENGL)==0) + SDL_FillRect(sdl.surface,NULL,SDL_MapRGB(sdl.surface->format,0,0,0)); + else { + glClearColor (0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + SDL_GL_SwapBuffers(); + } +#else //C_OPENGL + SDL_FillRect(sdl.surface,NULL,SDL_MapRGB(sdl.surface->format,0,0,0)); +#endif //C_OPENGL +#endif //SETMODE_SAVES_CLEAR + return sdl.surface; + } + + +#ifdef WIN32 + //SDL seems to crash if we are in OpenGL mode currently and change to exactly the same size without OpenGL. + //This happens when DOSBox is in textmode with aspect=true and output=opengl and the mapper is started. + //The easiest solution is to change the size. The mapper doesn't care. (PART PXX) + + //Also we have to switch back to windowed mode first, as else it may crash as well. + //Bug: we end up with a locked mouse cursor, but at least that beats crashing. (output=opengl,aspect=true,fullscreen=true) + if((i_flags&SDL_OPENGL) && !(flags&SDL_OPENGL) && (i_flags&SDL_FULLSCREEN) && !(flags&SDL_FULLSCREEN)){ + GFX_SwitchFullScreen(); + return SDL_SetVideoMode_Wrap(width,height,bpp,flags); + } + + //PXX + if ((i_flags&SDL_OPENGL) && !(flags&SDL_OPENGL) && height==i_height && width==i_width && height==480) { + height++; + } +#endif //WIN32 +#endif //SETMODE_SAVES + SDL_Surface* s = SDL_SetVideoMode(width,height,bpp,flags); +#if SETMODE_SAVES + if (s == NULL) return s; //Only store when successful + i_height = height; + i_width = width; + i_bpp = bpp; + i_flags = flags; +#endif + return s; +} + extern const char* RunningProgram; extern bool CPU_CycleAutoAdjust; //Globals for keyboard initialisation bool startup_state_numlock=false; bool startup_state_capslock=false; -void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused){ - char title[200]={0}; - static Bit32s internal_cycles=0; - static Bits internal_frameskip=0; - if(cycles != -1) internal_cycles = cycles; - if(frameskip != -1) internal_frameskip = frameskip; +void GFX_SetTitle(Bit32s cycles,int frameskip,bool paused){ + char title[200] = { 0 }; + static Bit32s internal_cycles = 0; + static int internal_frameskip = 0; + if (cycles != -1) internal_cycles = cycles; + if (frameskip != -1) internal_frameskip = frameskip; if(CPU_CycleAutoAdjust) { sprintf(title,"DOSBox %s, CPU speed: max %3d%% cycles, Frameskip %2d, Program: %8s",VERSION,internal_cycles,internal_frameskip,RunningProgram); } else { sprintf(title,"DOSBox %s, CPU speed: %8d cycles, Frameskip %2d, Program: %8s",VERSION,internal_cycles,internal_frameskip,RunningProgram); } - if(paused) strcat(title," PAUSED"); + if (paused) strcat(title," PAUSED"); SDL_WM_SetCaption(title,VERSION); } @@ -258,7 +313,7 @@ static void GFX_SetIcon() { /* Set Icon (must be done before any sdl_setvideomode call) */ /* But don't set it on OS X, as we use a nicer external icon there. */ /* Made into a separate call, so it can be called again when we restart the graphics output on win32 */ -#if WORDS_BIGENDIAN +#ifdef WORDS_BIGENDIAN SDL_Surface* logos= SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0xff000000,0x00ff0000,0x0000ff00,0); #else SDL_Surface* logos= SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0x000000ff,0x0000ff00,0x00ff0000,0); @@ -304,7 +359,7 @@ static void PauseDOSBox(bool pressed) { /* On macs, all aps exit when pressing cmd-q */ KillSwitch(true); break; - } + } #endif } } @@ -329,7 +384,7 @@ check_surface: else if (flags & GFX_LOVE_16) testbpp=16; else if (flags & GFX_LOVE_32) testbpp=32; else testbpp=0; -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW check_gotbpp: #endif if (sdl.desktop.fullscreen) gotbpp=SDL_VideoModeOK(640,480,testbpp,SDL_FULLSCREEN|SDL_HWSURFACE|SDL_HWPALETTE); @@ -352,7 +407,7 @@ check_gotbpp: } flags |= GFX_CAN_RANDOM; break; -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW case SCREEN_SURFACE_DDRAW: if (!(flags&(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32))) goto check_surface; if (flags & GFX_LOVE_15) testbpp=15; @@ -363,13 +418,16 @@ check_gotbpp: goto check_gotbpp; #endif case SCREEN_OVERLAY: + //We only accept 32bit output from the scalers here + //Can't handle true color inputs if (flags & GFX_RGBONLY || !(flags&GFX_CAN_32)) goto check_surface; flags|=GFX_SCALING; flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); break; #if C_OPENGL case SCREEN_OPENGL: - if (flags & GFX_RGBONLY || !(flags&GFX_CAN_32)) goto check_surface; + //We only accept 32bit output from the scalers here + if (!(flags&GFX_CAN_32)) goto check_surface; flags|=GFX_SCALING; flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); break; @@ -426,15 +484,19 @@ static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) { double ratio_h=(double)fixedHeight/(sdl.draw.height*sdl.draw.scaley); if ( ratio_w < ratio_h) { sdl.clip.w=fixedWidth; - sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley*ratio_w); + sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley*ratio_w + 0.1); //possible rounding issues } else { - sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex*ratio_h); + /* + * The 0.4 is there to correct for rounding issues. + * (partly caused by the rounding issues fix in RENDER_SetSize) + */ + sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex*ratio_h + 0.4); sdl.clip.h=(Bit16u)fixedHeight; } if (sdl.desktop.fullscreen) - sdl.surface = SDL_SetVideoMode(fixedWidth,fixedHeight,bpp,sdl_flags); + sdl.surface = SDL_SetVideoMode_Wrap(fixedWidth,fixedHeight,bpp,sdl_flags); else - sdl.surface = SDL_SetVideoMode(sdl.clip.w,sdl.clip.h,bpp,sdl_flags); + sdl.surface = SDL_SetVideoMode_Wrap(sdl.clip.w,sdl.clip.h,bpp,sdl_flags); if (sdl.surface && sdl.surface->flags & SDL_FULLSCREEN) { sdl.clip.x=(Sint16)((sdl.surface->w-sdl.clip.w)/2); sdl.clip.y=(Sint16)((sdl.surface->h-sdl.clip.h)/2); @@ -447,7 +509,7 @@ static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) { sdl.clip.x=0;sdl.clip.y=0; sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex); sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley); - sdl.surface=SDL_SetVideoMode(sdl.clip.w,sdl.clip.h,bpp,sdl_flags); + sdl.surface=SDL_SetVideoMode_Wrap(sdl.clip.w,sdl.clip.h,bpp,sdl_flags); return sdl.surface; } } @@ -472,7 +534,7 @@ Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,G sdl.draw.scalex=scalex; sdl.draw.scaley=scaley; - Bitu bpp=0; + int bpp=0; Bitu retFlags = 0; if (sdl.blit.surface) { @@ -493,21 +555,21 @@ dosurface: if (sdl.desktop.full.fixed) { sdl.clip.x=(Sint16)((sdl.desktop.full.width-width)/2); sdl.clip.y=(Sint16)((sdl.desktop.full.height-height)/2); - sdl.surface=SDL_SetVideoMode(sdl.desktop.full.width,sdl.desktop.full.height,bpp, + sdl.surface=SDL_SetVideoMode_Wrap(sdl.desktop.full.width,sdl.desktop.full.height,bpp, SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0) | SDL_HWPALETTE); if (sdl.surface == NULL) E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",sdl.desktop.full.width,sdl.desktop.full.height,bpp,SDL_GetError()); } else { sdl.clip.x=0;sdl.clip.y=0; - sdl.surface=SDL_SetVideoMode(width,height,bpp, + sdl.surface=SDL_SetVideoMode_Wrap(width,height,bpp, SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0)|SDL_HWPALETTE); if (sdl.surface == NULL) - E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",width,height,bpp,SDL_GetError()); + E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",(int)width,(int)height,bpp,SDL_GetError()); } } else { sdl.clip.x=0;sdl.clip.y=0; - sdl.surface=SDL_SetVideoMode(width,height,bpp,(flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE); + sdl.surface=SDL_SetVideoMode_Wrap(width,height,bpp,(flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE); #ifdef WIN32 if (sdl.surface == NULL) { SDL_QuitSubSystem(SDL_INIT_VIDEO); @@ -522,12 +584,12 @@ dosurface: } SDL_InitSubSystem(SDL_INIT_VIDEO); GFX_SetIcon(); //Set Icon again - sdl.surface = SDL_SetVideoMode(width,height,bpp,SDL_HWSURFACE); + sdl.surface = SDL_SetVideoMode_Wrap(width,height,bpp,SDL_HWSURFACE); if(sdl.surface) GFX_SetTitle(-1,-1,false); //refresh title. } #endif if (sdl.surface == NULL) - E_Exit("Could not set windowed video mode %ix%i-%i: %s",width,height,bpp,SDL_GetError()); + E_Exit("Could not set windowed video mode %ix%i-%i: %s",(int)width,(int)height,bpp,SDL_GetError()); } if (sdl.surface) { switch (sdl.surface->format->BitsPerPixel) { @@ -558,7 +620,7 @@ dosurface: } } break; -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW case SCREEN_SURFACE_DDRAW: if (flags & GFX_CAN_15) bpp=15; if (flags & GFX_CAN_16) bpp=16; @@ -605,7 +667,7 @@ dosurface: if (!GFX_SetupSurfaceScaled(0,0)) goto dosurface; sdl.overlay=SDL_CreateYUVOverlay(width*2,height,SDL_UYVY_OVERLAY,sdl.surface); if (!sdl.overlay) { - LOG_MSG("SDL:Failed to create overlay, switching back to surface"); + LOG_MSG("SDL: Failed to create overlay, switching back to surface"); goto dosurface; } sdl.desktop.type=SCREEN_OVERLAY; @@ -614,43 +676,46 @@ dosurface: #if C_OPENGL case SCREEN_OPENGL: { - if (sdl.opengl.framebuf) { -#if defined(NVIDIA_PixelDataRange) - if (sdl.opengl.pixel_data_range) db_glFreeMemoryNV(sdl.opengl.framebuf); - else -#endif + if (sdl.opengl.pixel_buffer_object) { + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); + if (sdl.opengl.buffer) glDeleteBuffersARB(1, &sdl.opengl.buffer); + } else if (sdl.opengl.framebuf) { free(sdl.opengl.framebuf); } sdl.opengl.framebuf=0; - if (!(flags&GFX_CAN_32) || (flags & GFX_RGBONLY)) goto dosurface; + if (!(flags&GFX_CAN_32)) goto dosurface; int texsize=2 << int_log2(width > height ? width : height); if (texsize>sdl.opengl.max_texsize) { - LOG_MSG("SDL:OPENGL:No support for texturesize of %d, falling back to surface",texsize); + LOG_MSG("SDL:OPENGL: No support for texturesize of %d, falling back to surface",texsize); goto dosurface; } SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); -#if defined (WIN32) && SDL_VERSION_ATLEAST(1, 2, 11) +#if SDL_VERSION_ATLEAST(1, 2, 11) SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 ); #endif GFX_SetupSurfaceScaled(SDL_OPENGL,0); if (!sdl.surface || sdl.surface->format->BitsPerPixel<15) { - LOG_MSG("SDL:OPENGL:Can't open drawing surface, are you running in 16bpp(or higher) mode?"); + LOG_MSG("SDL:OPENGL: Can't open drawing surface, are you running in 16bpp (or higher) mode?"); goto dosurface; } /* Create the texture and display list */ -#if defined(NVIDIA_PixelDataRange) - if (sdl.opengl.pixel_data_range) { - sdl.opengl.framebuf=db_glAllocateMemoryNV(width*height*4,0.0,1.0,1.0); - glPixelDataRangeNV(GL_WRITE_PIXEL_DATA_RANGE_NV,width*height*4,sdl.opengl.framebuf); - glEnableClientState(GL_WRITE_PIXEL_DATA_RANGE_NV); + if (sdl.opengl.pixel_buffer_object) { + glGenBuffersARB(1, &sdl.opengl.buffer); + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); + glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_DRAW_ARB); + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); } else { -#else - { -#endif sdl.opengl.framebuf=malloc(width*height*4); //32 bit color } sdl.opengl.pitch=width*4; - glViewport(sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h); + + if(sdl.clip.x ==0 && sdl.clip.y ==0 && sdl.desktop.fullscreen && !sdl.desktop.full.fixed && (sdl.clip.w != sdl.surface->w || sdl.clip.h != sdl.surface->h)) { +// LOG_MSG("attempting to fix the centering to %d %d %d %d",(sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h); + glViewport((sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h); + } else { + glViewport(sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h); + } + glMatrixMode (GL_PROJECTION); glDeleteTextures(1,&sdl.opengl.texture); glGenTextures(1,&sdl.opengl.texture); @@ -658,17 +723,20 @@ dosurface: // No borders glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - if (sdl.opengl.bilinear) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } else { + if (!sdl.opengl.bilinear || ( (sdl.clip.h % height) == 0 && (sdl.clip.w % width) == 0) ) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texsize, texsize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0); + Bit8u* emptytex = new Bit8u[texsize * texsize * 4]; + memset((void*) emptytex, 0, texsize * texsize * 4); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texsize, texsize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, (const GLvoid*)emptytex); + delete [] emptytex; - glClearColor (0.0, 0.0, 0.0, 1.0); + glClearColor (0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); SDL_GL_SwapBuffers(); glClear(GL_COLOR_BUFFER_BIT); @@ -687,23 +755,21 @@ dosurface: sdl.opengl.displaylist = glGenLists(1); glNewList(sdl.opengl.displaylist, GL_COMPILE); glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); - glBegin(GL_QUADS); - // lower left - glTexCoord2f(0,tex_height); glVertex2f(-1.0f,-1.0f); - // lower right - glTexCoord2f(tex_width,tex_height); glVertex2f(1.0f, -1.0f); - // upper right - glTexCoord2f(tex_width,0); glVertex2f(1.0f, 1.0f); + + glBegin(GL_TRIANGLES); // upper left glTexCoord2f(0,0); glVertex2f(-1.0f, 1.0f); + // lower left + glTexCoord2f(0,tex_height*2); glVertex2f(-1.0f,-3.0f); + // upper right + glTexCoord2f(tex_width*2,0); glVertex2f(3.0f, 1.0f); glEnd(); + glEndList(); sdl.desktop.type=SCREEN_OPENGL; retFlags = GFX_CAN_32 | GFX_SCALING; -#if defined(NVIDIA_PixelDataRange) - if (sdl.opengl.pixel_data_range) + if (sdl.opengl.pixel_buffer_object) retFlags |= GFX_HARDWARE; -#endif break; }//OPENGL #endif //C_OPENGL @@ -755,7 +821,7 @@ void sticky_keys(bool restore){ if (!inited){ inited = true; SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0); - } + } if (restore) { SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0); return; @@ -779,7 +845,7 @@ void GFX_SwitchFullScreen(void) { #endif } else { if (sdl.mouse.locked) GFX_CaptureMouse(); -#if defined (WIN32) +#if defined (WIN32) sticky_keys(true); //restore sticky keys to default state in windowed mode. #endif } @@ -838,7 +904,7 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { } sdl.updating=true; return true; -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW case SCREEN_SURFACE_DDRAW: if (SDL_LockSurface(sdl.blit.surface)) { // LOG_MSG("SDL Lock failed"); @@ -857,7 +923,11 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { return true; #if C_OPENGL case SCREEN_OPENGL: - pixels=(Bit8u *)sdl.opengl.framebuf; + if(sdl.opengl.pixel_buffer_object) { + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); + pixels=(Bit8u *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); + } else + pixels=(Bit8u *)sdl.opengl.framebuf; pitch=sdl.opengl.pitch; sdl.updating=true; return true; @@ -870,7 +940,7 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { void GFX_EndUpdate( const Bit16u *changedLines ) { -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW int ret; #endif if (!sdl.updating) @@ -911,7 +981,7 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { SDL_UpdateRects( sdl.surface, rectCount, sdl.updateRects ); } break; -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW case SCREEN_SURFACE_DDRAW: SDL_UnlockSurface(sdl.blit.surface); ret=IDirectDrawSurface3_Blt( @@ -926,7 +996,7 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { IDirectDrawSurface3_Restore(sdl.surface->hwdata->dd_surface); break; default: - LOG_MSG("DDRAW:Failed to blit, error %X",ret); + LOG_MSG("DDRAW: Failed to blit, error %X",ret); } SDL_Flip(sdl.surface); break; @@ -937,19 +1007,22 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { break; #if C_OPENGL case SCREEN_OPENGL: -#if defined(NVIDIA_PixelDataRange) - if (sdl.opengl.pixel_data_range) { - glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + // Clear drawing area. Some drivers (on Linux) have more than 2 buffers and the screen might + // be dirty because of other programs. + glClearColor (0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + if (sdl.opengl.pixel_buffer_object) { + glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT); + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sdl.draw.width, sdl.draw.height, GL_BGRA_EXT, - GL_UNSIGNED_INT_8_8_8_8_REV, sdl.opengl.framebuf); + GL_UNSIGNED_INT_8_8_8_8_REV, 0); + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); glCallList(sdl.opengl.displaylist); SDL_GL_SwapBuffers(); - } else -#endif - if (changedLines) { + } else if (changedLines) { Bitu y = 0, index = 0; - glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); while (y < sdl.draw.height) { if (!(index & 1)) { y += changedLines[index]; @@ -1152,15 +1225,17 @@ static void GUI_StartUp(Section * sec) { sdl.desktop.full.height = 0; if(fullresolution && *fullresolution) { char res[100]; - strncpy( res, fullresolution, sizeof( res )); + safe_strncpy( res, fullresolution, sizeof( res )); fullresolution = lowcase (res);//so x and X are allowed - if(strcmp(fullresolution,"original")) { + if (strcmp(fullresolution,"original")) { sdl.desktop.full.fixed = true; - char* height = const_cast(strchr(fullresolution,'x')); - if(height && * height) { - *height = 0; - sdl.desktop.full.height = (Bit16u)atoi(height+1); - sdl.desktop.full.width = (Bit16u)atoi(res); + if (strcmp(fullresolution,"desktop")) { //desktop = 0x0 + char* height = const_cast(strchr(fullresolution,'x')); + if (height && * height) { + *height = 0; + sdl.desktop.full.height = (Bit16u)atoi(height+1); + sdl.desktop.full.width = (Bit16u)atoi(res); + } } } } @@ -1170,7 +1245,7 @@ static void GUI_StartUp(Section * sec) { const char* windowresolution=section->Get_string("windowresolution"); if(windowresolution && *windowresolution) { char res[100]; - strncpy( res,windowresolution, sizeof( res )); + safe_strncpy( res,windowresolution, sizeof( res )); windowresolution = lowcase (res);//so x and X are allowed if(strcmp(windowresolution,"original")) { char* height = const_cast(strchr(windowresolution,'x')); @@ -1186,12 +1261,38 @@ static void GUI_StartUp(Section * sec) { #else sdl.desktop.doublebuf=section->Get_bool("fulldouble"); #endif +#if SDL_VERSION_ATLEAST(1, 2, 10) +#ifdef WIN32 + const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo(); + if (vidinfo) { + int sdl_w = vidinfo->current_w; + int sdl_h = vidinfo->current_h; + int win_w = GetSystemMetrics(SM_CXSCREEN); + int win_h = GetSystemMetrics(SM_CYSCREEN); + if (sdl_w != win_w && sdl_h != win_h) + LOG_MSG("Windows dpi/blurry apps scaling detected! The screen might be too large or not\n" + "show properly, please see the DOSBox options file (fullresolution) for details.\n"); + } +#else + if (!sdl.desktop.full.width || !sdl.desktop.full.height){ + //Can only be done on the very first call! Not restartable. + //On windows don't use it as SDL returns the values without taking in account the dpi scaling + const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo(); + if (vidinfo) { + sdl.desktop.full.width = vidinfo->current_w; + sdl.desktop.full.height = vidinfo->current_h; + } + } +#endif +#endif + if (!sdl.desktop.full.width) { #ifdef WIN32 sdl.desktop.full.width=(Bit16u)GetSystemMetrics(SM_CXSCREEN); #elif defined(HW_RVL) sdl.desktop.full.width=640; #else + LOG_MSG("Your fullscreen resolution can NOT be determined, it's assumed to be 1024x768.\nPlease edit the configuration file if this value is wrong."); sdl.desktop.full.width=1024; #endif } @@ -1207,7 +1308,10 @@ static void GUI_StartUp(Section * sec) { sdl.mouse.autoenable=section->Get_bool("autolock"); if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE); sdl.mouse.autolock=false; - sdl.mouse.sensitivity=section->Get_int("sensitivity"); + + Prop_multival* p3 = section->Get_multival("sensitivity"); + sdl.mouse.xsensitivity = p3->GetSection()->Get_int("xsens"); + sdl.mouse.ysensitivity = p3->GetSection()->Get_int("ysens"); std::string output=section->Get_string("output"); /* Setup Mouse correctly if fullscreen */ @@ -1215,7 +1319,7 @@ static void GUI_StartUp(Section * sec) { if (output == "surface") { sdl.desktop.want_type=SCREEN_SURFACE; -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW } else if (output == "ddraw") { sdl.desktop.want_type=SCREEN_SURFACE_DDRAW; #endif @@ -1230,36 +1334,36 @@ static void GUI_StartUp(Section * sec) { sdl.opengl.bilinear=false; #endif } else { - LOG_MSG("SDL:Unsupported output device %s, switching back to surface",output.c_str()); + LOG_MSG("SDL: Unsupported output device %s, switching back to surface",output.c_str()); sdl.desktop.want_type=SCREEN_SURFACE;//SHOULDN'T BE POSSIBLE anymore } sdl.overlay=0; #if C_OPENGL if(sdl.desktop.want_type==SCREEN_OPENGL){ /* OPENGL is requested */ - sdl.surface=SDL_SetVideoMode(640,400,0,SDL_OPENGL); + sdl.surface=SDL_SetVideoMode_Wrap(640,400,0,SDL_OPENGL); if (sdl.surface == NULL) { LOG_MSG("Could not initialize OpenGL, switching back to surface"); sdl.desktop.want_type=SCREEN_SURFACE; } else { + sdl.opengl.buffer=0; sdl.opengl.framebuf=0; sdl.opengl.texture=0; sdl.opengl.displaylist=0; glGetIntegerv (GL_MAX_TEXTURE_SIZE, &sdl.opengl.max_texsize); -#if defined(__WIN32__) && defined(NVIDIA_PixelDataRange) - glPixelDataRangeNV = (PFNGLPIXELDATARANGENVPROC) wglGetProcAddress("glPixelDataRangeNV"); - db_glAllocateMemoryNV = (PFNWGLALLOCATEMEMORYNVPROC) wglGetProcAddress("wglAllocateMemoryNV"); - db_glFreeMemoryNV = (PFNWGLFREEMEMORYNVPROC) wglGetProcAddress("wglFreeMemoryNV"); -#endif + glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)SDL_GL_GetProcAddress("glGenBuffersARB"); + glBindBufferARB = (PFNGLBINDBUFFERARBPROC)SDL_GL_GetProcAddress("glBindBufferARB"); + glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)SDL_GL_GetProcAddress("glDeleteBuffersARB"); + glBufferDataARB = (PFNGLBUFFERDATAARBPROC)SDL_GL_GetProcAddress("glBufferDataARB"); + glMapBufferARB = (PFNGLMAPBUFFERARBPROC)SDL_GL_GetProcAddress("glMapBufferARB"); + glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)SDL_GL_GetProcAddress("glUnmapBufferARB"); const char * gl_ext = (const char *)glGetString (GL_EXTENSIONS); if(gl_ext && *gl_ext){ - sdl.opengl.packed_pixel=(strstr(gl_ext,"EXT_packed_pixels") > 0); - sdl.opengl.paletted_texture=(strstr(gl_ext,"EXT_paletted_texture") > 0); -#if defined(NVIDIA_PixelDataRange) - sdl.opengl.pixel_data_range=(strstr(gl_ext,"GL_NV_pixel_data_range") >0 ) && - glPixelDataRangeNV && db_glAllocateMemoryNV && db_glFreeMemoryNV; - sdl.opengl.pixel_data_range = 0; -#endif + sdl.opengl.packed_pixel=(strstr(gl_ext,"EXT_packed_pixels") != NULL); + sdl.opengl.paletted_texture=(strstr(gl_ext,"EXT_paletted_texture") != NULL); + sdl.opengl.pixel_buffer_object=(strstr(gl_ext,"GL_ARB_pixel_buffer_object") != NULL ) && + glGenBuffersARB && glBindBufferARB && glDeleteBuffersARB && glBufferDataARB && + glMapBufferARB && glUnmapBufferARB; } else { sdl.opengl.packed_pixel=sdl.opengl.paletted_texture=false; } @@ -1271,12 +1375,12 @@ static void GUI_StartUp(Section * sec) { #ifdef HW_RVL sdl.surface=SDL_SetVideoMode(640,480,16,0); #else - sdl.surface=SDL_SetVideoMode(640,400,0,0); + sdl.surface=SDL_SetVideoMode_Wrap(640,400,0,0); #endif if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError()); sdl.desktop.bpp=sdl.surface->format->BitsPerPixel; if (sdl.desktop.bpp==24) { - LOG_MSG("SDL:You are running in 24 bpp mode, this will slow down things!"); + LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!"); } GFX_Stop(); SDL_WM_SetCaption("DOSBox",VERSION); @@ -1362,7 +1466,7 @@ static void GUI_StartUp(Section * sec) { #if C_DEBUG /* Pause binds with activate-debugger */ #else - MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause"); + MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause DBox"); #endif /* Get Keyboard state of numlock and capslock */ SDLMod keystate = SDL_GetModState(); @@ -1381,10 +1485,10 @@ void Mouse_AutoLock(bool enable) { static void HandleMouseMotion(SDL_MouseMotionEvent * motion) { if (sdl.mouse.locked || !sdl.mouse.autoenable) - Mouse_CursorMoved((float)motion->xrel*sdl.mouse.sensitivity/100.0f, - (float)motion->yrel*sdl.mouse.sensitivity/100.0f, - (float)(motion->x-sdl.clip.x)/(sdl.clip.w-1)*sdl.mouse.sensitivity/100.0f, - (float)(motion->y-sdl.clip.y)/(sdl.clip.h-1)*sdl.mouse.sensitivity/100.0f, + Mouse_CursorMoved((float)motion->xrel*sdl.mouse.xsensitivity/100.0f, + (float)motion->yrel*sdl.mouse.ysensitivity/100.0f, + (float)(motion->x-sdl.clip.x)/(sdl.clip.w-1)*sdl.mouse.xsensitivity/100.0f, + (float)(motion->y-sdl.clip.y)/(sdl.clip.h-1)*sdl.mouse.ysensitivity/100.0f, sdl.mouse.locked); } @@ -1393,7 +1497,7 @@ static void HandleMouseButton(SDL_MouseButtonEvent * button) { case SDL_PRESSED: if (sdl.mouse.requestlock && !sdl.mouse.locked) { GFX_CaptureMouse(); - // Dont pass klick to mouse handler + // Don't pass click to mouse handler break; } if (!sdl.mouse.autoenable && sdl.mouse.autolock && button->button == SDL_BUTTON_MIDDLE) { @@ -1438,6 +1542,19 @@ bool GFX_IsFullscreen(void) { return sdl.desktop.fullscreen; } +#if defined(MACOSX) +#define DB_POLLSKIP 3 +#else +//Not used yet, see comment below +#define DB_POLLSKIP 1 +#endif + +#if defined(LINUX) +#define SDL_XORG_FIX 1 +#else +#define SDL_XORG_FIX 0 +#endif + void GFX_Events() { #ifdef HW_RVL // check for home button @@ -1450,21 +1567,48 @@ void GFX_Events() { } #endif + //Don't poll too often. This can be heavy on the OS, especially Macs. + //In idle mode 3000-4000 polls are done per second without this check. + //Macs, with this code, max 250 polls per second. (non-macs unused default max 500) + //Currently not implemented for all platforms, given the ALT-TAB stuff for WIN32. +#if defined (MACOSX) + static int last_check = 0; + int current_check = GetTicks(); + if (current_check - last_check <= DB_POLLSKIP) return; + last_check = current_check; +#endif + SDL_Event event; #if defined (REDUCE_JOYSTICK_POLLING) - static int poll_delay=0; - int time=GetTicks(); - if (time-poll_delay>20) { - poll_delay=time; - if (sdl.num_joysticks>0) SDL_JoystickUpdate(); + static int poll_delay = 0; + int time = GetTicks(); + if (time - poll_delay > 20) { + poll_delay = time; + if (sdl.num_joysticks > 0) SDL_JoystickUpdate(); MAPPER_UpdateJoysticks(); } #endif while (SDL_PollEvent(&event)) { +#if SDL_XORG_FIX + // Special code for broken SDL with Xorg 1.20.1, where pairs of inputfocus gain and loss events are generated + // when locking the mouse in windowed mode. + if (event.type == SDL_ACTIVEEVENT && event.active.state == SDL_APPINPUTFOCUS && event.active.gain == 0) { + SDL_Event test; //Check if the next event would undo this one. + if (SDL_PeepEvents(&test,1,SDL_PEEKEVENT,SDL_ACTIVEEVENTMASK) == 1 && test.active.state == SDL_APPINPUTFOCUS && test.active.gain == 1) { + // Skip both events. + SDL_PeepEvents(&test,1,SDL_GETEVENT,SDL_ACTIVEEVENTMASK); + continue; + } + } +#endif + switch (event.type) { case SDL_ACTIVEEVENT: if (event.active.state & SDL_APPINPUTFOCUS) { if (event.active.gain) { +#ifdef WIN32 + if (!sdl.desktop.fullscreen) sdl.focus_ticks = GetTicks(); +#endif if (sdl.desktop.fullscreen && !sdl.mouse.locked) GFX_CaptureMouse(); SetPriority(sdl.priority.focus); @@ -1556,15 +1700,19 @@ void GFX_Events() { if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type; if (((event.key.keysym.sym==SDLK_TAB)) && ((sdl.laltstate==SDL_KEYDOWN) || (sdl.raltstate==SDL_KEYDOWN))) break; + // This can happen as well. + if (((event.key.keysym.sym == SDLK_TAB )) && (event.key.keysym.mod & KMOD_ALT)) break; + // ignore tab events that arrive just after regaining focus. (likely the result of alt-tab) + if ((event.key.keysym.sym == SDLK_TAB) && (GetTicks() - sdl.focus_ticks < 2)) break; #endif -#if defined (MACOSX) +#if defined (MACOSX) case SDL_KEYDOWN: case SDL_KEYUP: /* On macs CMD-Q is the default key to close an application */ if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) { KillSwitch(true); break; - } + } #endif default: void MAPPER_CheckEvent(SDL_Event * event); @@ -1595,12 +1743,14 @@ static BOOL WINAPI ConsoleEventHandler(DWORD event) { static bool no_stdout = false; void GFX_ShowMsg(char const* format,...) { char buf[512]; + va_list msg; va_start(msg,format); - vsprintf(buf,format,msg); - strcat(buf,"\n"); + vsnprintf(buf,sizeof(buf),format,msg); va_end(msg); - if(!no_stdout) printf("%s",buf); //Else buf is parsed again. + + buf[sizeof(buf) - 1] = '\0'; + if (!no_stdout) puts(buf); //Else buf is parsed again. (puts adds end of line) } @@ -1618,7 +1768,7 @@ void Config_Add_SDL() { Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false); #endif Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)"); - + #ifdef HW_RVL Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,true); #else @@ -1627,20 +1777,22 @@ void Config_Add_SDL() { Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox."); Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"original"); - Pstring->Set_help("What resolution to use for fullscreen: original or fixed size (e.g. 1024x768).\n" - " Using your monitor's native resolution with aspect=true might give the best results.\n" - " If you end up with small window on a large screen, try an output different from surface."); + Pstring->Set_help("What resolution to use for fullscreen: original, desktop or a fixed size (e.g. 1024x768).\n" + "Using your monitor's native resolution with aspect=true might give the best results.\n" + "If you end up with small window on a large screen, try an output different from surface." + "On Windows 10 with display scaling (Scale and layout) set to a value above 100%, it is recommended\n" + "to use a lower full/windowresolution, in order to avoid window size problems."); Pstring = sdl_sec->Add_string("windowresolution",Property::Changeable::Always,"original"); Pstring->Set_help("Scale the window to this size IF the output device supports hardware scaling.\n" - " (output=surface does not!)"); + "(output=surface does not!)"); const char* outputs[] = { "surface", "overlay", #if C_OPENGL "opengl", "openglnb", #endif -#if (HAVE_DDRAW_H) && defined(WIN32) +#if C_DDRAW "ddraw", #endif 0 }; @@ -1651,9 +1803,13 @@ void Config_Add_SDL() { Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always,true); Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)"); - Pint = sdl_sec->Add_int("sensitivity",Property::Changeable::Always,100); - Pint->SetMinMax(1,1000); - Pint->Set_help("Mouse sensitivity."); + Pmulti = sdl_sec->Add_multi("sensitivity",Property::Changeable::Always, ","); + Pmulti->Set_help("Mouse sensitivity. The optional second parameter specifies vertical sensitivity (e.g. 100,-50)."); + Pmulti->SetValue("100"); + Pint = Pmulti->GetSection()->Add_int("xsens",Property::Changeable::Always,100); + Pint->SetMinMax(-1000,1000); + Pint = Pmulti->GetSection()->Add_int("ysens",Property::Changeable::Always,100); + Pint->SetMinMax(-1000,1000); Pbool = sdl_sec->Add_bool("waitonerror",Property::Changeable::Always, true); Pbool->Set_help("Wait before closing the console if dosbox has an error."); @@ -1661,7 +1817,7 @@ void Config_Add_SDL() { Pmulti = sdl_sec->Add_multi("priority", Property::Changeable::Always, ","); Pmulti->SetValue("higher,normal"); Pmulti->Set_help("Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized.\n" - " pause is only valid for the second entry."); + "pause is only valid for the second entry."); const char* actt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0}; Pstring = Pmulti->GetSection()->Add_string("active",Property::Changeable::Always,"higher"); @@ -1672,7 +1828,7 @@ void Config_Add_SDL() { Pstring->Set_values(inactt); Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE); - Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the defaul value."); + Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the default value."); Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,true); Pbool->Set_help("Avoid usage of symkeys, might not work on all operating systems."); @@ -1685,9 +1841,9 @@ static void show_warning(char const * const message) { if ( !sdl.inited && SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE) < 0 ) textonly = true; sdl.inited = true; #endif - printf(message); + printf("%s",message); if(textonly) return; - if(!sdl.surface) sdl.surface = SDL_SetVideoMode(640,400,0,0); + if(!sdl.surface) sdl.surface = SDL_SetVideoMode_Wrap(640,400,0,0); if(!sdl.surface) return; #if SDL_BYTEORDER == SDL_BIG_ENDIAN Bit32u rmask = 0xff000000; @@ -1695,7 +1851,7 @@ static void show_warning(char const * const message) { Bit32u bmask = 0x0000ff00; #else Bit32u rmask = 0x000000ff; - Bit32u gmask = 0x0000ff00; + Bit32u gmask = 0x0000ff00; Bit32u bmask = 0x00ff0000; #endif SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0); @@ -1704,22 +1860,22 @@ static void show_warning(char const * const message) { int x = 120,y = 20; std::string m(message),m2; std::string::size_type a,b,c,d; - + while(m.size()) { //Max 50 characters. break on space before or on a newline c = m.find('\n'); d = m.rfind(' ',50); if(c>d) a=b=d; else a=b=c; - if( a != std::string::npos) b++; + if( a != std::string::npos) b++; m2 = m.substr(0,a); m.erase(0,b); OutputString(x,y,m2.c_str(),0xffffffff,0,splash_surf); y += 20; } - + SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL); SDL_Flip(sdl.surface); SDL_Delay(12000); } - + static void launcheditor() { std::string path,file; Cross::CreatePlatformConfigDir(path); @@ -1747,11 +1903,11 @@ extern void DEBUG_ShutDown(Section * /*sec*/); #endif void restart_program(std::vector & parameters) { - char** newargs = new char* [parameters.size()+1]; + char** newargs = new char* [parameters.size() + 1]; // parameter 0 is the executable path // contents of the vector follow // last one is NULL - for(Bitu i = 0; i < parameters.size(); i++) newargs[i]=(char*)parameters[i].c_str(); + for(Bitu i = 0; i < parameters.size(); i++) newargs[i] = (char*)parameters[i].c_str(); newargs[parameters.size()] = NULL; SDL_CloseAudio(); SDL_Delay(50); @@ -1762,9 +1918,20 @@ void restart_program(std::vector & parameters) { #endif #ifndef HW_RVL - execvp(newargs[0], newargs); + if(execvp(newargs[0], newargs) == -1) { +#ifdef WIN32 + if(newargs[0][0] == '\"') { + //everything specifies quotes around it if it contains a space, however my system disagrees + std::string edit = parameters[0]; + edit.erase(0,1);edit.erase(edit.length() - 1,1); + //However keep the first argument of the passed argv (newargs) with quotes, as else repeated restarts go wrong. + if(execvp(edit.c_str(), newargs) == -1) E_Exit("Restarting failed"); + } #endif - free(newargs); + E_Exit("Restarting failed"); + } +#endif + delete [] newargs; } void Restart(bool pressed) { // mapper handler restart_program(control->startup_params); @@ -1802,7 +1969,7 @@ static void printconfiglocation() { Cross::CreatePlatformConfigDir(path); Cross::GetPlatformConfigName(file); path += file; - + FILE* f = fopen(path.c_str(),"r"); if(!f && !control->PrintConfig(path.c_str())) { printf("tried creating %s. but failed",path.c_str()); @@ -1856,9 +2023,9 @@ int MountDOSBoxDir(char DriveLetter, const char *path); int main(int argc, char* argv[]) { try { #ifdef HW_RVL - WiiInit(); - if(argc > 0 && argv[0] != NULL) - CreateAppPath(argv[0]); + WiiInit(); + if(argc > 0 && argv[0] != NULL) + CreateAppPath(argv[0]); #endif CommandLine com_line(argc,argv); Config myconf(&com_line); @@ -1874,7 +2041,7 @@ int main(int argc, char* argv[]) { if(control->cmdline->FindExist("-resetconf")) eraseconfigfile(); if(control->cmdline->FindExist("-erasemapper")) erasemapperfile(); if(control->cmdline->FindExist("-resetmapper")) erasemapperfile(); - + /* Can't disable the console with debugger enabled */ #if defined(WIN32) && !(C_DEBUG) if (control->cmdline->FindExist("-noconsole")) { @@ -1899,7 +2066,7 @@ int main(int argc, char* argv[]) { #endif //defined(WIN32) && !(C_DEBUG) if (control->cmdline->FindExist("-version") || control->cmdline->FindExist("--version") ) { - printf("\nDOSBox version %s, copyright 2002-2011 DOSBox Team.\n\n",VERSION); + printf("\nDOSBox version %s, copyright 2002-2019 DOSBox Team.\n\n",VERSION); printf("DOSBox is written by the DOSBox Team (See AUTHORS file))\n"); printf("DOSBox comes with ABSOLUTELY NO WARRANTY. This is free software,\n"); printf("and you are welcome to redistribute it under certain conditions;\n"); @@ -1927,7 +2094,7 @@ int main(int argc, char* argv[]) { /* Display Welcometext in the console */ LOG_MSG("DOSBox version %s",VERSION); - LOG_MSG("Copyright 2002-2011 DOSBox Team, published under GNU GPL."); + LOG_MSG("Copyright 2002-2019 DOSBox Team, published under GNU GPL."); LOG_MSG("---"); /* Init SDL */ @@ -1938,13 +2105,15 @@ int main(int argc, char* argv[]) { */ putenv(const_cast("SDL_DISABLE_LOCK_KEYS=1")); #endif - if ( SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_CDROM + // Don't init timers, GetTicks seems to work fine and they can use a fair amount of power (Macs again) + // Please report problems with audio and other things. + if ( SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO | /*SDL_INIT_TIMER |*/ SDL_INIT_CDROM |SDL_INIT_NOPARACHUTE ) < 0 ) E_Exit("Can't init SDL %s",SDL_GetError()); sdl.inited = true; #ifndef DISABLE_JOYSTICK - //Initialise Joystick seperately. This way we can warn when it fails instead + //Initialise Joystick separately. This way we can warn when it fails instead //of exiting the application if( SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0 ) LOG_MSG("Failed to init joystick support"); #endif @@ -1984,8 +2153,7 @@ int main(int argc, char* argv[]) { sdl.num_joysticks=SDL_NumJoysticks(); /* Parse configuration files */ - std::string config_file,config_path; - + std::string config_file, config_path, config_combined; Cross::GetPlatformConfigDir(config_path); //First parse -userconf @@ -1993,27 +2161,29 @@ int main(int argc, char* argv[]) { config_file.clear(); Cross::GetPlatformConfigDir(config_path); Cross::GetPlatformConfigName(config_file); - config_path += config_file; - control->ParseConfigFile(config_path.c_str()); + config_combined = config_path + config_file; + control->ParseConfigFile(config_combined.c_str()); if(!control->configfiles.size()) { //Try to create the userlevel configfile. config_file.clear(); Cross::CreatePlatformConfigDir(config_path); Cross::GetPlatformConfigName(config_file); - config_path += config_file; - if(control->PrintConfig(config_path.c_str())) { - LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_path.c_str()); + config_combined = config_path + config_file; + if(control->PrintConfig(config_combined.c_str())) { + LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_combined.c_str()); //Load them as well. Makes relative paths much easier - control->ParseConfigFile(config_path.c_str()); + control->ParseConfigFile(config_combined.c_str()); } } } //Second parse -conf switches while(control->cmdline->FindString("-conf",config_file,true)) { - if(!control->ParseConfigFile(config_file.c_str())) { + if (!control->ParseConfigFile(config_file.c_str())) { // try to load it from the user directory - control->ParseConfigFile((config_path + config_file).c_str()); + if (!control->ParseConfigFile((config_path + config_file).c_str())) { + LOG_MSG("CONFIG: Can't open specified config file: %s",config_file.c_str()); + } } } // if none found => parse localdir conf @@ -2031,11 +2201,11 @@ int main(int argc, char* argv[]) { config_file.clear(); Cross::CreatePlatformConfigDir(config_path); Cross::GetPlatformConfigName(config_file); - config_path += config_file; - if(control->PrintConfig(config_path.c_str())) { - LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_path.c_str()); + config_combined = config_path + config_file; + if(control->PrintConfig(config_combined.c_str())) { + LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_combined.c_str()); //Load them as well. Makes relative paths much easier - control->ParseConfigFile(config_path.c_str()); + control->ParseConfigFile(config_combined.c_str()); } else { LOG_MSG("CONFIG: Using default settings. Create a configfile to change them"); } @@ -2087,23 +2257,18 @@ int main(int argc, char* argv[]) { } catch (int){ - ;//nothing pressed killswitch + ; //nothing, pressed killswitch } catch(...){ -#if defined (WIN32) - sticky_keys(true); -#endif - //Force visible mouse to end user. Somehow this sometimes doesn't happen - SDL_WM_GrabInput(SDL_GRAB_OFF); - SDL_ShowCursor(SDL_ENABLE); - throw;//dunno what happened. rethrow for sdl to catch + ; // Unknown error, let's just exit. } #if defined (WIN32) sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt -#endif +#endif //Force visible mouse to end user. Somehow this sometimes doesn't happen SDL_WM_GrabInput(SDL_GRAB_OFF); SDL_ShowCursor(SDL_ENABLE); + #ifdef HW_RVL WiiFinished(); #endif diff --git a/src/hardware/Makefile.am b/src/hardware/Makefile.am index 1ec7e67..2d73948 100644 --- a/src/hardware/Makefile.am +++ b/src/hardware/Makefile.am @@ -1,6 +1,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -SUBDIRS = serialport +SUBDIRS = serialport mame EXTRA_DIST = opl.cpp opl.h adlib.h dbopl.h pci_devices.h diff --git a/src/hardware/adlib.cpp b/src/hardware/adlib.cpp index dfa603d..e0b082f 100644 --- a/src/hardware/adlib.cpp +++ b/src/hardware/adlib.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -28,6 +28,13 @@ #include "mem.h" #include "dbopl.h" +#include "mame/emu.h" +#include "mame/fmopl.h" +#include "mame/ymf262.h" + +#define OPL2_INTERNAL_FREQ 3600000 // The OPL2 operates at 3.6MHz +#define OPL3_INTERNAL_FREQ 14400000 // The OPL3 operates at 14.4MHz + namespace OPL2 { #include "opl.cpp" @@ -85,6 +92,80 @@ namespace OPL3 { }; } +namespace MAMEOPL2 { + +struct Handler : public Adlib::Handler { + void* chip; + + virtual void WriteReg(Bit32u reg, Bit8u val) { + ym3812_write(chip, 0, reg); + ym3812_write(chip, 1, val); + } + virtual Bit32u WriteAddr(Bit32u port, Bit8u val) { + return val; + } + virtual void Generate(MixerChannel* chan, Bitu samples) { + Bit16s buf[1024 * 2]; + while (samples > 0) { + Bitu todo = samples > 1024 ? 1024 : samples; + samples -= todo; + ym3812_update_one(chip, buf, todo); + chan->AddSamples_m16(todo, buf); + } + } + virtual void Init(Bitu rate) { + chip = ym3812_init(0, OPL2_INTERNAL_FREQ, rate); + } + ~Handler() { + ym3812_shutdown(chip); + } +}; + +} + + +namespace MAMEOPL3 { + +struct Handler : public Adlib::Handler { + void* chip; + + virtual void WriteReg(Bit32u reg, Bit8u val) { + ymf262_write(chip, 0, reg); + ymf262_write(chip, 1, val); + } + virtual Bit32u WriteAddr(Bit32u port, Bit8u val) { + return val; + } + virtual void Generate(MixerChannel* chan, Bitu samples) { + //We generate data for 4 channels, but only the first 2 are connected on a pc + Bit16s buf[4][1024]; + Bit16s result[1024][2]; + Bit16s* buffers[4] = { buf[0], buf[1], buf[2], buf[3] }; + + while (samples > 0) { + Bitu todo = samples > 1024 ? 1024 : samples; + samples -= todo; + ymf262_update_one(chip, buffers, todo); + //Interleave the samples before mixing + for (Bitu i = 0; i < todo; i++) { + result[i][0] = buf[0][i]; + result[i][1] = buf[1][i]; + } + chan->AddSamples_s16(todo, result[0]); + } + } + virtual void Init(Bitu rate) { + chip = ymf262_init(0, OPL3_INTERNAL_FREQ, rate); + } + ~Handler() { + ymf262_shutdown(chip); + } +}; + +} + + + #define RAW_SIZE 1024 @@ -440,6 +521,36 @@ void Module::DualWrite( Bit8u index, Bit8u reg, Bit8u val ) { CacheWrite( fullReg, val ); } +void Module::CtrlWrite( Bit8u val ) { + switch ( ctrl.index ) { + case 0x09: /* Left FM Volume */ + ctrl.lvol = val; + goto setvol; + case 0x0a: /* Right FM Volume */ + ctrl.rvol = val; +setvol: + if ( ctrl.mixer ) { + //Dune cdrom uses 32 volume steps in an apparent mistake, should be 128 + mixerChan->SetVolume( (float)(ctrl.lvol&0x1f)/31.0f, (float)(ctrl.rvol&0x1f)/31.0f ); + } + break; + } +} + +Bitu Module::CtrlRead( void ) { + switch ( ctrl.index ) { + case 0x00: /* Board Options */ + return 0x70; //No options installed + case 0x09: /* Left FM Volume */ + return ctrl.lvol; + case 0x0a: /* Right FM Volume */ + return ctrl.rvol; + case 0x15: /* Audio Relocation */ + return 0x388 >> 3; //Cryo installer detection + } + return 0xff; +} + void Module::PortWrite( Bitu port, Bitu val, Bitu iolen ) { //Keep track of last write time @@ -450,6 +561,14 @@ void Module::PortWrite( Bitu port, Bitu val, Bitu iolen ) { } if ( port&1 ) { switch ( mode ) { + case MODE_OPL3GOLD: + if ( port == 0x38b ) { + if ( ctrl.active ) { + CtrlWrite( val ); + break; + } + } + //Fall-through if not handled by control chip case MODE_OPL2: case MODE_OPL3: if ( !chip[0].Write( reg.normal, val ) ) { @@ -476,6 +595,20 @@ void Module::PortWrite( Bitu port, Bitu val, Bitu iolen ) { case MODE_OPL2: reg.normal = handler->WriteAddr( port, val ) & 0xff; break; + case MODE_OPL3GOLD: + if ( port == 0x38a ) { + if ( val == 0xff ) { + ctrl.active = true; + break; + } else if ( val == 0xfe ) { + ctrl.active = false; + break; + } else if ( ctrl.active ) { + ctrl.index = val & 0xff; + break; + } + } + //Fall-through if not handled by control chip case MODE_OPL3: reg.normal = handler->WriteAddr( port, val ) & 0x1ff; break; @@ -504,6 +637,15 @@ Bitu Module::PortRead( Bitu port, Bitu iolen ) { } else { return 0xff; } + case MODE_OPL3GOLD: + if ( ctrl.active ) { + if ( port == 0x38a ) { + return 0; //Control status, not busy + } else if ( port == 0x38b ) { + return CtrlRead(); + } + } + //Fall-through if not handled by control chip case MODE_OPL3: //We allocated 4 ports, so just return -1 for the higher ones if ( !(port & 3 ) ) { @@ -527,6 +669,7 @@ void Module::Init( Mode m ) { mode = m; switch ( mode ) { case MODE_OPL3: + case MODE_OPL3GOLD: case MODE_OPL2: break; case MODE_DUALOPL2: @@ -627,6 +770,10 @@ Module::Module( Section* configuration ) : Module_base(configuration) { reg.dual[0] = 0; reg.dual[1] = 0; reg.normal = 0; + ctrl.active = false; + ctrl.index = 0; + ctrl.lvol = 0xff; + ctrl.rvol = 0xff; handler = 0; capture = 0; @@ -637,9 +784,12 @@ Module::Module( Section* configuration ) : Module_base(configuration) { if ( rate < 8000 ) rate = 8000; std::string oplemu( section->Get_string( "oplemu" ) ); + ctrl.mixer = section->Get_bool("sbmixer"); mixerChan = mixerObject.Install(OPL_CallBack,rate,"FM"); - mixerChan->SetScale( 2.0 ); + //Used to be 2.0, which was measured to be too high. Exact value depends on card/clone. + mixerChan->SetScale( 1.5f ); + if (oplemu == "fast") { handler = new DBOPL::Handler(); } else if (oplemu == "compat") { @@ -648,6 +798,14 @@ Module::Module( Section* configuration ) : Module_base(configuration) { } else { handler = new OPL3::Handler(); } + } + else if (oplemu == "mame") { + if (oplmode == OPL_opl2) { + handler = new MAMEOPL2::Handler(); + } + else { + handler = new MAMEOPL3::Handler(); + } } else { handler = new DBOPL::Handler(); } @@ -664,6 +822,9 @@ Module::Module( Section* configuration ) : Module_base(configuration) { case OPL_opl3: Init( Adlib::MODE_OPL3 ); break; + case OPL_opl3gold: + Init( Adlib::MODE_OPL3GOLD ); + break; } //0x388 range WriteHandler[0].Install(0x388,OPL_Write,IO_MB, 4 ); diff --git a/src/hardware/adlib.h b/src/hardware/adlib.h index 64bf2e9..e22d49e 100644 --- a/src/hardware/adlib.h +++ b/src/hardware/adlib.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -90,7 +90,8 @@ struct Chip { typedef enum { MODE_OPL2, MODE_DUALOPL2, - MODE_OPL3 + MODE_OPL3, + MODE_OPL3GOLD } Mode; class Handler { @@ -125,8 +126,17 @@ class Module: public Module_base { Bit32u normal; Bit8u dual[2]; } reg; + struct { + bool active; + Bit8u index; + Bit8u lvol; + Bit8u rvol; + bool mixer; + } ctrl; void CacheWrite( Bit32u reg, Bit8u val ); void DualWrite( Bit8u index, Bit8u reg, Bit8u val ); + void CtrlWrite( Bit8u val ); + Bitu CtrlRead( void ); public: static OPL_Mode oplmode; MixerChannel* mixerChan; diff --git a/src/hardware/cmos.cpp b/src/hardware/cmos.cpp index 279e97e..e909d38 100644 --- a/src/hardware/cmos.cpp +++ b/src/hardware/cmos.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/dbopl.cpp b/src/hardware/dbopl.cpp index 1723f31..26f4f23 100644 --- a/src/hardware/dbopl.cpp +++ b/src/hardware/dbopl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* @@ -37,6 +37,7 @@ #include #include #include +#include #include "dosbox.h" #include "dbopl.h" @@ -716,18 +717,23 @@ void Channel::WriteB0( const Chip* chip, Bit8u val ) { } } -void Channel::WriteC0( const Chip* chip, Bit8u val ) { +void Channel::WriteC0(const Chip* chip, Bit8u val) { Bit8u change = val ^ regC0; - if ( !change ) + if (!change) return; regC0 = val; - feedback = ( val >> 1 ) & 7; - if ( feedback ) { + feedback = (regC0 >> 1) & 7; + if (feedback) { //We shift the input to the right 10 bit wave index value feedback = 9 - feedback; - } else { + } + else { feedback = 31; } + UpdateSynth(chip); +} + +void Channel::UpdateSynth( const Chip* chip ) { //Select the new synth mode if ( chip->opl3Active ) { //4-op mode enabled for this channel @@ -761,20 +767,20 @@ void Channel::WriteC0( const Chip* chip, Bit8u val ) { } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) { //Regular dual op, am or fm - } else if ( val & 1 ) { + } else if (regC0 & 1 ) { synthHandler = &Channel::BlockTemplate< sm3AM >; } else { synthHandler = &Channel::BlockTemplate< sm3FM >; } - maskLeft = ( val & 0x10 ) ? -1 : 0; - maskRight = ( val & 0x20 ) ? -1 : 0; + maskLeft = (regC0 & 0x10 ) ? -1 : 0; + maskRight = (regC0 & 0x20 ) ? -1 : 0; //opl2 active } else { //Disable updating percussion channels if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { //Regular dual op, am or fm - } else if ( val & 1 ) { + } else if (regC0 & 1 ) { synthHandler = &Channel::BlockTemplate< sm2AM >; } else { synthHandler = &Channel::BlockTemplate< sm2FM >; @@ -782,12 +788,6 @@ void Channel::WriteC0( const Chip* chip, Bit8u val ) { } } -void Channel::ResetC0( const Chip* chip ) { - Bit8u val = regC0; - regC0 ^= 0xff; - WriteC0( chip, val ); -}; - template< bool opl3Mode> INLINE void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) { Channel* chan = this; @@ -1071,7 +1071,8 @@ void Chip::WriteBD( Bit8u val ) { //Toggle keyoffs when we turn off the percussion } else if ( change & 0x20 ) { //Trigger a reset to setup the original synth handler - chan[6].ResetC0( this ); + //This makes it call + chan[6].UpdateSynth( this ); chan[6].op[0].KeyOff( 0x2 ); chan[6].op[1].KeyOff( 0x2 ); chan[7].op[0].KeyOff( 0x2 ); @@ -1085,17 +1086,25 @@ void Chip::WriteBD( Bit8u val ) { #define REGOP( _FUNC_ ) \ index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ if ( OpOffsetTable[ index ] ) { \ - Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \ + Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ]-1 ); \ regOp->_FUNC_( this, val ); \ } #define REGCHAN( _FUNC_ ) \ index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ if ( ChanOffsetTable[ index ] ) { \ - Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \ + Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ]-1 ); \ regChan->_FUNC_( this, val ); \ } +//Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers +void Chip::UpdateSynths() { + for (int i = 0; i < 18; i++) { + chan[i].UpdateSynth(this); + } +} + + void Chip::WriteReg( Bit32u reg, Bit8u val ) { Bitu index; switch ( (reg & 0xf0) >> 4 ) { @@ -1108,15 +1117,16 @@ void Chip::WriteReg( Bit32u reg, Bit8u val ) { return; //Always keep the highest bit enabled, for checking > 0x80 reg104 = 0x80 | ( val & 0x3f ); + //Switch synths when changing the 4op combinations + UpdateSynths(); } else if ( reg == 0x105 ) { //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register if ( !((opl3Active ^ val) & 1 ) ) return; opl3Active = ( val & 1 ) ? 0xff : 0; - //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers - for ( int i = 0; i < 18;i++ ) { - chan[i].ResetC0( this ); - } + //Just tupdate the synths now that opl3 most have been enabled + //This isn't how the real card handles it but need to switch to stereo generating handlers + UpdateSynths(); } else if ( reg == 0x08 ) { reg08 = val; } @@ -1177,9 +1187,9 @@ void Chip::GenerateBlock2( Bitu total, Bit32s* output ) { while ( total > 0 ) { Bit32u samples = ForwardLFO( total ); memset(output, 0, sizeof(Bit32s) * samples); - int count = 0; +// int count = 0; for( Channel* ch = chan; ch < chan + 9; ) { - count++; +// count++; ch = (ch->*(ch->synthHandler))( this, samples, output ); } total -= samples; @@ -1191,9 +1201,9 @@ void Chip::GenerateBlock3( Bitu total, Bit32s* output ) { while ( total > 0 ) { Bit32u samples = ForwardLFO( total ); memset(output, 0, sizeof(Bit32s) * samples *2); - int count = 0; +// int count = 0; for( Channel* ch = chan; ch < chan + 18; ) { - count++; +// count++; ch = (ch->*(ch->synthHandler))( this, samples, output ); } total -= samples; @@ -1237,6 +1247,7 @@ void Chip::Setup( Bit32u rate ) { EnvelopeSelect( i, index, shift ); linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); } +// Bit32s attackDiffs[62]; //Generate the best matching attack rate for ( Bit8u i = 0; i < 62; i++ ) { Bit8u index, shift; @@ -1267,22 +1278,22 @@ void Chip::Setup( Bit32u rate ) { if ( lDiff < bestDiff ) { bestDiff = lDiff; bestAdd = guessAdd; + //We hit an exactly matching sample count if ( !bestDiff ) break; } + //Linear correction factor, not exactly perfect but seems to work + double correct = (original - diff) / (double)original; + guessAdd = (Bit32u)(guessAdd * correct); //Below our target if ( diff < 0 ) { - //Better than the last time - Bit32s mul = ((original - diff) << 12) / original; - guessAdd = ((guessAdd * mul) >> 12); + //Always add one here for rounding, an overshoot will get corrected by another pass decreasing guessAdd++; - } else if ( diff > 0 ) { - Bit32s mul = ((original - diff) << 12) / original; - guessAdd = (guessAdd * mul) >> 12; - guessAdd--; } } attackRates[i] = bestAdd; + //Keep track of the diffs for some debugging +// attackDiffs[i] = bestDiff; } for ( Bit8u i = 62; i < 76; i++ ) { //This should provide instant volume maximizing @@ -1420,7 +1431,6 @@ void InitTables( void ) { TremoloTable[TREMOLO_TABLE - 1 - i] = val; } //Create a table with offsets of the channels from the start of the chip - DBOPL::Chip* chip = 0; for ( Bitu i = 0; i < 32; i++ ) { Bitu index = i & 0xf; if ( index >= 9 ) { @@ -1434,8 +1444,7 @@ void InitTables( void ) { //Add back the bits for highest ones if ( i >= 16 ) index += 9; - Bitu blah = reinterpret_cast( &(chip->chan[ index ]) ); - ChanOffsetTable[i] = blah; + ChanOffsetTable[i] = 1+(Bit16u)(index*sizeof(DBOPL::Channel)); } //Same for operators for ( Bitu i = 0; i < 64; i++ ) { @@ -1448,16 +1457,15 @@ void InitTables( void ) { if ( chNum >= 12 ) chNum += 16 - 12; Bitu opNum = ( i % 8 ) / 3; - DBOPL::Channel* chan = 0; - Bitu blah = reinterpret_cast( &(chan->op[opNum]) ); - OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah; + OpOffsetTable[i] = ChanOffsetTable[chNum]+(Bit16u)(opNum*sizeof(DBOPL::Operator)); } #if 0 + DBOPL::Chip* chip = 0; //Stupid checks if table's are correct for ( Bitu i = 0; i < 18; i++ ) { Bit32u find = (Bit16u)( &(chip->chan[ i ]) ); for ( Bitu c = 0; c < 32; c++ ) { - if ( ChanOffsetTable[c] == find ) { + if ( ChanOffsetTable[c] == find+1 ) { find = 0; break; } @@ -1469,7 +1477,7 @@ void InitTables( void ) { for ( Bitu i = 0; i < 36; i++ ) { Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) ); for ( Bitu c = 0; c < 64; c++ ) { - if ( OpOffsetTable[c] == find ) { + if ( OpOffsetTable[c] == find+1 ) { find = 0; break; } diff --git a/src/hardware/dbopl.h b/src/hardware/dbopl.h index 624df43..97dc7ca 100644 --- a/src/hardware/dbopl.h +++ b/src/hardware/dbopl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "adlib.h" @@ -156,7 +156,7 @@ public: }; struct Channel { - Operator op[2]; + Operator op[2]; //Leave on top of struct for simpler pointer math. inline Operator* Op( Bitu index ) { return &( ( this + (index >> 1) )->op[ index & 1 ]); } @@ -176,10 +176,10 @@ struct Channel { void SetChanData( const Chip* chip, Bit32u data ); //Change in the chandata, check for new values and if we have to forward to operators void UpdateFrequency( const Chip* chip, Bit8u fourOp ); + void UpdateSynth(const Chip* chip); void WriteA0( const Chip* chip, Bit8u val ); void WriteB0( const Chip* chip, Bit8u val ); void WriteC0( const Chip* chip, Bit8u val ); - void ResetC0( const Chip* chip ); //call this for the first channel template< bool opl3Mode > @@ -192,6 +192,9 @@ struct Channel { }; struct Chip { + //18 channels with 2 operators each. Leave on top of struct for simpler pointer math. + Channel chan[18]; + //This is used as the base counter for vibrato and tremolo Bit32u lfoCounter; Bit32u lfoAdd; @@ -208,9 +211,6 @@ struct Chip { //Best match attack rates for the rate of this chip Bit32u attackRates[76]; - //18 channels with 2 operators each - Channel chan[18]; - Bit8u reg104; Bit8u reg08; Bit8u reg04; @@ -239,6 +239,8 @@ struct Chip { void GenerateBlock2( Bitu samples, Bit32s* output ); void GenerateBlock3( Bitu samples, Bit32s* output ); + //Update the synth handlers in all channels + void UpdateSynths(); void Generate( Bit32u samples ); void Setup( Bit32u r ); diff --git a/src/hardware/disney.cpp b/src/hardware/disney.cpp index 52d7b8c..629d94b 100644 --- a/src/hardware/disney.cpp +++ b/src/hardware/disney.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -69,11 +69,9 @@ static void DISNEY_disable(Bitu) { if(disney.mo) { disney.chan->AddSilence(); disney.chan->Enable(false); - delete disney.mo; } disney.leader = 0; disney.last_used = 0; - disney.mo = 0; disney.state = DS_IDLE; disney.interface_det = 0; disney.interface_det_ext = 0; @@ -90,8 +88,7 @@ static void DISNEY_enable(Bitu freq) { if(disney.stereo) LOG(LOG_MISC,LOG_NORMAL)("disney enable %d Hz, stereo",freq); else LOG(LOG_MISC,LOG_NORMAL)("disney enable %d Hz, mono",freq); #endif - disney.mo = new MixerObject(); - disney.chan=disney.mo->Install(&DISNEY_CallBack,freq,"DISNEY"); + disney.chan->SetFreq(freq); disney.chan->Enable(true); disney.state = DS_RUNNING; } @@ -377,11 +374,16 @@ public: disney.control=0; disney.last_used=0; - disney.mo=0; + disney.mo = new MixerObject(); + disney.chan=disney.mo->Install(&DISNEY_CallBack,10000,"DISNEY"); DISNEY_disable(0); + + } ~DISNEY(){ DISNEY_disable(0); + if (disney.mo) + delete disney.mo; } }; diff --git a/src/hardware/dma.cpp b/src/hardware/dma.cpp index 1346e73..bf8ecba 100644 --- a/src/hardware/dma.cpp +++ b/src/hardware/dma.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -31,6 +31,8 @@ DmaController *DmaControllers[2]; #define EMM_PAGEFRAME4K ((0xE000*16)/4096) Bit32u ems_board_mapping[LINK_START]; +static Bit32u dma_wrapping = 0xffff; + static void UpdateEMSMapping(void) { /* if EMS is not present, this will result in a 1:1 mapping */ Bitu i; @@ -48,7 +50,7 @@ static void DMA_BlockRead(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u Bit32u dma_wrap = ((0xffff<(dma_wrapping<> 12); @@ -69,7 +71,7 @@ static void DMA_BlockWrite(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8 Bit32u dma_wrap = ((0xffff<(dma_wrapping<> 12); @@ -107,6 +109,7 @@ bool SecondDMAControllerAvailable(void) { } static void DMA_Write_Port(Bitu port,Bitu val,Bitu /*iolen*/) { + //LOG(LOG_DMACONTROL,LOG_ERROR)("Write %" sBitfs(X) " %" sBitfs(X),port,val); if (port<0x10) { /* write to the first DMA controller (channels 0-3) */ DmaControllers[0]->WriteControllerReg(port,val,1); @@ -120,6 +123,7 @@ static void DMA_Write_Port(Bitu port,Bitu val,Bitu /*iolen*/) { case 0x81:GetDMAChannel(2)->SetPage((Bit8u)val);break; case 0x82:GetDMAChannel(3)->SetPage((Bit8u)val);break; case 0x83:GetDMAChannel(1)->SetPage((Bit8u)val);break; + case 0x87:GetDMAChannel(0)->SetPage((Bit8u)val);break; case 0x89:GetDMAChannel(6)->SetPage((Bit8u)val);break; case 0x8a:GetDMAChannel(7)->SetPage((Bit8u)val);break; case 0x8b:GetDMAChannel(5)->SetPage((Bit8u)val);break; @@ -128,6 +132,7 @@ static void DMA_Write_Port(Bitu port,Bitu val,Bitu /*iolen*/) { } static Bitu DMA_Read_Port(Bitu port,Bitu iolen) { + //LOG(LOG_DMACONTROL,LOG_ERROR)("Read %" sBitfs(X),port); if (port<0x10) { /* read from the first DMA controller (channels 0-3) */ return DmaControllers[0]->ReadControllerReg(port,iolen); @@ -139,6 +144,7 @@ static Bitu DMA_Read_Port(Bitu port,Bitu iolen) { case 0x81:return GetDMAChannel(2)->pagenum; case 0x82:return GetDMAChannel(3)->pagenum; case 0x83:return GetDMAChannel(1)->pagenum; + case 0x87:return GetDMAChannel(0)->pagenum; case 0x89:return GetDMAChannel(6)->pagenum; case 0x8a:return GetDMAChannel(7)->pagenum; case 0x8b:return GetDMAChannel(5)->pagenum; @@ -252,7 +258,7 @@ Bitu DmaController::ReadControllerReg(Bitu reg,Bitu /*len*/) { } return ret; default: - LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to read undefined DMA port %x",reg); + LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to read undefined DMA port %" sBitfs(x),reg); break; } return 0xffffffff; @@ -302,7 +308,7 @@ again: currcnt=0xffff; masked=true; UpdateEMSMapping(); - DoCallBack(DMA_TRANSFEREND); + DoCallBack(DMA_MASKED); } } return done; @@ -334,7 +340,7 @@ again: currcnt=0xffff; masked=true; UpdateEMSMapping(); - DoCallBack(DMA_TRANSFEREND); + DoCallBack(DMA_MASKED); } } return done; @@ -363,9 +369,11 @@ public: /* install handlers for ports 0x81-0x83 (on the first DMA controller) */ DmaControllers[0]->DMA_WriteHandler[0x10].Install(0x81,DMA_Write_Port,IO_MB,3); DmaControllers[0]->DMA_ReadHandler[0x10].Install(0x81,DMA_Read_Port,IO_MB,3); + DmaControllers[0]->DMA_WriteHandler[0x11].Install(0x87,DMA_Write_Port,IO_MB,1); + DmaControllers[0]->DMA_ReadHandler[0x11].Install(0x87,DMA_Read_Port,IO_MB,1); if (IS_EGAVGA_ARCH) { - /* install handlers for ports 0x81-0x83 (on the second DMA controller) */ + /* install handlers for ports 0x89-0x8B (on the second DMA controller) */ DmaControllers[1]->DMA_WriteHandler[0x10].Install(0x89,DMA_Write_Port,IO_MB,3); DmaControllers[1]->DMA_ReadHandler[0x10].Install(0x89,DMA_Read_Port,IO_MB,3); } diff --git a/src/hardware/gameblaster.cpp b/src/hardware/gameblaster.cpp index 324e2bc..c602c0a 100644 --- a/src/hardware/gameblaster.cpp +++ b/src/hardware/gameblaster.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dosbox.h" @@ -27,406 +27,75 @@ #include #include +#include "mame/emu.h" +#include "mame/saa1099.h" -#define LEFT 0x00 -#define RIGHT 0x01 -#define CMS_BUFFER_SIZE 128 -#define CMS_RATE 22050 +#define MASTER_CLOCK 7159090 - -typedef Bit8u UINT8; -typedef Bit16s INT16; - -/* this structure defines a channel */ -struct saa1099_channel -{ - int frequency; /* frequency (0x00..0xff) */ - int freq_enable; /* frequency enable */ - int noise_enable; /* noise enable */ - int octave; /* octave (0x00..0x07) */ - int amplitude[2]; /* amplitude (0x00..0x0f) */ - int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ - - /* vars to simulate the square wave */ - double counter; - double freq; - int level; -}; - -/* this structure defines a noise channel */ -struct saa1099_noise -{ - /* vars to simulate the noise generator output */ - double counter; - double freq; - int level; /* noise polynomal shifter */ -}; - -/* this structure defines a SAA1099 chip */ -struct SAA1099 -{ - int stream; /* our stream */ - int noise_params[2]; /* noise generators parameters */ - int env_enable[2]; /* envelope generators enable */ - int env_reverse_right[2]; /* envelope reversed for right channel */ - int env_mode[2]; /* envelope generators mode */ - int env_bits[2]; /* non zero = 3 bits resolution */ - int env_clock[2]; /* envelope clock mode (non-zero external) */ - int env_step[2]; /* current envelope step */ - int all_ch_enable; /* all channels enable */ - int sync_state; /* sync all channels */ - int selected_reg; /* selected register */ - struct saa1099_channel channels[6]; /* channels */ - struct saa1099_noise noise[2]; /* noise generators */ -}; - -static const UINT8 envelope[8][64] = { - /* zero amplitude */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* maximum amplitude */ - {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, - /* single decay */ - {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive decay */ - {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, - /* single triangular */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive triangular */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, - /* single attack */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive attack */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } -}; - - -static const int amplitude_lookup[16] = { - 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, - 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, - 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, - 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 -}; - -/* global parameters */ -static double sample_rate; -static SAA1099 saa1099[2]; +//My mixer channel static MixerChannel * cms_chan; -static Bit16s cms_buffer[2][2][CMS_BUFFER_SIZE]; -static Bit16s * cms_buf_point[4] = { - cms_buffer[0][0],cms_buffer[0][1],cms_buffer[1][0],cms_buffer[1][1] }; - -static Bitu last_command; -static Bitu base_port; - - -static void saa1099_envelope(int chip, int ch) -{ - struct SAA1099 *saa = &saa1099[chip]; - if (saa->env_enable[ch]) - { - int step, mode, mask; - mode = saa->env_mode[ch]; - /* step from 0..63 and then loop in steps 32..63 */ - step = saa->env_step[ch] = - ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20); - - mask = 15; - if (saa->env_bits[ch]) - mask &= ~1; /* 3 bit resolution, mask LSB */ - - saa->channels[ch*3+0].envelope[ LEFT] = - saa->channels[ch*3+1].envelope[ LEFT] = - saa->channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask; - if (saa->env_reverse_right[ch] & 0x01) - { - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask; - } - else - { - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask; - } - } - else - { - /* envelope mode off, set all envelope factors to 16 */ - saa->channels[ch*3+0].envelope[ LEFT] = - saa->channels[ch*3+1].envelope[ LEFT] = - saa->channels[ch*3+2].envelope[ LEFT] = - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = 16; - } -} - - -static void saa1099_update(int chip, INT16 **buffer, int length) -{ - struct SAA1099 *saa = &saa1099[chip]; - int j, ch; - - /* if the channels are disabled we're done */ - if (!saa->all_ch_enable) - { - /* init output data */ - memset(buffer[LEFT],0,length*sizeof(INT16)); - memset(buffer[RIGHT],0,length*sizeof(INT16)); - return; - } - - for (ch = 0; ch < 2; ch++) - { - switch (saa->noise_params[ch]) - { - case 0: saa->noise[ch].freq = 31250.0 * 2; break; - case 1: saa->noise[ch].freq = 15625.0 * 2; break; - case 2: saa->noise[ch].freq = 7812.5 * 2; break; - case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break; - } - } - - /* fill all data needed */ - for( j = 0; j < length; j++ ) - { - int output_l = 0, output_r = 0; - - /* for each channel */ - for (ch = 0; ch < 6; ch++) - { - if (saa->channels[ch].freq == 0.0) - saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) / - (511.0 - (double)saa->channels[ch].frequency); - - /* check the actual position in the square wave */ - saa->channels[ch].counter -= saa->channels[ch].freq; - while (saa->channels[ch].counter < 0) - { - /* calculate new frequency now after the half wave is updated */ - saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) / - (511.0 - (double)saa->channels[ch].frequency); - - saa->channels[ch].counter += sample_rate; - saa->channels[ch].level ^= 1; - - /* eventually clock the envelope counters */ - if (ch == 1 && saa->env_clock[0] == 0) - saa1099_envelope(chip, 0); - if (ch == 4 && saa->env_clock[1] == 0) - saa1099_envelope(chip, 1); - } - - /* if the noise is enabled */ - if (saa->channels[ch].noise_enable) - { - /* if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) */ - if (saa->noise[ch/3].level & 1) - { - /* subtract to avoid overflows, also use only half amplitude */ - output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2; - output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2; - } - } - - /* if the square wave is enabled */ - if (saa->channels[ch].freq_enable) - { - /* if the channel level is high */ - if (saa->channels[ch].level & 1) - { - output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16; - output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16; - } - } - } - - for (ch = 0; ch < 2; ch++) - { - /* check the actual position in noise generator */ - saa->noise[ch].counter -= saa->noise[ch].freq; - while (saa->noise[ch].counter < 0) - { - saa->noise[ch].counter += sample_rate; - if( ((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) ) - saa->noise[ch].level = (saa->noise[ch].level << 1) | 1; - else - saa->noise[ch].level <<= 1; - } - } - /* write sound data to the buffer */ - buffer[LEFT][j] = output_l / 6; - buffer[RIGHT][j] = output_r / 6; - } -} - -static void saa1099_write_port_w( int chip, int offset, int data ) -{ - struct SAA1099 *saa = &saa1099[chip]; - if(offset == 1) { - // address port - saa->selected_reg = data & 0x1f; - if (saa->selected_reg == 0x18 || saa->selected_reg == 0x19) { - /* clock the envelope channels */ - if (saa->env_clock[0]) saa1099_envelope(chip,0); - if (saa->env_clock[1]) saa1099_envelope(chip,1); - } - return; - } - int reg = saa->selected_reg; - int ch; - - switch (reg) - { - /* channel i amplitude */ - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: - ch = reg & 7; - saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; - saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; - break; - /* channel i frequency */ - case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: - ch = reg & 7; - saa->channels[ch].frequency = data & 0xff; - break; - /* channel i octave */ - case 0x10: case 0x11: case 0x12: - ch = (reg - 0x10) << 1; - saa->channels[ch + 0].octave = data & 0x07; - saa->channels[ch + 1].octave = (data >> 4) & 0x07; - break; - /* channel i frequency enable */ - case 0x14: - saa->channels[0].freq_enable = data & 0x01; - saa->channels[1].freq_enable = data & 0x02; - saa->channels[2].freq_enable = data & 0x04; - saa->channels[3].freq_enable = data & 0x08; - saa->channels[4].freq_enable = data & 0x10; - saa->channels[5].freq_enable = data & 0x20; - break; - /* channel i noise enable */ - case 0x15: - saa->channels[0].noise_enable = data & 0x01; - saa->channels[1].noise_enable = data & 0x02; - saa->channels[2].noise_enable = data & 0x04; - saa->channels[3].noise_enable = data & 0x08; - saa->channels[4].noise_enable = data & 0x10; - saa->channels[5].noise_enable = data & 0x20; - break; - /* noise generators parameters */ - case 0x16: - saa->noise_params[0] = data & 0x03; - saa->noise_params[1] = (data >> 4) & 0x03; - break; - /* envelope generators parameters */ - case 0x18: case 0x19: - ch = reg - 0x18; - saa->env_reverse_right[ch] = data & 0x01; - saa->env_mode[ch] = (data >> 1) & 0x07; - saa->env_bits[ch] = data & 0x10; - saa->env_clock[ch] = data & 0x20; - saa->env_enable[ch] = data & 0x80; - /* reset the envelope */ - saa->env_step[ch] = 0; - break; - /* channels enable & reset generators */ - case 0x1c: - saa->all_ch_enable = data & 0x01; - saa->sync_state = data & 0x02; - if (data & 0x02) - { - int i; -// logerror("%04x: (SAA1099 #%d) -reg 0x1c- Chip reset\n",activecpu_get_pc(), chip); - /* Synch & Reset generators */ - for (i = 0; i < 6; i++) - { - saa->channels[i].level = 0; - saa->channels[i].counter = 0.0; - } - } - break; - default: /* Error! */ -// logerror("%04x: (SAA1099 #%d) Unknown operation (reg:%02x, data:%02x)\n",activecpu_get_pc(), chip, reg, data); - LOG(LOG_MISC,LOG_ERROR)("CMS Unkown write to reg %x with %x",reg, data); - } -} +//Timer to disable the channel after a while +static Bit32u lastWriteTicks; +static Bit32u cmsBase; +static saa1099_device* device[2]; static void write_cms(Bitu port, Bitu val, Bitu /* iolen */) { if(cms_chan && (!cms_chan->enabled)) cms_chan->Enable(true); - last_command = PIC_Ticks; - switch (port-base_port) { - case 0: - saa1099_write_port_w(0,0,val); - break; + lastWriteTicks = PIC_Ticks; + switch ( port - cmsBase ) { case 1: - saa1099_write_port_w(0,1,val); + device[0]->control_w(0, 0, val); break; - case 2: - saa1099_write_port_w(1,0,val); + case 0: + device[0]->data_w(0, 0, val); break; case 3: - saa1099_write_port_w(1,1,val); + device[1]->control_w(0, 0, val); + break; + case 2: + device[1]->data_w(0, 0, val); break; } } static void CMS_CallBack(Bitu len) { - if (len > CMS_BUFFER_SIZE) return; + enum { + BUFFER_SIZE = 2048 + }; - saa1099_update(0, &cms_buf_point[0], (int)len); - saa1099_update(1, &cms_buf_point[2], (int)len); + if ( len > BUFFER_SIZE ) + return; - Bit16s * stream=(Bit16s *) MixTemp; - /* Mix chip outputs */ - for (Bitu l=0;lMAX_AUDIO) *stream=MAX_AUDIO; - else if (leftMAX_AUDIO) *stream=MAX_AUDIO; - else if (rightEnable( false ); + return; + } + Bit32s result[BUFFER_SIZE][2]; + Bit16s work[2][BUFFER_SIZE]; + Bit16s* buffers[2] = { work[0], work[1] }; + device_sound_interface::sound_stream stream; + device[0]->sound_stream_update(stream, 0, buffers, len); + for (Bitu i = 0; i < len; i++) { + result[i][0] = work[0][i]; + result[i][1] = work[1][i]; + } + device[1]->sound_stream_update(stream, 0, buffers, len); + for (Bitu i = 0; i < len; i++) { + result[i][0] += work[0][i]; + result[i][1] += work[1][i]; + } + cms_chan->AddSamples_s32( len, result[0] ); } - if(cms_chan) cms_chan->AddSamples_s16(len,(Bit16s *)MixTemp); - if (last_command + 10000 < PIC_Ticks) if(cms_chan) cms_chan->Enable(false); } // The Gameblaster detection static Bit8u cms_detect_register = 0xff; static void write_cms_detect(Bitu port, Bitu val, Bitu /* iolen */) { - switch(port-base_port) { + switch ( port - cmsBase ) { case 0x6: case 0x7: cms_detect_register = val; @@ -436,7 +105,7 @@ static void write_cms_detect(Bitu port, Bitu val, Bitu /* iolen */) { static Bitu read_cms_detect(Bitu port, Bitu /* iolen */) { Bit8u retval = 0xff; - switch(port-base_port) { + switch ( port - cmsBase ) { case 0x4: retval = 0x7f; break; @@ -459,31 +128,37 @@ private: public: CMS(Section* configuration):Module_base(configuration) { Section_prop * section = static_cast(configuration); - Bitu sample_rate_temp = section->Get_int("oplrate"); - sample_rate = static_cast(sample_rate_temp); - base_port = section->Get_hex("sbbase"); - WriteHandler.Install(base_port, write_cms, IO_MB,4); + Bitu sampleRate = section->Get_int( "oplrate" ); + cmsBase = section->Get_hex("sbbase"); + WriteHandler.Install( cmsBase, write_cms, IO_MB, 4 ); // A standalone Gameblaster has a magic chip on it which is // sometimes used for detection. const char * sbtype=section->Get_string("sbtype"); if (!strcasecmp(sbtype,"gb")) { - DetWriteHandler.Install(base_port+4,write_cms_detect,IO_MB,12); - DetReadHandler.Install(base_port,read_cms_detect,IO_MB,16); + DetWriteHandler.Install( cmsBase + 4, write_cms_detect, IO_MB, 12 ); + DetReadHandler.Install(cmsBase,read_cms_detect,IO_MB,16); } /* Register the Mixer CallBack */ - cms_chan = MixerChan.Install(CMS_CallBack,sample_rate_temp,"CMS"); + cms_chan = MixerChan.Install(CMS_CallBack,sampleRate,"CMS"); - last_command = PIC_Ticks; - - for (int s=0;s<2;s++) { - struct SAA1099 *saa = &saa1099[s]; - memset(saa, 0, sizeof(struct SAA1099)); - } + lastWriteTicks = PIC_Ticks; + + Bit32u freq = 7159000; //14318180 isa clock / 2 + + machine_config config; + device[0] = new saa1099_device(config, "", 0, 7159090); + device[1] = new saa1099_device(config, "", 0, 7159090); + + device[0]->device_start(); + device[1]->device_start(); } + ~CMS() { cms_chan = 0; + delete device[0]; + delete device[1]; } }; diff --git a/src/hardware/gus.cpp b/src/hardware/gus.cpp index fb0d6c0..d1d7e42 100644 --- a/src/hardware/gus.cpp +++ b/src/hardware/gus.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -32,10 +32,9 @@ using namespace std; //Extra bits of precision over normal gus -#define WAVE_BITS 2 -#define WAVE_FRACT (9+WAVE_BITS) +#define WAVE_FRACT 9 #define WAVE_FRACT_MASK ((1 << WAVE_FRACT)-1) -#define WAVE_MSWMASK ((1 << (16+WAVE_BITS))-1) +#define WAVE_MSWMASK ((1 << 16)-1) #define WAVE_LSWMASK (0xffffffff ^ WAVE_MSWMASK) //Amount of precision the volume has @@ -46,12 +45,22 @@ using namespace std; #define GUS_RATE myGUS.rate #define LOG_GUS 0 +#define VOL_SHIFT 14 + +#define WCTRL_STOPPED 0x01 +#define WCTRL_STOP 0x02 +#define WCTRL_16BIT 0x04 +#define WCTRL_LOOP 0x08 +#define WCTRL_BIDIRECTIONAL 0x10 +#define WCTRL_IRQENABLED 0x20 +#define WCTRL_DECREASING 0x40 +#define WCTRL_IRQPENDING 0x80 + Bit8u adlib_commandreg; static MixerChannel * gus_chan; static Bit8u irqtable[8] = { 0, 2, 5, 3, 7, 11, 12, 15 }; static Bit8u dmatable[8] = { 0, 1, 3, 5, 6, 7, 0, 0 }; static Bit8u GUSRam[1024*1024]; // 1024K of GUS Ram -static Bit32s AutoAmp = 512; static Bit16u vol16bit[4096]; static Bit32u pantable[16]; @@ -102,41 +111,6 @@ Bitu DEBUG_EnableDebugger(void); static void GUS_DMA_Callback(DmaChannel * chan,DMAEvent event); -// Returns a single 16-bit sample from the Gravis's RAM -static INLINE Bit32s GetSample(Bit32u Delta, Bit32u CurAddr, bool eightbit) { - Bit32u useAddr; - Bit32u holdAddr; - useAddr = CurAddr >> WAVE_FRACT; - if (eightbit) { - if (Delta >= (1 << WAVE_FRACT)) { - Bit32s tmpsmall = (Bit8s)GUSRam[useAddr]; - return tmpsmall << 8; - } else { - // Interpolate - Bit32s w1 = ((Bit8s)GUSRam[useAddr+0]) << 8; - Bit32s w2 = ((Bit8s)GUSRam[useAddr+1]) << 8; - Bit32s diff = w2 - w1; - return (w1+((diff*(Bit32s)(CurAddr&WAVE_FRACT_MASK ))>>WAVE_FRACT)); - } - } else { - // Formula used to convert addresses for use with 16-bit samples - holdAddr = useAddr & 0xc0000L; - useAddr = useAddr & 0x1ffffL; - useAddr = useAddr << 1; - useAddr = (holdAddr | useAddr); - - if(Delta >= (1 << WAVE_FRACT)) { - return (GUSRam[useAddr+0] | (((Bit8s)GUSRam[useAddr+1]) << 8)); - } else { - // Interpolate - Bit32s w1 = (GUSRam[useAddr+0] | (((Bit8s)GUSRam[useAddr+1]) << 8)); - Bit32s w2 = (GUSRam[useAddr+2] | (((Bit8s)GUSRam[useAddr+3]) << 8)); - Bit32s diff = w2 - w1; - return (w1+((diff*(Bit32s)(CurAddr&WAVE_FRACT_MASK ))>>WAVE_FRACT)); - } - } -} - class GUSChannels { public: Bit32u WaveStart; @@ -183,7 +157,47 @@ public: PanLeft = 0; PanRight = 0; PanPot = 0x7; - }; + } + + // Returns a single 16-bit sample from the Gravis's RAM + + INLINE Bit32s GetSample8() const { + Bit32u useAddr = WaveAddr >> WAVE_FRACT; + if (WaveAdd >= (1 << WAVE_FRACT)) { + Bit32s tmpsmall = (Bit8s)GUSRam[useAddr]; + return tmpsmall << 8; + } + else { + Bit32u nextAddr = (useAddr + 1) & ( 1024 * 1024 - 1 ); + // Interpolate + Bit32s w1 = ((Bit8s)GUSRam[useAddr]) << 8; + Bit32s w2 = ((Bit8s)GUSRam[nextAddr]) << 8; + Bit32s diff = w2 - w1; + Bit32s scale = (Bit32s)(WaveAddr&WAVE_FRACT_MASK); + return (w1 + ((diff*scale) >> WAVE_FRACT)); + } + } + + INLINE Bit32s GetSample16() const { + Bit32u useAddr = WaveAddr >> WAVE_FRACT; + // Formula used to convert addresses for use with 16-bit samples + Bit32u holdAddr = useAddr & 0xc0000L; + useAddr = useAddr & 0x1ffffL; + useAddr = useAddr << 1; + useAddr = (holdAddr | useAddr); + if (WaveAdd >= (1 << WAVE_FRACT)) { + return (GUSRam[useAddr + 0] | (((Bit8s)GUSRam[useAddr + 1]) << 8)); + } + else { + // Interpolate + Bit32s w1 = (GUSRam[useAddr + 0] | (((Bit8s)GUSRam[useAddr + 1]) << 8)); + Bit32s w2 = (GUSRam[useAddr + 2] | (((Bit8s)GUSRam[useAddr + 3]) << 8)); + Bit32s diff = w2 - w1; + Bit32s scale = (Bit32s)(WaveAddr&WAVE_FRACT_MASK); + return (w1 + ((diff*scale) >> WAVE_FRACT)); + } + } + void WriteWaveFreq(Bit16u val) { WaveFreq = val; double frameadd = double(val >> 1)/512.0; //Samples / original gus frame @@ -219,13 +233,17 @@ public: void WriteRampCtrl(Bit8u val) { Bit32u old=myGUS.RampIRQ; RampCtrl = val & 0x7f; - if ((val & 0xa0)==0xa0) myGUS.RampIRQ|=irqmask; - else myGUS.RampIRQ&=~irqmask; - if (old != myGUS.RampIRQ) CheckVoiceIrq(); + //Manually set the irq + if ((val & 0xa0)==0xa0) + myGUS.RampIRQ|=irqmask; + else + myGUS.RampIRQ&=~irqmask; + if (old != myGUS.RampIRQ) + CheckVoiceIrq(); } INLINE Bit8u ReadRampCtrl(void) { Bit8u ret=RampCtrl; - if (myGUS.RampIRQ & irqmask) ret|=0x80; + if (myGUS.RampIRQ & irqmask) ret |= 0x80; return ret; } void WriteRampRate(Bit8u val) { @@ -235,16 +253,18 @@ public: RampAdd = (Bit32u)realadd; } INLINE void WaveUpdate(void) { - if (WaveCtrl & 0x3) return; + if (WaveCtrl & ( WCTRL_STOP | WCTRL_STOPPED)) return; Bit32s WaveLeft; - if (WaveCtrl & 0x40) { - WaveAddr-=WaveAdd; - WaveLeft=WaveStart-WaveAddr; + if (WaveCtrl & WCTRL_DECREASING) { + WaveAddr -= WaveAdd; + WaveLeft = WaveStart-WaveAddr; } else { - WaveAddr+=WaveAdd; - WaveLeft=WaveAddr-WaveEnd; + WaveAddr += WaveAdd; + WaveLeft = WaveAddr-WaveEnd; } - if (WaveLeft<0) return; + //Not yet reaching a boundary + if (WaveLeft<0) + return; /* Generate an IRQ if needed */ if (WaveCtrl & 0x20) { myGUS.WaveIRQ|=irqmask; @@ -252,13 +272,13 @@ public: /* Check for not being in PCM operation */ if (RampCtrl & 0x04) return; /* Check for looping */ - if (WaveCtrl & 0x08) { + if (WaveCtrl & WCTRL_LOOP) { /* Bi-directional looping */ - if (WaveCtrl & 0x10) WaveCtrl^=0x40; - WaveAddr = (WaveCtrl & 0x40) ? (WaveEnd-WaveLeft) : (WaveStart+WaveLeft); + if (WaveCtrl & WCTRL_BIDIRECTIONAL) WaveCtrl^= WCTRL_DECREASING; + WaveAddr = (WaveCtrl & WCTRL_DECREASING) ? (WaveEnd-WaveLeft) : (WaveStart+WaveLeft); } else { WaveCtrl|=1; //Stop the channel - WaveAddr = (WaveCtrl & 0x40) ? WaveStart : WaveEnd; + WaveAddr = (WaveCtrl & WCTRL_DECREASING) ? WaveStart : WaveEnd; } } INLINE void UpdateVolumes(void) { @@ -299,21 +319,32 @@ public: } UpdateVolumes(); } - void generateSamples(Bit32s * stream,Bit32u len) { - int i; - Bit32s tmpsamp; - bool eightbit; - if (RampCtrl & WaveCtrl & 3) return; - eightbit = ((WaveCtrl & 0x4) == 0); - for(i=0;i<(int)len;i++) { - // Get sample - tmpsamp = GetSample(WaveAdd, WaveAddr, eightbit); - // Output stereo sample - stream[i<<1]+= tmpsamp * VolLeft; - stream[(i<<1)+1]+= tmpsamp * VolRight; - WaveUpdate(); - RampUpdate(); + void generateSamples(Bit32s * stream,Bit32u len) { + //Disabled channel + if (RampCtrl & WaveCtrl & 3) return; + + if (WaveCtrl & WCTRL_16BIT) { + for (int i = 0; i < (int)len; i++) { + // Get sample + Bit32s tmpsamp = GetSample16(); + // Output stereo sample + stream[i << 1] += tmpsamp * VolLeft; + stream[(i << 1) + 1] += tmpsamp * VolRight; + WaveUpdate(); + RampUpdate(); + } + } + else { + for (int i = 0; i < (int)len; i++) { + // Get sample + Bit32s tmpsamp = GetSample8(); + // Output stereo sample + stream[i << 1] += tmpsamp * VolLeft; + stream[(i << 1) + 1] += tmpsamp * VolRight; + WaveUpdate(); + RampUpdate(); + } } } }; @@ -401,20 +432,20 @@ static Bit16u ExecuteReadRegister(void) { else return 0x0300; case 0x82: // Channel MSB start address register - if (curchan) return (Bit16u)(curchan->WaveStart >> (WAVE_BITS+16)); + if (curchan) return (Bit16u)(curchan->WaveStart >> 16); else return 0x0000; case 0x83: // Channel LSW start address register - if (curchan) return (Bit16u)(curchan->WaveStart >> WAVE_BITS); + if (curchan) return (Bit16u)(curchan->WaveStart ); else return 0x0000; case 0x89: // Channel volume register if (curchan) return (Bit16u)((curchan->RampVol >> RAMP_FRACT) << 4); else return 0x0000; case 0x8a: // Channel MSB current address register - if (curchan) return (Bit16u)(curchan->WaveAddr >> (WAVE_BITS+16)); + if (curchan) return (Bit16u)(curchan->WaveAddr >> 16); else return 0x0000; case 0x8b: // Channel LSW current address register - if (curchan) return (Bit16u)(curchan->WaveAddr >> WAVE_BITS); + if (curchan) return (Bit16u)(curchan->WaveAddr ); else return 0x0000; case 0x8d: // Channel volume control register @@ -461,25 +492,25 @@ static void ExecuteGlobRegister(void) { break; case 0x2: // Channel MSW start address register if (curchan) { - Bit32u tmpaddr = (Bit32u)(myGUS.gRegData & 0x1fff) << (16+WAVE_BITS); + Bit32u tmpaddr = (Bit32u)((myGUS.gRegData & 0x1fff) << 16); curchan->WaveStart = (curchan->WaveStart & WAVE_MSWMASK) | tmpaddr; } break; case 0x3: // Channel LSW start address register if(curchan != NULL) { - Bit32u tmpaddr = (Bit32u)(myGUS.gRegData) << WAVE_BITS; + Bit32u tmpaddr = (Bit32u)(myGUS.gRegData); curchan->WaveStart = (curchan->WaveStart & WAVE_LSWMASK) | tmpaddr; } break; case 0x4: // Channel MSW end address register if(curchan != NULL) { - Bit32u tmpaddr = (Bit32u)(myGUS.gRegData & 0x1fff) << (16+WAVE_BITS); + Bit32u tmpaddr = (Bit32u)(myGUS.gRegData & 0x1fff) << 16; curchan->WaveEnd = (curchan->WaveEnd & WAVE_MSWMASK) | tmpaddr; } break; case 0x5: // Channel MSW end address register if(curchan != NULL) { - Bit32u tmpaddr = (Bit32u)(myGUS.gRegData) << WAVE_BITS; + Bit32u tmpaddr = (Bit32u)(myGUS.gRegData); curchan->WaveEnd = (curchan->WaveEnd & WAVE_LSWMASK) | tmpaddr; } break; @@ -510,13 +541,13 @@ static void ExecuteGlobRegister(void) { break; case 0xA: // Channel MSW current address register if(curchan != NULL) { - Bit32u tmpaddr = (Bit32u)(myGUS.gRegData & 0x1fff) << (16+WAVE_BITS); + Bit32u tmpaddr = (Bit32u)(myGUS.gRegData & 0x1fff) << 16; curchan->WaveAddr = (curchan->WaveAddr & WAVE_MSWMASK) | tmpaddr; } break; case 0xB: // Channel LSW current address register if(curchan != NULL) { - Bit32u tmpaddr = (Bit32u)(myGUS.gRegData) << (WAVE_BITS); + Bit32u tmpaddr = (Bit32u)(myGUS.gRegData); curchan->WaveAddr = (curchan->WaveAddr & WAVE_LSWMASK) | tmpaddr; } break; @@ -533,9 +564,9 @@ static void ExecuteGlobRegister(void) { if(myGUS.ActiveChannels > 32) myGUS.ActiveChannels = 32; myGUS.ActiveMask=0xffffffffU >> (32-myGUS.ActiveChannels); gus_chan->Enable(true); - myGUS.basefreq = (Bit32u)((float)1000000/(1.619695497*(float)(myGUS.ActiveChannels))); + myGUS.basefreq = (Bit32u)(0.5 + 1000000.0/(1.619695497*(double)(myGUS.ActiveChannels))); #if LOG_GUS - LOG_MSG("GUS set to %d channels", myGUS.ActiveChannels); + LOG_MSG("GUS set to %d channels, freq %d", myGUS.ActiveChannels, myGUS.basefreq); #endif for (i=0;iUpdateWaveRamp(); break; @@ -711,7 +742,15 @@ static void write_gus(Bitu port,Bitu val,Bitu iolen) { static void GUS_DMA_Callback(DmaChannel * chan,DMAEvent event) { if (event!=DMA_UNMASKED) return; - Bitu dmaaddr = myGUS.dmaAddr << 4; + Bitu dmaaddr; + //Calculate the dma address + //DMA transfers can't cross 256k boundaries, so you should be safe to just determine the start once and go from there + //Bit 2 - 0 = if DMA channel is an 8 bit channel(0 - 3). + if (myGUS.DMAControl & 0x4) + dmaaddr = (((myGUS.dmaAddr & 0x1fff) << 1) | (myGUS.dmaAddr & 0xc000)) << 4; + else + dmaaddr = myGUS.dmaAddr << 4; + //Reading from dma? if((myGUS.DMAControl & 0x2) == 0) { Bitu read=chan->Read(chan->currcnt+1,&GUSRam[dmaaddr]); //Check for 16 or 8bit channel @@ -727,8 +766,8 @@ static void GUS_DMA_Callback(DmaChannel * chan,DMAEvent event) { for(i=dmaaddr+1;i<(dmaaddr+read);i+=2) GUSRam[i] ^= 0x80; } } + //Writing to dma } else { - //Read data out of UltraSound chan->Write(chan->currcnt+1,&GUSRam[dmaaddr]); } /* Raise the TC irq if needed */ @@ -740,24 +779,17 @@ static void GUS_DMA_Callback(DmaChannel * chan,DMAEvent event) { } static void GUS_CallBack(Bitu len) { - memset(&MixTemp,0,len*8); - Bitu i; - Bit16s * buf16 = (Bit16s *)MixTemp; - Bit32s * buf32 = (Bit32s *)MixTemp; - for(i=0;igenerateSamples(buf32,len); - for(i=0;i> 13)*AutoAmp)>>9; - if (sample>32767) { - sample=32767; - AutoAmp--; - } else if (sample<-32768) { - sample=-32768; - AutoAmp--; - } - buf16[i] = (Bit16s)(sample); + Bit32s buffer[MIXER_BUFSIZE][2]; + memset(buffer, 0, len * sizeof(buffer[0])); + + for (Bitu i = 0; i < myGUS.ActiveChannels; i++) { + guschan[i]->generateSamples(buffer[0], len); } - gus_chan->AddSamples_s16(len,buf16); + for (Bitu i = 0; i < len; i++) { + buffer[i][0] >>= VOL_SHIFT; + buffer[i][1] >>= VOL_SHIFT; + } + gus_chan->AddSamples_s32(len, buffer[0]); CheckVoiceIrq(); } @@ -768,10 +800,12 @@ static void MakeTables(void) { for (i=4095;i>=0;i--) { vol16bit[i]=(Bit16s)out; out/=1.002709201; /* 0.0235 dB Steps */ + //Original amplification routine in the hardware + //vol16bit[i] = ((256 + i & 0xff) << VOL_SHIFT) / (1 << (24 - (i >> 8))); } pantable[0] = 4095 << RAMP_FRACT; for (i=1;i<16;i++) { - pantable[i]=(Bit32u)(-128.0*(log((double)i/15.0)/log(2.0))*(double)(1 << RAMP_FRACT)); + pantable[i]=(Bit32u)(0.5-128.0*(log((double)i/15.0)/log(2.0))*(double)(1 << RAMP_FRACT)); } } @@ -874,7 +908,7 @@ public: memset(&myGUS,0,sizeof(myGUS)); memset(GUSRam,0,1024*1024); - } + } }; static GUS* test; diff --git a/src/hardware/hardware.cpp b/src/hardware/hardware.cpp index 8f818e8..2f800b7 100644 --- a/src/hardware/hardware.cpp +++ b/src/hardware/hardware.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -361,7 +361,7 @@ void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, } #ifdef PNG_TEXT_SUPPORTED int fields = 1; - png_text text[1]; + png_text text[1] = {}; const char* text_s = "DOSBox " VERSION; size_t strl = strlen(text_s); char* ptext_s = new char[strl + 1]; @@ -490,7 +490,7 @@ skip_shot: if (!capture.video.buf) goto skip_video; capture.video.index = (Bit8u*)malloc( 16*4096 ); - if (!capture.video.buf) + if (!capture.video.index) goto skip_video; capture.video.indexsize = 16*4096; capture.video.indexused = 8; diff --git a/src/hardware/iohandler.cpp b/src/hardware/iohandler.cpp index 80c7091..af1d010 100644 --- a/src/hardware/iohandler.cpp +++ b/src/hardware/iohandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -208,14 +208,14 @@ inline void IO_USEC_write_delay_old() { inline void IO_USEC_read_delay() { Bits delaycyc = CPU_CycleMax/IODELAY_READ_MICROSk; - if(GCC_UNLIKELY(CPU_Cycles < 3*delaycyc)) delaycyc = 0; //Else port acces will set cycles to 0. which might trigger problem with games which read 16 bit values + if(GCC_UNLIKELY(delaycyc > CPU_Cycles)) delaycyc = CPU_Cycles; CPU_Cycles -= delaycyc; CPU_IODelayRemoved += delaycyc; } inline void IO_USEC_write_delay() { Bits delaycyc = CPU_CycleMax/IODELAY_WRITE_MICROSk; - if(GCC_UNLIKELY(CPU_Cycles < 3*delaycyc)) delaycyc=0; + if(GCC_UNLIKELY(delaycyc > CPU_Cycles)) delaycyc = CPU_Cycles; CPU_Cycles -= delaycyc; CPU_IODelayRemoved += delaycyc; } @@ -404,6 +404,7 @@ Bitu IO_ReadB(Bitu port) { entry->eip=reg_eip; CPU_Push16(SegValue(cs)); CPU_Push16(reg_ip); + Bit8u old_al = reg_al; Bit16u old_dx = reg_dx; reg_dx = port; RealPt icb = CALLBACK_RealPointer(call_priv_io); @@ -415,6 +416,7 @@ Bitu IO_ReadB(Bitu port) { iof_queue.used--; retval = reg_al; + reg_al = old_al; reg_dx = old_dx; memcpy(&lflags,&old_lflags,sizeof(LazyFlags)); cpudecoder=old_cpudecoder; @@ -441,6 +443,7 @@ Bitu IO_ReadW(Bitu port) { entry->eip=reg_eip; CPU_Push16(SegValue(cs)); CPU_Push16(reg_ip); + Bit16u old_ax = reg_ax; Bit16u old_dx = reg_dx; reg_dx = port; RealPt icb = CALLBACK_RealPointer(call_priv_io); @@ -452,6 +455,7 @@ Bitu IO_ReadW(Bitu port) { iof_queue.used--; retval = reg_ax; + reg_ax = old_ax; reg_dx = old_dx; memcpy(&lflags,&old_lflags,sizeof(LazyFlags)); cpudecoder=old_cpudecoder; @@ -477,6 +481,7 @@ Bitu IO_ReadD(Bitu port) { entry->eip=reg_eip; CPU_Push16(SegValue(cs)); CPU_Push16(reg_ip); + Bit32u old_eax = reg_eax; Bit16u old_dx = reg_dx; reg_dx = port; RealPt icb = CALLBACK_RealPointer(call_priv_io); @@ -488,6 +493,7 @@ Bitu IO_ReadD(Bitu port) { iof_queue.used--; retval = reg_eax; + reg_eax = old_eax; reg_dx = old_dx; memcpy(&lflags,&old_lflags,sizeof(LazyFlags)); cpudecoder=old_cpudecoder; diff --git a/src/hardware/ipx.cpp b/src/hardware/ipx.cpp index 14f55fb..9783df2 100644 --- a/src/hardware/ipx.cpp +++ b/src/hardware/ipx.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -854,7 +854,7 @@ public: // Help on connect command if(strcasecmp("connect", helpStr) == 0) { WriteOut("IPXNET CONNECT opens a connection to an IPX tunneling server running on another\n"); - WriteOut("DosBox session. The \"address\" parameter specifies the IP address or host name\n"); + WriteOut("DOSBox session. The \"address\" parameter specifies the IP address or host name\n"); WriteOut("of the server computer. One can also specify the UDP port to use. By default\n"); WriteOut("IPXNET uses port 213, the assigned IANA port for IPX tunneling, for its\nconnection.\n\n"); WriteOut("The syntax for IPXNET CONNECT is:\n\n"); @@ -870,9 +870,9 @@ public: } // Help on the startserver command if(strcasecmp("startserver", helpStr) == 0) { - WriteOut("IPXNET STARTSERVER starts and IPX tunneling server on this DosBox session. By\n"); + WriteOut("IPXNET STARTSERVER starts and IPX tunneling server on this DOSBox session. By\n"); WriteOut("default, the server will accept connections on UDP port 213, though this can be\n"); - WriteOut("changed. Once the server is started, DosBox will automatically start a client\n"); + WriteOut("changed. Once the server is started, DOSBox will automatically start a client\n"); WriteOut("connection to the IPX tunneling server.\n\n"); WriteOut("The syntax for IPXNET STARTSERVER is:\n\n"); WriteOut("IPXNET STARTSERVER \n\n"); @@ -880,9 +880,9 @@ public: } // Help on the stop server command if(strcasecmp("stopserver", helpStr) == 0) { - WriteOut("IPXNET STOPSERVER stops the IPX tunneling server running on this DosBox\nsession."); + WriteOut("IPXNET STOPSERVER stops the IPX tunneling server running on this DOSBox\nsession."); WriteOut(" Care should be taken to ensure that all other connections have\nterminated "); - WriteOut("as well sinnce stoping the server may cause lockups on other\nmachines still using "); + WriteOut("as well since stopping the server may cause lockups on other\nmachines still using "); WriteOut("the IPX tunneling server.\n\n"); WriteOut("The syntax for IPXNET STOPSERVER is:\n\n"); WriteOut("IPXNET STOPSERVER\n\n"); @@ -899,7 +899,7 @@ public: } // Help on the status command if(strcasecmp("status", helpStr) == 0) { - WriteOut("IPXNET STATUS reports the current state of this DosBox's sessions IPX tunneling\n"); + WriteOut("IPXNET STATUS reports the current state of this DOSBox's sessions IPX tunneling\n"); WriteOut("network. For a list of the computers connected to the network use the IPXNET \n"); WriteOut("PING command.\n\n"); WriteOut("The syntax for IPXNET STATUS is:\n\n"); @@ -910,7 +910,7 @@ public: void Run(void) { - WriteOut("IPX Tunneling utility for DosBox\n\n"); + WriteOut("IPX Tunneling utility for DOSBox\n\n"); if(!cmd->GetCount()) { WriteOut("The syntax of this command is:\n\n"); WriteOut("IPXNET [ CONNECT | DISCONNECT | STARTSERVER | STOPSERVER | PING | HELP |\n STATUS ]\n\n"); @@ -960,7 +960,7 @@ public: } if(strcasecmp("stopserver", temp_line.c_str()) == 0) { if(!isIpxServer) { - WriteOut("IPX Tunneling Server not running in this DosBox session.\n"); + WriteOut("IPX Tunneling Server not running in this DOSBox session.\n"); } else { isIpxServer = false; DisconnectFromServer(false); @@ -1072,7 +1072,7 @@ Bitu IPX_ESRHandler(void) { CALLBACK_RunRealFar(RealSeg(ESRList->getESRAddr()), RealOff(ESRList->getESRAddr())); } - delete ESRList; + delete ESRList; //Destructor updates this pointer to the next value or NULL } // while IO_WriteB(0xa0,0x63); //EOI11 diff --git a/src/hardware/ipxserver.cpp b/src/hardware/ipxserver.cpp index ad77eed..d22ac77 100644 --- a/src/hardware/ipxserver.cpp +++ b/src/hardware/ipxserver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/joystick.cpp b/src/hardware/joystick.cpp index e45c222..f1dc22a 100644 --- a/src/hardware/joystick.cpp +++ b/src/hardware/joystick.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,13 +11,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include +#include #include "dosbox.h" #include "inout.h" #include "setup.h" @@ -25,6 +26,12 @@ #include "pic.h" #include "support.h" + +//TODO: higher axis can't be mapped. Find out why again + +//Set to true, to enable automated switching back to square on circle mode if the inputs are outside the cirle. +#define SUPPORT_MAP_AUTO 0 + #define RANGE 64 #define TIMEOUT 10 @@ -33,17 +40,104 @@ #define S_PER_OHM 0.000000011 struct JoyStick { + enum {JOYMAP_SQUARE,JOYMAP_CIRCLE,JOYMAP_INBETWEEN} mapstate; bool enabled; - float xpos,ypos; - double xtick,ytick; - Bitu xcount,ycount; + float xpos, ypos; //position as set by SDL. + double xtick, ytick; + Bitu xcount, ycount; bool button[2]; + int deadzone; //Deadzone (value between 0 and 100) interpreted as percentage. + bool transformed; //Whether xpos,ypos have been converted to xfinal and yfinal. Cleared when new xpos orypos have been set + float xfinal, yfinal; //position returned to the game for stick 0. + + void clip() { + if (xfinal > 1.0) xfinal = 1.0; + else if (xfinal < -1.0) xfinal = -1.0; + if (yfinal > 1.0) yfinal = 1.0; + else if (yfinal < -1.0) yfinal = -1.0; + } + + void fake_digital() { + if (xpos > 0.5f) xfinal = 1.0f; + else if (xpos < -0.5f) xfinal = -1.0f; + else xfinal = 0.0f; + if (ypos > 0.5f) yfinal = 1.0f; + else if (ypos < -0.5f) yfinal = -1.0f; + else yfinal = 0.0f; + } + + void transform_circular(){ + float r = sqrtf(xpos * xpos + ypos * ypos); + if (r == 0.0) {xfinal = xpos; yfinal = ypos; return;} + float deadzone_f = deadzone / 100.0f; + float s = 1.0f - deadzone_f; + if (r < deadzone_f) { + xfinal = yfinal = 0.0f; + return; + } + + float deadzonescale = (r - deadzone_f) / s; //r if deadzone=0; + float xa = fabsf(xpos); + float ya = fabsf(ypos); + float maxpos = (ya>xa?ya:xa); + xfinal = xpos * deadzonescale/maxpos; + yfinal = ypos * deadzonescale/maxpos; + } + + void transform_square() { + float deadzone_f = deadzone / 100.0f; + float s = 1.0f - deadzone_f; + + if (xpos > deadzone_f) { + xfinal = (xpos - deadzone_f)/ s; + } else if ( xpos < -deadzone_f) { + xfinal = (xpos + deadzone_f) / s; + } else xfinal = 0.0f; + if (ypos > deadzone_f) { + yfinal = (ypos - deadzone_f)/ s; + } else if ( ypos < - deadzone_f) { + yfinal = (ypos + deadzone_f) / s; + } else yfinal = 0.0f; + } + +#if SUPPORT_MAP_AUTO + void transform_inbetween(){ + //First transform to a circle and crop the values to -1.0 -> 1.0 + //then keep on doing this in future calls until it is safe to switch square mapping + // safe = 0.95 as ratio for both axis, or in deadzone + transform_circular(); + clip(); + + + float xrate = xpos / xfinal; + float yrate = ypos / yfinal; + if (xrate > 0.95 && yrate > 0.95) { + mapstate = JOYMAP_SQUARE; //TODO misschien xfinal=xpos... + //LOG_MSG("switched to square %f %f",xrate,yrate); + } + } +#endif + void transform_input(){ + if (transformed) return; + transformed = true; + if (deadzone == 100) fake_digital(); + else { + if (mapstate == JOYMAP_SQUARE) transform_square(); + else if (mapstate == JOYMAP_CIRCLE) transform_circular(); +#if SUPPORT_MAP_AUTO + if (mapstate == JOYMAP_INBETWEEN) transform_inbetween(); //No else here +#endif + clip(); + } + } + + }; JoystickType joytype; static JoyStick stick[2]; -static Bit32u last_write = 0; +static Bitu last_write = 0; static bool write_active = false; static bool swap34 = false; bool button_wrapping_enabled = true; @@ -114,8 +208,9 @@ static void write_p201(Bitu port,Bitu val,Bitu iolen) { write_active = true; last_write = PIC_Ticks; if (stick[0].enabled) { - stick[0].xcount=(Bitu)((stick[0].xpos*RANGE)+RANGE); - stick[0].ycount=(Bitu)((stick[0].ypos*RANGE)+RANGE); + stick[0].transform_input(); + stick[0].xcount=(Bitu)((stick[0].xfinal*RANGE)+RANGE); + stick[0].ycount=(Bitu)((stick[0].yfinal*RANGE)+RANGE); } if (stick[1].enabled) { stick[1].xcount=(Bitu)(((swap34? stick[1].ypos : stick[1].xpos)*RANGE)+RANGE); @@ -124,16 +219,16 @@ static void write_p201(Bitu port,Bitu val,Bitu iolen) { } static void write_p201_timed(Bitu port,Bitu val,Bitu iolen) { - // Store writetime index // Axes take time = 24.2 microseconds + ( 0.011 microsecons/ohm * resistance ) // to reset to 0 - // Precalculate the time at which each axis hits 0 here + // Pre-calculate the time at which each axis hits 0 here double currentTick = PIC_FullIndex(); if (stick[0].enabled) { + stick[0].transform_input(); stick[0].xtick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM * - (double)(((stick[0].xpos+1.0)* OHMS)) ); + (double)(((stick[0].xfinal+1.0)* OHMS)) ); stick[0].ytick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM * - (double)(((stick[0].ypos+1.0)* OHMS)) ); + (double)(((stick[0].yfinal+1.0)* OHMS)) ); } if (stick[1].enabled) { stick[1].xtick = currentTick + 1000.0*( JOY_S_CONSTANT + S_PER_OHM * @@ -144,23 +239,27 @@ static void write_p201_timed(Bitu port,Bitu val,Bitu iolen) { } void JOYSTICK_Enable(Bitu which,bool enabled) { - if (which<2) stick[which].enabled=enabled; + if (which<2) stick[which].enabled = enabled; } void JOYSTICK_Button(Bitu which,Bitu num,bool pressed) { - if ((which<2) && (num<2)) stick[which].button[num]=pressed; + if ((which<2) && (num<2)) stick[which].button[num] = pressed; } void JOYSTICK_Move_X(Bitu which,float x) { - if (which<2) { - stick[which].xpos=x; - } + if(which > 2) return; + if (stick[which].xpos == x) return; + stick[which].xpos = x; + stick[which].transformed = false; +// if( which == 0 || joytype != JOY_FCS) +// stick[which].applied_conversion; //todo } void JOYSTICK_Move_Y(Bitu which,float y) { - if (which<2) { - stick[which].ypos=y; - } + if(which > 2) return; + if (stick[which].ypos == y) return; + stick[which].ypos = y; + stick[which].transformed = false; } bool JOYSTICK_IsEnabled(Bitu which) { @@ -174,13 +273,15 @@ bool JOYSTICK_GetButton(Bitu which, Bitu num) { } float JOYSTICK_GetMove_X(Bitu which) { - if (which<2) return stick[which].xpos; - return 0.0f; + if (which > 1) return 0.0f; + if (which == 0) { stick[0].transform_input(); return stick[0].xfinal;} + return stick[1].xpos; } float JOYSTICK_GetMove_Y(Bitu which) { - if (which<2) return stick[which].ypos; - return 0.0f; + if (which > 1) return 0.0f; + if (which == 0) { stick[0].transform_input(); return stick[0].yfinal;} + return stick[1].ypos; } class JOYSTICK:public Module_base{ @@ -189,20 +290,20 @@ private: IO_WriteHandleObject WriteHandler; public: JOYSTICK(Section* configuration):Module_base(configuration){ - Section_prop * section=static_cast(configuration); - const char * type=section->Get_string("joysticktype"); - if (!strcasecmp(type,"none")) joytype = JOY_NONE; - else if (!strcasecmp(type,"false")) joytype = JOY_NONE; - else if (!strcasecmp(type,"auto")) joytype = JOY_AUTO; - else if (!strcasecmp(type,"2axis")) joytype = JOY_2AXIS; - else if (!strcasecmp(type,"4axis")) joytype = JOY_4AXIS; + Section_prop * section = static_cast(configuration); + const char * type = section->Get_string("joysticktype"); + if (!strcasecmp(type,"none")) joytype = JOY_NONE; + else if (!strcasecmp(type,"false")) joytype = JOY_NONE; + else if (!strcasecmp(type,"auto")) joytype = JOY_AUTO; + else if (!strcasecmp(type,"2axis")) joytype = JOY_2AXIS; + else if (!strcasecmp(type,"4axis")) joytype = JOY_4AXIS; else if (!strcasecmp(type,"4axis_2")) joytype = JOY_4AXIS_2; - else if (!strcasecmp(type,"fcs")) joytype = JOY_FCS; - else if (!strcasecmp(type,"ch")) joytype = JOY_CH; + else if (!strcasecmp(type,"fcs")) joytype = JOY_FCS; + else if (!strcasecmp(type,"ch")) joytype = JOY_CH; else joytype = JOY_AUTO; bool timed = section->Get_bool("timed"); - if(timed) { + if (timed) { ReadHandler.Install(0x201,read_p201_timed,IO_MB); WriteHandler.Install(0x201,write_p201_timed,IO_MB); } else { @@ -212,10 +313,16 @@ public: autofire = section->Get_bool("autofire"); swap34 = section->Get_bool("swap34"); button_wrapping_enabled = section->Get_bool("buttonwrap"); - stick[0].enabled = false; - stick[1].enabled = false; stick[0].xtick = stick[0].ytick = stick[1].xtick = stick[1].ytick = PIC_FullIndex(); + stick[0].xpos = stick[0].ypos = stick[1].xpos = stick[1].ypos = 0.0f; + stick[0].transformed = false; + + + stick[0].mapstate = JoyStick::JOYMAP_SQUARE; + bool circ = section->Get_bool("circularinput"); + if (circ) stick[0].mapstate = JoyStick::JOYMAP_CIRCLE; + stick[0].deadzone = section->Get_int("deadzone"); } }; static JOYSTICK* test; diff --git a/src/hardware/keyboard.cpp b/src/hardware/keyboard.cpp index 1604e33..97fbd86 100644 --- a/src/hardware/keyboard.cpp +++ b/src/hardware/keyboard.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -115,7 +115,7 @@ static void write_p60(Bitu port,Bitu val,Bitu iolen) { KEYBOARD_AddBuffer(0xfa); /* Acknowledge */ break; case 0xee: /* Echo */ - KEYBOARD_AddBuffer(0xfa); /* Acknowledge */ + KEYBOARD_AddBuffer(0xee); /* Echo */ break; case 0xf2: /* Identify keyboard */ /* AT's just send acknowledge */ @@ -170,9 +170,11 @@ static void write_p60(Bitu port,Bitu val,Bitu iolen) { } } +extern bool TIMER_GetOutput2(void); static Bit8u port_61_data = 0; static Bitu read_p61(Bitu port,Bitu iolen) { - port_61_data^=0x20; + if (TIMER_GetOutput2()) port_61_data|=0x20; + else port_61_data&=~0x20; port_61_data^=0x10; return port_61_data; } @@ -186,6 +188,12 @@ static void write_p61(Bitu port,Bitu val,Bitu iolen) { port_61_data = val; } +static Bitu read_p62(Bitu port,Bitu iolen) { + Bit8u ret=~0x20; + if (TIMER_GetOutput2()) ret|=0x20; + return ret; +} + static void write_p64(Bitu port,Bitu val,Bitu iolen) { switch (val) { case 0xae: /* Activate keyboard */ @@ -339,7 +347,10 @@ void KEYBOARD_AddKey(KBD_KEYS keytype,bool pressed) { KEYBOARD_AddBuffer(69|(pressed?0:0x80)); return; case KBD_printscreen: - /* Not handled yet. But usuable in mapper for special events */ + KEYBOARD_AddBuffer(0xe0); + KEYBOARD_AddBuffer(42|(pressed?0:0x80)); + KEYBOARD_AddBuffer(0xe0); + KEYBOARD_AddBuffer(55|(pressed?0:0x80)); return; default: E_Exit("Unsupported key press"); @@ -374,6 +385,7 @@ void KEYBOARD_Init(Section* sec) { IO_RegisterReadHandler(0x60,read_p60,IO_MB); IO_RegisterWriteHandler(0x61,write_p61,IO_MB); IO_RegisterReadHandler(0x61,read_p61,IO_MB); + if (machine==MCH_CGA || machine==MCH_HERC) IO_RegisterReadHandler(0x62,read_p62,IO_MB); IO_RegisterWriteHandler(0x64,write_p64,IO_MB); IO_RegisterReadHandler(0x64,read_p64,IO_MB); TIMER_AddTickHandler(&KEYBOARD_TickHandler); diff --git a/src/hardware/mame/Makefile.am b/src/hardware/mame/Makefile.am new file mode 100644 index 0000000..3d551b1 --- /dev/null +++ b/src/hardware/mame/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LIBRARIES = libmame.a +libmame_a_SOURCES = emu.h \ + fmopl.cpp fmopl.h \ + saa1099.cpp saa1099.h \ + sn76496.cpp sn76496.h \ + ymdeltat.cpp ymdeltat.h \ + ymf262.cpp ymf262.h diff --git a/src/hardware/mame/emu.h b/src/hardware/mame/emu.h new file mode 100644 index 0000000..dfacd66 --- /dev/null +++ b/src/hardware/mame/emu.h @@ -0,0 +1,142 @@ +#ifndef DOSBOX_EMU_H +#define DOSBOX_EMU_H + + +#include "dosbox.h" +#if defined(_MSC_VER) && (_MSC_VER <= 1500) +#include +#else +#include +#endif +#include +#include +#include +#include + +#if C_DEBUG +#include +#include +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +typedef Bit16s stream_sample_t; + +typedef Bit8u u8; +typedef Bit32u u32; + +class device_t; +struct machine_config; + +#define NAME( _ASDF_ ) 0 +#define BIT( _INPUT_, _BIT_ ) ( ( _INPUT_) >> (_BIT_)) & 1 + +#define ATTR_UNUSED +#define DECLARE_READ8_MEMBER(name) u8 name( int, int) +#define DECLARE_WRITE8_MEMBER(name) void name( int, int, u8 data) +#define READ8_MEMBER(name) u8 name( int, int) +#define WRITE8_MEMBER(name) void name( int offset, int space, u8 data) + +#define DECLARE_DEVICE_TYPE(Type, Class) \ + extern const device_type Type; \ + class Class; + +#define DEFINE_DEVICE_TYPE(Type, Class, ShortName, FullName) \ + const device_type Type = 0; + + +class device_sound_interface { +public: + struct sound_stream { + void update() { + } + }; + sound_stream temp; + + sound_stream* stream_alloc(int whatever, int channels, int size) { + return &temp; + }; + + + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) = 0; + + device_sound_interface(const machine_config &mconfig, device_t& _device) { + } + +}; + +struct attotime { + int whatever; + + static attotime from_hz(int hz) { + return attotime(); + } +}; + +struct machine_config { +}; + +typedef int device_type; + +class device_t { + u32 clockRate; +public: + struct machine_t { + int describe_context() const { + return 0; + } + }; + + machine_t machine() const { + return machine_t(); + } + + //int offset, space; + + u32 clock() const { + return clockRate; + } + + void logerror(const char* format, ...) { +#if C_DEBUG + char buf[512*2]; + va_list msg; + va_start(msg,format); + vsprintf(buf,format,msg); + va_end(msg); + LOG(LOG_MISC,LOG_NORMAL)("%s",buf); +#endif + } + + static int tag() { + return 0; + } + + virtual void device_start() { + } + + void save_item(int wtf, int blah= 0) { + } + + device_t(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 _clock) : clockRate( _clock ) { + } + + virtual ~device_t() { + } +}; + + + +static void auto_free(const device_t::machine_t& machine, void * buffer) { + free(buffer); +} + +#define auto_alloc_array_clear(m, t, c) calloc(c, sizeof(t) ) +#define auto_alloc_clear(m, t) static_cast( calloc(1, sizeof(t) ) ) + + + + +#endif diff --git a/src/hardware/mame/fmopl.cpp b/src/hardware/mame/fmopl.cpp new file mode 100644 index 0000000..90b8834 --- /dev/null +++ b/src/hardware/mame/fmopl.cpp @@ -0,0 +1,2577 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh +/* +** +** File: fmopl.c - software implementation of FM sound generator +** types OPL and OPL2 +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 0.72 +** + +Revision History: + +04-08-2003 Jarek Burczynski: + - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip + handles memory read/write or during the adpcm synthesis when the chip + requests another byte of ADPCM data. + +24-07-2003 Jarek Burczynski: + - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after + some (unknown) delay). Right now it's always set. + +14-06-2003 Jarek Burczynski: + - implemented all of the status register flags in Y8950 emulation + - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since + they can be either RAM or ROM + +08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) + - corrected ym3526_read() to always set bit 2 and bit 1 + to HIGH state - identical to ym3812_read (verified on real YM3526) + +04-28-2002 Jarek Burczynski: + - binary exact Envelope Generator (verified on real YM3812); + compared to YM2151: the EG clock is equal to internal_clock, + rates are 2 times slower and volume resolution is one bit less + - modified interface functions (they no longer return pointer - + that's internal to the emulator now): + - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init() + - corrected 'off by one' error in feedback calculations (when feedback is off) + - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) + - speeded up noise generator calculations (Nicola Salmoria) + +03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) + Complete rewrite (all verified on real YM3812): + - corrected sin_tab and tl_tab data + - corrected operator output calculations + - corrected waveform_select_enable register; + simply: ignore all writes to waveform_select register when + waveform_select_enable == 0 and do not change the waveform previously selected. + - corrected KSR handling + - corrected Envelope Generator: attack shape, Sustain mode and + Percussive/Non-percussive modes handling + - Envelope Generator rates are two times slower now + - LFO amplitude (tremolo) and phase modulation (vibrato) + - rhythm sounds phase generation + - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) + - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) + - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) + +12-28-2001 Acho A. Tang + - reflected Delta-T EOS status on Y8950 status port. + - fixed subscription range of attack/decay tables + + + To do: + add delay before key off in CSM mode (see CSMKeyControll) + verify volume of the FM part on the Y8950 +*/ + +#include "emu.h" +#include "ymdeltat.h" +#include "fmopl.h" + + + +/* output final shift */ +#if (OPL_SAMPLE_BITS==16) + #define FINAL_SH (0) + #define MAXOUT (+32767) + #define MINOUT (-32768) +#else + #define FINAL_SH (8) + #define MAXOUT (+127) + #define MINOUT (-128) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<> 4 ) +#define LFO_AM_TAB_ELEMENTS 210 + + + +/* register number to channel number , slot offset */ +#define SLOT1 0 +#define SLOT2 1 + +/* Envelope Generator phases */ + +#define EG_ATT 4 +#define EG_DEC 3 +#define EG_SUS 2 +#define EG_REL 1 +#define EG_OFF 0 + + +/* save output as raw 16-bit sample */ + +/*#define SAVE_SAMPLE*/ + +#ifdef SAVE_SAMPLE +static inline signed int acc_calc(signed int value) +{ + if (value>=0) + { + if (value < 0x0200) + return (value & ~0); + if (value < 0x0400) + return (value & ~1); + if (value < 0x0800) + return (value & ~3); + if (value < 0x1000) + return (value & ~7); + if (value < 0x2000) + return (value & ~15); + if (value < 0x4000) + return (value & ~31); + return (value & ~63); + } + /*else value < 0*/ + if (value > -0x0200) + return (~abs(value) & ~0); + if (value > -0x0400) + return (~abs(value) & ~1); + if (value > -0x0800) + return (~abs(value) & ~3); + if (value > -0x1000) + return (~abs(value) & ~7); + if (value > -0x2000) + return (~abs(value) & ~15); + if (value > -0x4000) + return (~abs(value) & ~31); + return (~abs(value) & ~63); +} + + +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = acc_calc(lt); \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ +#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ +#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ +#define OPL_TYPE_IO 0x08 /* I/O port */ + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3526 (0) +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) +#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) + + +namespace { + +// TODO: make these static members + +#define RATE_STEPS (8) +extern const unsigned char eg_rate_shift[16+64+16]; +extern const unsigned char eg_rate_select[16+64+16]; + + +struct OPL_SLOT +{ + uint32_t ar; /* attack rate: AR<<2 */ + uint32_t dr; /* decay rate: DR<<2 */ + uint32_t rr; /* release rate:RR<<2 */ + uint8_t KSR; /* key scale rate */ + uint8_t ksl; /* keyscale level */ + uint8_t ksr; /* key scale rate: kcode>>KSR */ + uint8_t mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + uint32_t Cnt; /* frequency counter */ + uint32_t Incr; /* frequency counter step */ + uint8_t FB; /* feedback shift value */ + int32_t *connect1; /* slot1 output pointer */ + int32_t op1_out[2]; /* slot1 output for feedback */ + uint8_t CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + uint8_t eg_type; /* percussive/non-percussive mode */ + uint8_t state; /* phase type */ + uint32_t TL; /* total level: TL << 2 */ + int32_t TLL; /* adjusted now TL */ + int32_t volume; /* envelope counter */ + int32_t sl; /* sustain level: sl_tab[SL] */ + uint8_t eg_sh_ar; /* (attack state) */ + uint8_t eg_sel_ar; /* (attack state) */ + uint8_t eg_sh_dr; /* (decay state) */ + uint8_t eg_sel_dr; /* (decay state) */ + uint8_t eg_sh_rr; /* (release state) */ + uint8_t eg_sel_rr; /* (release state) */ + uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ + uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + uint16_t wavetable; + + void KEYON(uint32_t key_set) + { + if( !key ) + { + /* restart Phase Generator */ + Cnt = 0; + /* phase -> Attack */ + state = EG_ATT; + } + key |= key_set; + } + + void KEYOFF(uint32_t key_clr) + { + if( key ) + { + key &= key_clr; + + if( !key ) + { + /* phase -> Release */ + if (state>EG_REL) + state = EG_REL; + } + } + } +}; + +struct OPL_CH +{ + OPL_SLOT SLOT[2]; + /* phase generator state */ + uint32_t block_fnum; /* block+fnum */ + uint32_t fc; /* Freq. Increment base */ + uint32_t ksl_base; /* KeyScaleLevel Base step */ + uint8_t kcode; /* key code (for key scaling) */ + + + /* update phase increment counter of operator (also update the EG rates if necessary) */ + void CALC_FCSLOT(OPL_SLOT &SLOT) + { + /* (frequency) phase increment counter */ + SLOT.Incr = fc * SLOT.mul; + int const ksr = kcode >> SLOT.KSR; + + if( SLOT.ksr != ksr ) + { + SLOT.ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT.ar + SLOT.ksr) < 16+62) + { + SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; + SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; + } + else + { + SLOT.eg_sh_ar = 0; + SLOT.eg_sel_ar = 13*RATE_STEPS; + } + SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; + SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; + SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; + SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; + } + } +}; + +/* OPL state */ +struct FM_OPL +{ + /* FM channel slots */ + OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ + + uint32_t eg_cnt; /* global envelope generator counter */ + uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ + uint32_t eg_timer_add; /* step of eg_timer */ + uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */ + + uint8_t rhythm; /* Rhythm mode */ + + uint32_t fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + uint32_t LFO_AM; + int32_t LFO_PM; + + uint8_t lfo_am_depth; + uint8_t lfo_pm_depth_range; + uint32_t lfo_am_cnt; + uint32_t lfo_am_inc; + uint32_t lfo_pm_cnt; + uint32_t lfo_pm_inc; + + uint32_t noise_rng; /* 23 bit noise shift register */ + uint32_t noise_p; /* current noise 'phase' */ + uint32_t noise_f; /* current noise period */ + + uint8_t wavesel; /* waveform select enable flag */ + + uint32_t T[2]; /* timer counters */ + uint8_t st[2]; /* timer enable */ + +#if BUILD_Y8950 + /* Delta-T ADPCM unit (Y8950) */ + + YM_DELTAT *deltat; + + /* Keyboard and I/O ports interface */ + uint8_t portDirection; + uint8_t portLatch; + OPL_PORTHANDLER_R porthandler_r; + OPL_PORTHANDLER_W porthandler_w; + device_t * port_param; + OPL_PORTHANDLER_R keyboardhandler_r; + OPL_PORTHANDLER_W keyboardhandler_w; + device_t * keyboard_param; +#endif + + /* external event callback handlers */ + OPL_TIMERHANDLER timer_handler; /* TIMER handler */ + device_t *TimerParam; /* TIMER parameter */ + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ + device_t *IRQParam; /* IRQ parameter */ + OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */ + device_t *UpdateParam; /* stream update parameter */ + + uint8_t type; /* chip type */ + uint8_t address; /* address register */ + uint8_t status; /* status flag */ + uint8_t statusmask; /* status mask */ + uint8_t mode; /* Reg.08 : CSM,notesel,etc. */ + + uint32_t clock; /* master clock (Hz) */ + uint32_t rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + //attotime TimerBase; /* Timer base time (==sampling time)*/ + device_t *device; + + signed int phase_modulation; /* phase modulation input (SLOT 2) */ + signed int output[1]; +#if BUILD_Y8950 + int32_t output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */ +#endif + + + /* status set and IRQ handling */ + void STATUS_SET(int flag) + { + /* set status flag */ + status |= flag; + if(!(status & 0x80)) + { + if(status & statusmask) + { /* IRQ on */ + status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(IRQHandler) (IRQHandler)(IRQParam,1); + } + } + } + + /* status reset and IRQ handling */ + void STATUS_RESET(int flag) + { + /* reset status flag */ + status &=~flag; + if(status & 0x80) + { + if (!(status & statusmask) ) + { + status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(IRQHandler) (IRQHandler)(IRQParam,0); + } + } + } + + /* IRQ mask set */ + void STATUSMASK_SET(int flag) + { + statusmask = flag; + /* IRQ handling check */ + STATUS_SET(0); + STATUS_RESET(0); + } + + + /* advance LFO to next sample */ + void advance_lfo() + { + /* LFO */ + lfo_am_cnt += lfo_am_inc; + if (lfo_am_cnt >= (uint32_t(LFO_AM_TAB_ELEMENTS) << LFO_SH)) /* lfo_am_table is 210 elements long */ + lfo_am_cnt -= (uint32_t(LFO_AM_TAB_ELEMENTS) << LFO_SH); + + uint8_t const tmp = lfo_am_table[ lfo_am_cnt >> LFO_SH ]; + + LFO_AM = lfo_am_depth ? tmp : tmp >> 2; + + lfo_pm_cnt += lfo_pm_inc; + LFO_PM = (lfo_pm_cnt>>LFO_SH & 7) | lfo_pm_depth_range; + } + + /* advance to next sample */ + void advance() + { + eg_timer += eg_timer_add; + + while (eg_timer >= eg_timer_overflow) + { + eg_timer -= eg_timer_overflow; + + eg_cnt++; + + for (int i=0; i<9*2; i++) + { + OPL_CH &CH = P_CH[i/2]; + OPL_SLOT &op = CH.SLOT[i&1]; + + /* Envelope Generator */ + switch(op.state) + { + case EG_ATT: /* attack phase */ + if ( !(eg_cnt & ((1<>op.eg_sh_ar)&7)]) + ) >>3; + + if (op.volume <= MIN_ATT_INDEX) + { + op.volume = MIN_ATT_INDEX; + op.state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ + if ( !(eg_cnt & ((1<>op.eg_sh_dr)&7)]; + + if ( op.volume >= op.sl ) + op.state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op.eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ + if ( !(eg_cnt & ((1<>op.eg_sh_rr)&7)]; + + if ( op.volume >= MAX_ATT_INDEX ) + op.volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ + if ( !(eg_cnt & ((1<>op.eg_sh_rr)&7)]; + + if ( op.volume >= MAX_ATT_INDEX ) + { + op.volume = MAX_ATT_INDEX; + op.state = EG_OFF; + } + + } + break; + + default: + break; + } + } + } + + for (int i=0; i<9*2; i++) + { + OPL_CH &CH = P_CH[i/2]; + OPL_SLOT &op = CH.SLOT[i&1]; + + /* Phase Generator */ + if(op.vib) + { + unsigned int block_fnum = CH.block_fnum; + unsigned int const fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int const lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + uint8_t const block = (block_fnum&0x1c00) >> 10; + op.Cnt += (fn_tab[block_fnum&0x03ff] >> (7-block)) * op.mul; + } + else /* LFO phase modulation = zero */ + { + op.Cnt += op.Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op.Cnt += op.Incr; + } + } + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + noise_p += noise_f; + int i = noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + noise_p &= FREQ_MASK; + while (i) + { + /* + uint32_t j; + j = ( (noise_rng) ^ (noise_rng>>14) ^ (noise_rng>>15) ^ (noise_rng>>22) ) & 1; + noise_rng = (j<<22) | (noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (noise_rng & 1) noise_rng ^= 0x800302; + noise_rng >>= 1; + + i--; + } + } + + /* calculate output */ + void CALC_CH(OPL_CH &CH) + { + OPL_SLOT *SLOT; + unsigned int env; + signed int out; + + phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH.SLOT[SLOT1]; + env = volume_calc(*SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(*SLOT); + if( env < ENV_QUIET ) + output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable); + } + + /* + operators used in the rhythm sounds generation process: + + Envelope Generator: + + channel operator register number Bass High Snare Tom Top + / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + + channel operator register number Bass High Snare Tom Top + / slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + + channel operator register number Bass High Snare Tom Top + number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + + */ + + /* calculate rhythm */ + + void CALC_RH() + { + unsigned int const noise = BIT(noise_rng, 0); + + OPL_SLOT *SLOT; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + phase_modulation = 0; + /* SLOT 1 */ + SLOT = &P_CH[6].SLOT[SLOT1]; + env = volume_calc(*SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + phase_modulation = SLOT->op1_out[0]; + /* else ignore output of operator 1 */ + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(*SLOT); + if( env < ENV_QUIET ) + output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ + /* SD (16) channel 7->slot 1 */ + /* TOM (14) channel 8->slot 1 */ + /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ + + /* Envelope generation based on: */ + /* HH channel 7->slot1 */ + /* SD channel 7->slot2 */ + /* TOM channel 8->slot1 */ + /* TOP channel 8->slot2 */ + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + OPL_SLOT const &SLOT7_1 = P_CH[7].SLOT[SLOT1]; + OPL_SLOT const &SLOT8_2 = P_CH[8].SLOT[SLOT2]; + env = volume_calc(SLOT7_1); + if( env < ENV_QUIET ) + { + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char const bit7 = BIT(SLOT7_1.Cnt >> FREQ_SH, 7); + unsigned char const bit3 = BIT(SLOT7_1.Cnt >> FREQ_SH, 3); + unsigned char const bit2 = BIT(SLOT7_1.Cnt >> FREQ_SH, 2); + + unsigned char const res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char const bit5e= BIT(SLOT8_2.Cnt >> FREQ_SH, 5); + unsigned char const bit3e= BIT(SLOT8_2.Cnt >> FREQ_SH, 3); + + unsigned char const res2 = bit3e ^ bit5e; + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + output[0] += op_calc(phase<> FREQ_SH, 8); + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + uint32_t phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + output[0] += op_calc(phase<> FREQ_SH, 7); + unsigned char const bit3 = BIT(SLOT7_1.Cnt >> FREQ_SH, 3); + unsigned char const bit2 = BIT(SLOT7_1.Cnt >> FREQ_SH, 2); + + unsigned char const res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + uint32_t phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char const bit5e= BIT(SLOT8_2.Cnt >> FREQ_SH, 5); + unsigned char const bit3e= BIT(SLOT8_2.Cnt >> FREQ_SH, 3); + + unsigned char const res2 = bit3e ^ bit5e; + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + output[0] += op_calc(phase<> 6]; + SLOT.TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + SLOT.TLL = SLOT.TL + (CH.ksl_base >> SLOT.ksl); + } + + /* set attack rate & decay rate */ + void set_ar_dr(int slot, int v) + { + OPL_CH &CH = P_CH[slot/2]; + OPL_SLOT &SLOT = CH.SLOT[slot&1]; + + SLOT.ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT.ar + SLOT.ksr) < 16+62) + { + SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; + SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; + } + else + { + SLOT.eg_sh_ar = 0; + SLOT.eg_sel_ar = 13*RATE_STEPS; + } + + SLOT.dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; + SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; + } + + /* set sustain level & release rate */ + void set_sl_rr(int slot, int v) + { + OPL_CH &CH = P_CH[slot/2]; + OPL_SLOT &SLOT = CH.SLOT[slot&1]; + + SLOT.sl = sl_tab[ v>>4 ]; + + SLOT.rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; + SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; + } + + + void WriteReg(int r, int v); + void ResetChip(); + void postload(); + + + /* lock/unlock for common table */ + static int LockTable(device_t *device) + { + num_lock++; + if(num_lock>1) return 0; + + /* first time */ + + /* allocate total level table (128kb space) */ + if( !init_tables() ) + { + num_lock--; + return -1; + } + + return 0; + } + + static void UnLockTable() + { + if(num_lock) num_lock--; + if(num_lock) return; + + /* last time */ + CloseTable(); + } + +private: + uint32_t volume_calc(OPL_SLOT const &OP) const + { + return OP.TLL + uint32_t(OP.volume) + (LFO_AM & OP.AMmask); + } + + static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) + { + uint32_t const p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + return (p >= TL_TAB_LEN) ? 0 : tl_tab[p]; + } + + static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) + { + uint32_t const p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; + + return (p >= TL_TAB_LEN) ? 0 : tl_tab[p]; + } + + + static int init_tables(); + + static void CloseTable() + { +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif + } + + static const double ksl_tab[8*16]; + static const uint32_t ksl_shift[4]; + static const int32_t sl_tab[16]; + static const unsigned char eg_inc[15 * RATE_STEPS]; + + static const uint8_t mul_tab[16]; + static signed int tl_tab[TL_TAB_LEN]; + static unsigned int sin_tab[SIN_LEN * 4]; + + static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS]; + static const int8_t lfo_pm_table[8 * 8 * 2]; + + static int num_lock; +}; + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +const double FM_OPL::ksl_tab[8*16]= +{ + /* OCT 0 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + /* OCT 1 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, + 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, + /* OCT 2 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, + 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, + 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, + /* OCT 3 */ + 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, + 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, + 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, + 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, + /* OCT 4 */ + 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, + 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, + 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, + 10.875/DV,11.250/DV,11.625/DV,12.000/DV, + /* OCT 5 */ + 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, + 9.000/DV,10.125/DV,10.875/DV,11.625/DV, + 12.000/DV,12.750/DV,13.125/DV,13.500/DV, + 13.875/DV,14.250/DV,14.625/DV,15.000/DV, + /* OCT 6 */ + 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, + 12.000/DV,13.125/DV,13.875/DV,14.625/DV, + 15.000/DV,15.750/DV,16.125/DV,16.500/DV, + 16.875/DV,17.250/DV,17.625/DV,18.000/DV, + /* OCT 7 */ + 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, + 15.000/DV,16.125/DV,16.875/DV,17.625/DV, + 18.000/DV,18.750/DV,19.125/DV,19.500/DV, + 19.875/DV,20.250/DV,20.625/DV,21.000/DV +}; + +/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ +const uint32_t FM_OPL::ksl_shift[4] = { 31, 1, 2, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) int32_t(db * (2.0 / ENV_STEP)) +const int32_t FM_OPL::sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC( 3),SC( 4),SC( 5),SC( 6),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; + + +const unsigned char FM_OPL::eg_inc[15*RATE_STEPS]={ +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(13) in this table - it's directly in the code */ +const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +const uint8_t FM_OPL::mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML, + 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML +}; +#undef ML + +signed int FM_OPL::tl_tab[TL_TAB_LEN]; + +/* sin waveform table in 'decibel' scale */ +/* four waveforms on OPL2 type chips */ +unsigned int FM_OPL::sin_tab[SIN_LEN * 4]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (losing precision is important) +*/ + +const uint8_t FM_OPL::lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +const int8_t FM_OPL::lfo_pm_table[8*8*2] = { +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + +/* lock level of common table */ +int FM_OPL::num_lock = 0; + + + +static inline int limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + + +/* generic table initialize */ +int FM_OPL::init_tables() +{ + signed int i,x; + signed int n; + double o,m; + + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<12; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + #if 0 + logerror("tl %04i", x*2); + for (i=0; i<12; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); + logerror("\n"); + #endif + } + /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ + + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + + /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + + /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); + logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); + logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/ + } + /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ + + +#ifdef SAVE_SAMPLE + sample[0]=fopen("sampsum.pcm","wb"); +#endif + + return 1; +} + + +void FM_OPL::initialize() +{ + int i; + + /* frequency base */ + freqbase = (rate) ? ((double)clock / 72.0) / rate : 0; +#if 0 + rate = (double)clock / 72.0; + freqbase = 1.0; +#endif + + /*logerror("freqbase=%f\n", freqbase);*/ + + /* Timer base time */ + //TimerBase = attotime::from_hz(clock) * 72; + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + fn_tab[i] = (uint32_t)( (double)i * 64 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +#if 0 + logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n", + i, fn_tab[i]>>6, fn_tab[i]>>6 ); +#endif + } + +#if 0 + for( i=0 ; i < 16 ; i++ ) + { + logerror("FMOPL.C: sl_tab[%i] = %08x\n", + i, sl_tab[i] ); + } + for( i=0 ; i < 8 ; i++ ) + { + int j; + logerror("FMOPL.C: ksl_tab[oct=%2i] =",i); + for (j=0; j<16; j++) + { + logerror("%08x ", static_cast(ksl_tab[i*16+j]) ); + } + logerror("\n"); + } +#endif + + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + lfo_am_inc = (1.0 / 64.0 ) * (1<>1)&1; + + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + STATUS_RESET(v & (0x78-0x08)); + STATUSMASK_SET((~v) & 0x78); + + /* timer 2 */ + if(st[1] != st2) + { + attotime period = st2 ? (TimerBase * T[1]) : attotime::zero; + st[1] = st2; + if (timer_handler) (timer_handler)(TimerParam,1,period); + } + /* timer 1 */ + if(st[0] != st1) + { + attotime period = st1 ? (TimerBase * T[0]) : attotime::zero; + st[0] = st1; + if (timer_handler) (timer_handler)(TimerParam,0,period); + } + } + break; +#endif +#if BUILD_Y8950 + case 0x06: /* Key Board OUT */ + if(type&OPL_TYPE_KEYBOARD) + { + if(keyboardhandler_w) + keyboardhandler_w(keyboard_param,v); + else + device->logerror("Y8950: write unmapped KEYBOARD port\n"); + } + break; + case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ + if(type&OPL_TYPE_ADPCM) + deltat->ADPCM_Write(r-0x07,v); + break; +#endif + case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ + mode = v; +#if BUILD_Y8950 + if(type&OPL_TYPE_ADPCM) + deltat->ADPCM_Write(r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */ +#endif + break; + +#if BUILD_Y8950 + case 0x09: /* START ADD */ + case 0x0a: + case 0x0b: /* STOP ADD */ + case 0x0c: + case 0x0d: /* PRESCALE */ + case 0x0e: + case 0x0f: /* ADPCM data write */ + case 0x10: /* DELTA-N */ + case 0x11: /* DELTA-N */ + case 0x12: /* ADPCM volume */ + if(type&OPL_TYPE_ADPCM) + deltat->ADPCM_Write(r-0x07,v); + break; + + case 0x15: /* DAC data high 8 bits (F7,F6...F2) */ + case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */ + case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */ + device->logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v); + break; + + case 0x18: /* I/O CTRL (Direction) */ + if(type&OPL_TYPE_IO) + portDirection = v&0x0f; + break; + case 0x19: /* I/O DATA */ + if(type&OPL_TYPE_IO) + { + portLatch = v; + if(porthandler_w) + porthandler_w(port_param,v&portDirection); + } + break; +#endif + default: + device->logerror("FMOPL.C: write to unknown register: %02x\n",r); + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(slot,v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(slot,v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(slot,v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(slot,v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + lfo_am_depth = v & 0x80; + lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + rhythm = v&0x3f; + + if(rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + P_CH[6].SLOT[SLOT1].KEYON(2); + P_CH[6].SLOT[SLOT2].KEYON(2); + } + else + { + P_CH[6].SLOT[SLOT1].KEYOFF(~2); + P_CH[6].SLOT[SLOT2].KEYOFF(~2); + } + /* HH key on/off */ + if(v&0x01) P_CH[7].SLOT[SLOT1].KEYON ( 2); + else P_CH[7].SLOT[SLOT1].KEYOFF(~2); + /* SD key on/off */ + if(v&0x08) P_CH[7].SLOT[SLOT2].KEYON ( 2); + else P_CH[7].SLOT[SLOT2].KEYOFF(~2); + /* TOM key on/off */ + if(v&0x04) P_CH[8].SLOT[SLOT1].KEYON ( 2); + else P_CH[8].SLOT[SLOT1].KEYOFF(~2); + /* TOP-CY key on/off */ + if(v&0x02) P_CH[8].SLOT[SLOT2].KEYON ( 2); + else P_CH[8].SLOT[SLOT2].KEYOFF(~2); + } + else + { + /* BD key off */ + P_CH[6].SLOT[SLOT1].KEYOFF(~2); + P_CH[6].SLOT[SLOT2].KEYOFF(~2); + /* HH key off */ + P_CH[7].SLOT[SLOT1].KEYOFF(~2); + /* SD key off */ + P_CH[7].SLOT[SLOT2].KEYOFF(~2); + /* TOM key off */ + P_CH[8].SLOT[SLOT1].KEYOFF(~2); + /* TOP-CY off */ + P_CH[8].SLOT[SLOT2].KEYOFF(~2); + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if(v&0x20) + { + CH->SLOT[SLOT1].KEYON ( 1); + CH->SLOT[SLOT2].KEYON ( 1); + } + else + { + CH->SLOT[SLOT1].KEYOFF(~1); + CH->SLOT[SLOT2].KEYOFF(~1); + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + uint8_t block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = static_cast(ksl_tab[block_fnum>>6]); + CH->fc = fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (mode&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CH->CALC_FCSLOT(CH->SLOT[SLOT1]); + CH->CALC_FCSLOT(CH->SLOT[SLOT2]); + } + break; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &P_CH[r&0x0f]; + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &output[0] : &phase_modulation; + break; + case 0xe0: /* waveform select */ + /* simply ignore write to the waveform select register if selecting not enabled in test register */ + if(wavesel) + { + slot = slot_array[r&0x1f]; + if(slot < 0) return; + CH = &P_CH[slot/2]; + + CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; + } + break; + } +} + + +void FM_OPL::ResetChip() +{ + eg_timer = 0; + eg_cnt = 0; + + noise_rng = 1; /* noise shift register */ + mode = 0; /* normal mode */ + STATUS_RESET(0x7f); + + /* reset with register write */ + WriteReg(0x01,0); /* wavesel disable */ + WriteReg(0x02,0); /* Timer1 */ + WriteReg(0x03,0); /* Timer2 */ + WriteReg(0x04,0); /* IRQ mask clear */ + for(int i = 0xff ; i >= 0x20 ; i-- ) WriteReg(i,0); + + /* reset operator parameters */ +// for(OPL_CH &CH : P_CH) + for(int ch = 0; ch < sizeof( P_CH )/ sizeof(P_CH[0]); ch++) + { + OPL_CH &CH = P_CH[ch]; +// for(OPL_SLOT &SLOT : CH.SLOT) + for(int slot = 0; slot < sizeof( CH.SLOT ) / sizeof( CH.SLOT[0]); slot++) + { + + OPL_SLOT &SLOT = CH.SLOT[slot]; + /* wave table */ + SLOT.wavetable = 0; + SLOT.state = EG_OFF; + SLOT.volume = MAX_ATT_INDEX; + } + } +#if BUILD_Y8950 + if(type&OPL_TYPE_ADPCM) + { + YM_DELTAT *DELTAT = deltat; + + DELTAT->freqbase = freqbase; + DELTAT->output_pointer = &output_deltat[0]; + DELTAT->portshift = 5; + DELTAT->output_range = 1<<23; + DELTAT->ADPCM_Reset(0,YM_DELTAT::EMULATION_MODE_NORMAL,device); + } +#endif +} + + +void FM_OPL::postload() +{ + for(int ch = 0; ch < sizeof( P_CH )/ sizeof(P_CH[0]); ch++) + { + OPL_CH &CH = P_CH[ch]; + /* Look up key scale level */ + uint32_t const block_fnum = CH.block_fnum; + CH.ksl_base = static_cast(ksl_tab[block_fnum >> 6]); + CH.fc = fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10)); + + for(int slot = 0; slot < sizeof( CH.SLOT ) / sizeof( CH.SLOT[0]); slot++) + { + OPL_SLOT &SLOT = CH.SLOT[slot]; + /* Calculate key scale rate */ + SLOT.ksr = CH.kcode >> SLOT.KSR; + + /* Calculate attack, decay and release rates */ + if ((SLOT.ar + SLOT.ksr) < 16+62) + { + SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; + SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; + } + else + { + SLOT.eg_sh_ar = 0; + SLOT.eg_sel_ar = 13*RATE_STEPS; + } + SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; + SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; + SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; + SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; + + /* Calculate phase increment */ + SLOT.Incr = CH.fc * SLOT.mul; + + /* Total level */ + SLOT.TLL = SLOT.TL + (CH.ksl_base >> SLOT.ksl); + + /* Connect output */ + SLOT.connect1 = SLOT.CON ? &output[0] : &phase_modulation; + } + } +#if BUILD_Y8950 + if ( (type & OPL_TYPE_ADPCM) && (deltat) ) + { + // We really should call the postlod function for the YM_DELTAT, but it's hard without registers + // (see the way the YM2610 does it) + //deltat->postload(REGS); + } +#endif +} + +} // anonymous namespace + + +#if 0 +static void OPLsave_state_channel(device_t *device, OPL_CH *CH) +{ + int slot, ch; + + for( ch=0 ; ch < 9 ; ch++, CH++ ) + { + /* channel */ + device->save_item(NAME(CH->block_fnum), ch); + device->save_item(NAME(CH->kcode), ch); + /* slots */ + for( slot=0 ; slot < 2 ; slot++ ) + { + OPL_SLOT *SLOT = &CH->SLOT[slot]; + + device->save_item(NAME(SLOT->ar), ch * 2 + slot); + device->save_item(NAME(SLOT->dr), ch * 2 + slot); + device->save_item(NAME(SLOT->rr), ch * 2 + slot); + device->save_item(NAME(SLOT->KSR), ch * 2 + slot); + device->save_item(NAME(SLOT->ksl), ch * 2 + slot); + device->save_item(NAME(SLOT->mul), ch * 2 + slot); + + device->save_item(NAME(SLOT->Cnt), ch * 2 + slot); + device->save_item(NAME(SLOT->FB), ch * 2 + slot); + device->save_item(NAME(SLOT->op1_out), ch * 2 + slot); + device->save_item(NAME(SLOT->CON), ch * 2 + slot); + + device->save_item(NAME(SLOT->eg_type), ch * 2 + slot); + device->save_item(NAME(SLOT->state), ch * 2 + slot); + device->save_item(NAME(SLOT->TL), ch * 2 + slot); + device->save_item(NAME(SLOT->volume), ch * 2 + slot); + device->save_item(NAME(SLOT->sl), ch * 2 + slot); + device->save_item(NAME(SLOT->key), ch * 2 + slot); + + device->save_item(NAME(SLOT->AMmask), ch * 2 + slot); + device->save_item(NAME(SLOT->vib), ch * 2 + slot); + + device->save_item(NAME(SLOT->wavetable), ch * 2 + slot); + } + } +} + +/* Register savestate for a virtual YM3812/YM3526Y8950 */ + +static void OPL_save_state(FM_OPL *OPL, device_t *device) +{ + OPLsave_state_channel(device, OPL->P_CH); + + device->save_item(NAME(OPL->eg_cnt)); + device->save_item(NAME(OPL->eg_timer)); + + device->save_item(NAME(OPL->rhythm)); + + device->save_item(NAME(OPL->lfo_am_depth)); + device->save_item(NAME(OPL->lfo_pm_depth_range)); + device->save_item(NAME(OPL->lfo_am_cnt)); + device->save_item(NAME(OPL->lfo_pm_cnt)); + + device->save_item(NAME(OPL->noise_rng)); + device->save_item(NAME(OPL->noise_p)); + + if( OPL->type & OPL_TYPE_WAVESEL ) + { + device->save_item(NAME(OPL->wavesel)); + } + + device->save_item(NAME(OPL->T)); + device->save_item(NAME(OPL->st)); + +#if BUILD_Y8950 + if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) + { + OPL->deltat->savestate(device); + } + + if ( OPL->type & OPL_TYPE_IO ) + { + device->save_item(NAME(OPL->portDirection)); + device->save_item(NAME(OPL->portLatch)); + } +#endif + + device->save_item(NAME(OPL->address)); + device->save_item(NAME(OPL->status)); + device->save_item(NAME(OPL->statusmask)); + device->save_item(NAME(OPL->mode)); + + device->machine().save().register_postload(save_prepost_delegate(FUNC(FM_OPL::postload), OPL)); +} + +#endif + +static void OPL_clock_changed(FM_OPL *OPL, uint32_t clock, uint32_t rate) +{ + OPL->clock = clock; + OPL->rate = rate; + + /* init global tables */ + OPL->initialize(); +} + + +/* Create one of virtual YM3812/YM3526/Y8950 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +static FM_OPL *OPLCreate(device_t *device, uint32_t clock, uint32_t rate, int type) +{ + char *ptr; + FM_OPL *OPL; + int state_size; + + if (FM_OPL::LockTable(device) == -1) return 0; + + /* calculate OPL state size */ + state_size = sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); +#endif + + /* allocate memory block */ + ptr = (char *)auto_alloc_array_clear(device->machine(), uint8_t, state_size); + + OPL = (FM_OPL *)ptr; + + ptr += sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) + { + OPL->deltat = (YM_DELTAT *)ptr; + } + ptr += sizeof(YM_DELTAT); +#endif + + OPL->device = device; + OPL->type = type; + OPL_clock_changed(OPL, clock, rate); + + return OPL; +} + +/* Destroy one of virtual YM3812 */ +static void OPLDestroy(FM_OPL *OPL) +{ + FM_OPL::UnLockTable(); + auto_free(OPL->device->machine(), OPL); +} + +/* Optional handlers */ + +static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,device_t *device) +{ + OPL->timer_handler = timer_handler; + OPL->TimerParam = device; +} +static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,device_t *device) +{ + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = device; +} +static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,device_t *device) +{ + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = device; +} + +static int OPLWrite(FM_OPL *OPL,int a,int v) +{ + if( !(a&1) ) + { /* address port */ + OPL->address = v & 0xff; + } + else + { /* data port */ + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + OPL->WriteReg(OPL->address,v); + } + return OPL->status>>7; +} + +static unsigned char OPLRead(FM_OPL *OPL,int a) +{ + if( !(a&1) ) + { + /* status port */ + + #if BUILD_Y8950 + + if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */ + { + return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1); + } + + #endif + + /* OPL and OPL2 */ + return OPL->status & (OPL->statusmask|0x80); + } + +#if BUILD_Y8950 + /* data port */ + switch(OPL->address) + { + case 0x05: /* KeyBoard IN */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_r) + return OPL->keyboardhandler_r(OPL->keyboard_param); + else + OPL->device->logerror("Y8950: read unmapped KEYBOARD port\n"); + } + return 0; + + case 0x0f: /* ADPCM-DATA */ + if(OPL->type&OPL_TYPE_ADPCM) + { + uint8_t val; + + val = OPL->deltat->ADPCM_Read(); + /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/ + return val; + } + return 0; + + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + if(OPL->porthandler_r) + return OPL->porthandler_r(OPL->port_param); + else + OPL->device->logerror("Y8950:read unmapped I/O port\n"); + } + return 0; + case 0x1a: /* PCM-DATA */ + if(OPL->type&OPL_TYPE_ADPCM) + { + OPL->device->logerror("Y8950 A/D conversion is accessed but not implemented !\n"); + return 0x80; /* 2's complement PCM data - result from A/D conversion */ + } + return 0; + } +#endif + + return 0xff; +} + +/* CSM Key Controll */ +static inline void CSMKeyControll(OPL_CH *CH) +{ + CH->SLOT[SLOT1].KEYON(4); + CH->SLOT[SLOT2].KEYON(4); + + /* The key off should happen exactly one sample later - not implemented correctly yet */ + + CH->SLOT[SLOT1].KEYOFF(~4); + CH->SLOT[SLOT2].KEYOFF(~4); +} + +static int OPLTimerOver(FM_OPL *OPL,int c) +{ + if( c ) + { /* Timer B */ + OPL->STATUS_SET(0x20); + } + else + { /* Timer A */ + OPL->STATUS_SET(0x40); + /* CSM mode key,TL controll */ + if( OPL->mode & 0x80 ) + { /* CSM mode total level latch and auto key on */ + int ch; + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + for(ch=0; ch<9; ch++) + CSMKeyControll( &OPL->P_CH[ch] ); + } + } + /* reload timer */ + //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,OPL->TimerBase * OPL->T[c]); + return OPL->status>>7; +} + +#define MAX_OPL_CHIPS 2 + + +#if BUILD_YM3812 + +void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate) +{ + OPL_clock_changed((FM_OPL *)chip, clock, rate); +} + +void * ym3812_init(device_t *device, uint32_t clock, uint32_t rate) +{ + /* emulator create */ + FM_OPL *YM3812 = OPLCreate(device,clock,rate,OPL_TYPE_YM3812); + if (YM3812) + { + //OPL_save_state(YM3812, device); + ym3812_reset_chip(YM3812); + } + return YM3812; +} + +void ym3812_shutdown(void *chip) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + + /* emulator shutdown */ + OPLDestroy(YM3812); +} +void ym3812_reset_chip(void *chip) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + YM3812->ResetChip(); +} + +int ym3812_write(void *chip, int a, int v) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + return OPLWrite(YM3812, a, v); +} + +unsigned char ym3812_read(void *chip, int a) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + /* YM3812 always returns bit2 and bit1 in HIGH state */ + return OPLRead(YM3812, a) | 0x06 ; +} +int ym3812_timer_over(void *chip, int c) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + return OPLTimerOver(YM3812, c); +} + +void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetTimerHandler(YM3812, timer_handler, device); +} +void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetIRQHandler(YM3812, IRQHandler, device); +} +void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetUpdateHandler(YM3812, UpdateHandler, device); +} + +/* +** Generate samples for one of the YM3812's +** +** 'which' is the virtual YM3812 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length) +{ + FM_OPL *OPL = (FM_OPL *)chip; + uint8_t rhythm = OPL->rhythm&0x20; + OPLSAMPLE *buf = buffer; + int i; + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + + OPL->advance_lfo(); + + /* FM part */ + OPL->CALC_CH(OPL->P_CH[0]); + OPL->CALC_CH(OPL->P_CH[1]); + OPL->CALC_CH(OPL->P_CH[2]); + OPL->CALC_CH(OPL->P_CH[3]); + OPL->CALC_CH(OPL->P_CH[4]); + OPL->CALC_CH(OPL->P_CH[5]); + + if(!rhythm) + { + OPL->CALC_CH(OPL->P_CH[6]); + OPL->CALC_CH(OPL->P_CH[7]); + OPL->CALC_CH(OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL->CALC_RH(); + } + + lt = OPL->output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + OPL->advance(); + } + +} +#endif /* BUILD_YM3812 */ + + + +#if (BUILD_YM3526) + +void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate) +{ + OPL_clock_changed((FM_OPL *)chip, clock, rate); +} + +void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate) +{ + /* emulator create */ + FM_OPL *YM3526 = OPLCreate(device,clock,rate,OPL_TYPE_YM3526); + if (YM3526) + { + //OPL_save_state(YM3526, device); + ym3526_reset_chip(YM3526); + } + return YM3526; +} + +void ym3526_shutdown(void *chip) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + /* emulator shutdown */ + OPLDestroy(YM3526); +} +void ym3526_reset_chip(void *chip) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + YM3526->ResetChip(); +} + +int ym3526_write(void *chip, int a, int v) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + return OPLWrite(YM3526, a, v); +} + +unsigned char ym3526_read(void *chip, int a) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + /* YM3526 always returns bit2 and bit1 in HIGH state */ + return OPLRead(YM3526, a) | 0x06 ; +} +int ym3526_timer_over(void *chip, int c) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + return OPLTimerOver(YM3526, c); +} + +void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetTimerHandler(YM3526, timer_handler, device); +} +void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetIRQHandler(YM3526, IRQHandler, device); +} +void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetUpdateHandler(YM3526, UpdateHandler, device); +} + + +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length) +{ + FM_OPL *OPL = (FM_OPL *)chip; + uint8_t rhythm = OPL->rhythm&0x20; + OPLSAMPLE *buf = buffer; + int i; + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + + OPL->advance_lfo(); + + /* FM part */ + OPL->CALC_CH(OPL->P_CH[0]); + OPL->CALC_CH(OPL->P_CH[1]); + OPL->CALC_CH(OPL->P_CH[2]); + OPL->CALC_CH(OPL->P_CH[3]); + OPL->CALC_CH(OPL->P_CH[4]); + OPL->CALC_CH(OPL->P_CH[5]); + + if(!rhythm) + { + OPL->CALC_CH(OPL->P_CH[6]); + OPL->CALC_CH(OPL->P_CH[7]); + OPL->CALC_CH(OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL->CALC_RH(); + } + + lt = OPL->output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + OPL->advance(); + } + +} +#endif /* BUILD_YM3526 */ + + + + +#if BUILD_Y8950 + +static void Y8950_deltat_status_set(void *chip, uint8_t changebits) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + Y8950->STATUS_SET(changebits); +} +static void Y8950_deltat_status_reset(void *chip, uint8_t changebits) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + Y8950->STATUS_RESET(changebits); +} + +void *y8950_init(device_t *device, uint32_t clock, uint32_t rate) +{ + /* emulator create */ + FM_OPL *Y8950 = OPLCreate(device,clock,rate,OPL_TYPE_Y8950); + if (Y8950) + { + Y8950->deltat->status_set_handler = Y8950_deltat_status_set; + Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset; + Y8950->deltat->status_change_which_chip = Y8950; + Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */ + Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */ + + /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */ + /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */ + /* reset */ + //OPL_save_state(Y8950, device); + y8950_reset_chip(Y8950); + } + + return Y8950; +} + +void y8950_shutdown(void *chip) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + /* emulator shutdown */ + OPLDestroy(Y8950); +} +void y8950_reset_chip(void *chip) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + Y8950->ResetChip(); +} + +int y8950_write(void *chip, int a, int v) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLWrite(Y8950, a, v); +} + +unsigned char y8950_read(void *chip, int a) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLRead(Y8950, a); +} +int y8950_timer_over(void *chip, int c) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLTimerOver(Y8950, c); +} + +void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetTimerHandler(Y8950, timer_handler, device); +} +void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetIRQHandler(Y8950, IRQHandler, device); +} +void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetUpdateHandler(Y8950, UpdateHandler, device); +} + +void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->deltat->memory = (uint8_t *)(deltat_mem_ptr); + OPL->deltat->memory_size = deltat_mem_size; +} + +/* +** Generate samples for one of the Y8950's +** +** 'which' is the virtual Y8950 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length) +{ + int i; + FM_OPL *OPL = (FM_OPL *)chip; + uint8_t rhythm = OPL->rhythm&0x20; + YM_DELTAT *DELTAT = OPL->deltat; + OPLSAMPLE *buf = buffer; + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + OPL->output_deltat[0] = 0; + + OPL->advance_lfo(); + + /* deltaT ADPCM */ + if( DELTAT->portstate&0x80 ) + DELTAT->ADPCM_CALC(); + + /* FM part */ + OPL->CALC_CH(OPL->P_CH[0]); + OPL->CALC_CH(OPL->P_CH[1]); + OPL->CALC_CH(OPL->P_CH[2]); + OPL->CALC_CH(OPL->P_CH[3]); + OPL->CALC_CH(OPL->P_CH[4]); + OPL->CALC_CH(OPL->P_CH[5]); + + if(!rhythm) + { + OPL->CALC_CH(OPL->P_CH[6]); + OPL->CALC_CH(OPL->P_CH[7]); + OPL->CALC_CH(OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL->CALC_RH(); + } + + lt = OPL->output[0] + (OPL->output_deltat[0]>>11); + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + OPL->advance(); + } + +} + +void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,device_t *device) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->porthandler_w = PortHandler_w; + OPL->porthandler_r = PortHandler_r; + OPL->port_param = device; +} + +void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,device_t *device) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->keyboardhandler_w = KeyboardHandler_w; + OPL->keyboardhandler_r = KeyboardHandler_r; + OPL->keyboard_param = device; +} + + +#endif diff --git a/src/hardware/mame/fmopl.h b/src/hardware/mame/fmopl.h new file mode 100644 index 0000000..3ea60ad --- /dev/null +++ b/src/hardware/mame/fmopl.h @@ -0,0 +1,113 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh +#ifndef MAME_SOUND_FMOPL_H +#define MAME_SOUND_FMOPL_H + +#pragma once + +#if defined(_MSC_VER) && (_MSC_VER <= 1500) +#include +#else +#include +#endif + + +/* --- select emulation chips --- */ +#define BUILD_YM3812 1 +#define BUILD_YM3526 (0) +#define BUILD_Y8950 (0) + +/* select output bits size of output : 8 or 16 */ +#define OPL_SAMPLE_BITS 16 + +typedef stream_sample_t OPLSAMPLE; +/* +#if (OPL_SAMPLE_BITS==16) +typedef int16_t OPLSAMPLE; +#endif +#if (OPL_SAMPLE_BITS==8) +typedef int8_t OPLSAMPLE; +#endif +*/ + +typedef void (*OPL_TIMERHANDLER)(device_t *device,int timer,const attotime &period); +typedef void (*OPL_IRQHANDLER)(device_t *device,int irq); +typedef void (*OPL_UPDATEHANDLER)(device_t *device,int min_interval_us); +typedef void (*OPL_PORTHANDLER_W)(device_t *device,unsigned char data); +typedef unsigned char (*OPL_PORTHANDLER_R)(device_t *device); + + +#if BUILD_YM3812 + +void *ym3812_init(device_t *device, uint32_t clock, uint32_t rate); +void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate); +void ym3812_shutdown(void *chip); +void ym3812_reset_chip(void *chip); +int ym3812_write(void *chip, int a, int v); +unsigned char ym3812_read(void *chip, int a); +int ym3812_timer_over(void *chip, int c); +void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length); + +void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); +void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); +void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); + +#endif /* BUILD_YM3812 */ + + +#if BUILD_YM3526 + +/* +** Initialize YM3526 emulator(s). +** +** 'num' is the number of virtual YM3526's to allocate +** 'clock' is the chip clock in Hz +** 'rate' is sampling rate +*/ +void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate); +void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate); +/* shutdown the YM3526 emulators*/ +void ym3526_shutdown(void *chip); +void ym3526_reset_chip(void *chip); +int ym3526_write(void *chip, int a, int v); +unsigned char ym3526_read(void *chip, int a); +int ym3526_timer_over(void *chip, int c); +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length); + +void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); +void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); +void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); + +#endif /* BUILD_YM3526 */ + + +#if BUILD_Y8950 + +/* Y8950 port handlers */ +void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, device_t *device); +void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, device_t *device); +void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ); + +void * y8950_init(device_t *device, uint32_t clock, uint32_t rate); +void y8950_shutdown(void *chip); +void y8950_reset_chip(void *chip); +int y8950_write(void *chip, int a, int v); +unsigned char y8950_read (void *chip, int a); +int y8950_timer_over(void *chip, int c); +void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length); + +void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); +void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); +void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); + +#endif /* BUILD_Y8950 */ + + +#endif // MAME_SOUND_FMOPL_H diff --git a/src/hardware/mame/saa1099.cpp b/src/hardware/mame/saa1099.cpp new file mode 100644 index 0000000..7f9823e --- /dev/null +++ b/src/hardware/mame/saa1099.cpp @@ -0,0 +1,471 @@ +// license:BSD-3-Clause +// copyright-holders:Juergen Buchmueller, Manuel Abadia +/*************************************************************************** + + Philips SAA1099 Sound driver + + By Juergen Buchmueller and Manuel Abadia + + SAA1099 register layout: + ======================== + + offs | 7654 3210 | description + -----+-----------+--------------------------- + 0x00 | ---- xxxx | Amplitude channel 0 (left) + 0x00 | xxxx ---- | Amplitude channel 0 (right) + 0x01 | ---- xxxx | Amplitude channel 1 (left) + 0x01 | xxxx ---- | Amplitude channel 1 (right) + 0x02 | ---- xxxx | Amplitude channel 2 (left) + 0x02 | xxxx ---- | Amplitude channel 2 (right) + 0x03 | ---- xxxx | Amplitude channel 3 (left) + 0x03 | xxxx ---- | Amplitude channel 3 (right) + 0x04 | ---- xxxx | Amplitude channel 4 (left) + 0x04 | xxxx ---- | Amplitude channel 4 (right) + 0x05 | ---- xxxx | Amplitude channel 5 (left) + 0x05 | xxxx ---- | Amplitude channel 5 (right) + | | + 0x08 | xxxx xxxx | Frequency channel 0 + 0x09 | xxxx xxxx | Frequency channel 1 + 0x0a | xxxx xxxx | Frequency channel 2 + 0x0b | xxxx xxxx | Frequency channel 3 + 0x0c | xxxx xxxx | Frequency channel 4 + 0x0d | xxxx xxxx | Frequency channel 5 + | | + 0x10 | ---- -xxx | Channel 0 octave select + 0x10 | -xxx ---- | Channel 1 octave select + 0x11 | ---- -xxx | Channel 2 octave select + 0x11 | -xxx ---- | Channel 3 octave select + 0x12 | ---- -xxx | Channel 4 octave select + 0x12 | -xxx ---- | Channel 5 octave select + | | + 0x14 | ---- ---x | Channel 0 frequency enable (0 = off, 1 = on) + 0x14 | ---- --x- | Channel 1 frequency enable (0 = off, 1 = on) + 0x14 | ---- -x-- | Channel 2 frequency enable (0 = off, 1 = on) + 0x14 | ---- x--- | Channel 3 frequency enable (0 = off, 1 = on) + 0x14 | ---x ---- | Channel 4 frequency enable (0 = off, 1 = on) + 0x14 | --x- ---- | Channel 5 frequency enable (0 = off, 1 = on) + | | + 0x15 | ---- ---x | Channel 0 noise enable (0 = off, 1 = on) + 0x15 | ---- --x- | Channel 1 noise enable (0 = off, 1 = on) + 0x15 | ---- -x-- | Channel 2 noise enable (0 = off, 1 = on) + 0x15 | ---- x--- | Channel 3 noise enable (0 = off, 1 = on) + 0x15 | ---x ---- | Channel 4 noise enable (0 = off, 1 = on) + 0x15 | --x- ---- | Channel 5 noise enable (0 = off, 1 = on) + | | + 0x16 | ---- --xx | Noise generator parameters 0 + 0x16 | --xx ---- | Noise generator parameters 1 + | | + 0x18 | --xx xxxx | Envelope generator 0 parameters + 0x18 | x--- ---- | Envelope generator 0 control enable (0 = off, 1 = on) + 0x19 | --xx xxxx | Envelope generator 1 parameters + 0x19 | x--- ---- | Envelope generator 1 control enable (0 = off, 1 = on) + | | + 0x1c | ---- ---x | All channels enable (0 = off, 1 = on) + 0x1c | ---- --x- | Synch & Reset generators + + Unspecified bits should be written as zero. + +***************************************************************************/ + +#include "emu.h" +#include "saa1099.h" + +#define LEFT 0x00 +#define RIGHT 0x01 + +static const int amplitude_lookup[16] = { + 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, + 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, + 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, + 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 +}; + +static const uint8_t envelope[8][64] = { + /* zero amplitude */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* maximum amplitude */ + {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, + /* single decay */ + {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive decay */ + {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, + /* single triangular */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive triangular */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, + /* single attack */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive attack */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } +}; + + +// device type definition +//DEFINE_DEVICE_TYPE(SAA1099, saa1099_device, "saa1099", "Philips SAA1099") +#define SAA1099 1 + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// saa1099_device - constructor +//------------------------------------------------- + +#define FILL_ARRAY( _FILL_ ) memset( _FILL_, 0, sizeof( _FILL_ ) ) + +saa1099_device::saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SAA1099, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_stream(0) +#if 0 + , m_noise_params{ 0, 0 } + , m_env_enable{ 0, 0 } + , m_env_reverse_right{ 0, 0 } + , m_env_mode{ 0, 0 } + , m_env_bits{ 0, 0 } + , m_env_clock{ 0, 0 } + , m_env_step{ 0, 0 } +#endif + , m_all_ch_enable(0) + , m_sync_state(0) + , m_selected_reg(0) + , m_sample_rate(0.0) +{ + FILL_ARRAY( m_noise_params ); + FILL_ARRAY( m_env_enable ); + FILL_ARRAY( m_env_reverse_right ); + FILL_ARRAY( m_env_mode ); + FILL_ARRAY( m_env_bits ); + FILL_ARRAY( m_env_clock ); + FILL_ARRAY( m_env_step ); + +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void saa1099_device::device_start() +{ + /* copy global parameters */ + m_master_clock = clock(); + m_sample_rate = clock() / 256; + + /* for each chip allocate one stream */ + m_stream = stream_alloc(0, 2, m_sample_rate); + + save_item(NAME(m_noise_params)); + save_item(NAME(m_env_enable)); + save_item(NAME(m_env_reverse_right)); + save_item(NAME(m_env_mode)); + save_item(NAME(m_env_bits)); + save_item(NAME(m_env_clock)); + save_item(NAME(m_env_step)); + save_item(NAME(m_all_ch_enable)); + save_item(NAME(m_sync_state)); + save_item(NAME(m_selected_reg)); + + for (int i = 0; i < 6; i++) + { + save_item(NAME(m_channels[i].frequency), i); + save_item(NAME(m_channels[i].freq_enable), i); + save_item(NAME(m_channels[i].noise_enable), i); + save_item(NAME(m_channels[i].octave), i); + save_item(NAME(m_channels[i].amplitude), i); + save_item(NAME(m_channels[i].envelope), i); + save_item(NAME(m_channels[i].counter), i); + save_item(NAME(m_channels[i].freq), i); + save_item(NAME(m_channels[i].level), i); + } + + for (int i = 0; i < 2; i++) + { + save_item(NAME(m_noise[i].counter), i); + save_item(NAME(m_noise[i].freq), i); + save_item(NAME(m_noise[i].level), i); + } +} + + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void saa1099_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + int j, ch; + /* if the channels are disabled we're done */ + if (!m_all_ch_enable) + { + /* init output data */ + memset(outputs[LEFT],0,samples*sizeof(*outputs[LEFT])); + memset(outputs[RIGHT],0,samples*sizeof(*outputs[RIGHT])); + return; + } + + for (ch = 0; ch < 2; ch++) + { + switch (m_noise_params[ch]) + { + case 0: m_noise[ch].freq = m_master_clock/256.0 * 2; break; + case 1: m_noise[ch].freq = m_master_clock/512.0 * 2; break; + case 2: m_noise[ch].freq = m_master_clock/1024.0 * 2; break; + case 3: m_noise[ch].freq = m_channels[ch * 3].freq; break; // todo: this case will be m_master_clock/[ch*3's octave divisor, 0 is = 256*2, higher numbers are higher] * 2 if the tone generator phase reset bit (0x1c bit 1) is set. + } + } + + /* fill all data needed */ + for( j = 0; j < samples; j++ ) + { + int output_l = 0, output_r = 0; + + /* for each channel */ + for (ch = 0; ch < 6; ch++) + { + if (m_channels[ch].freq == 0.0) + m_channels[ch].freq = (double)((2 * m_master_clock / 512) << m_channels[ch].octave) / + (511.0 - (double)m_channels[ch].frequency); + + /* check the actual position in the square wave */ + m_channels[ch].counter -= m_channels[ch].freq; + while (m_channels[ch].counter < 0) + { + /* calculate new frequency now after the half wave is updated */ + m_channels[ch].freq = (double)((2 * m_master_clock / 512) << m_channels[ch].octave) / + (511.0 - (double)m_channels[ch].frequency); + + m_channels[ch].counter += m_sample_rate; + m_channels[ch].level ^= 1; + + /* eventually clock the envelope counters */ + if (ch == 1 && m_env_clock[0] == 0) + envelope_w(0); + if (ch == 4 && m_env_clock[1] == 0) + envelope_w(1); + } + + // if the noise is enabled + if (m_channels[ch].noise_enable) + { + // if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) + if (m_noise[ch/3].level & 1) + { + // subtract to avoid overflows, also use only half amplitude + output_l -= m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16 / 2; + output_r -= m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16 / 2; + } + } + // if the square wave is enabled + if (m_channels[ch].freq_enable) + { + // if the channel level is high + if (m_channels[ch].level & 1) + { + output_l += m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16; + output_r += m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16; + } + } + } + + for (ch = 0; ch < 2; ch++) + { + /* update the state of the noise generator + * polynomial is x^18 + x^11 + x (i.e. 0x20400) and is a plain XOR, initial state is probably all 1s + * see http://www.vogons.org/viewtopic.php?f=9&t=51695 */ + m_noise[ch].counter -= m_noise[ch].freq; + while (m_noise[ch].counter < 0) + { + m_noise[ch].counter += m_sample_rate; + if( ((m_noise[ch].level & 0x20000) == 0) != ((m_noise[ch].level & 0x0400) == 0) ) + m_noise[ch].level = (m_noise[ch].level << 1) | 1; + else + m_noise[ch].level <<= 1; + } + } + /* write sound data to the buffer */ + outputs[LEFT][j] = output_l / 6; + outputs[RIGHT][j] = output_r / 6; + } +} + + +void saa1099_device::envelope_w(int ch) +{ + if (m_env_enable[ch]) + { + int step, mode, mask; + mode = m_env_mode[ch]; + /* step from 0..63 and then loop in steps 32..63 */ + step = m_env_step[ch] = + ((m_env_step[ch] + 1) & 0x3f) | (m_env_step[ch] & 0x20); + + mask = 15; + if (m_env_bits[ch]) + mask &= ~1; /* 3 bit resolution, mask LSB */ + + m_channels[ch*3+0].envelope[ LEFT] = + m_channels[ch*3+1].envelope[ LEFT] = + m_channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask; + if (m_env_reverse_right[ch] & 0x01) + { + m_channels[ch*3+0].envelope[RIGHT] = + m_channels[ch*3+1].envelope[RIGHT] = + m_channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask; + } + else + { + m_channels[ch*3+0].envelope[RIGHT] = + m_channels[ch*3+1].envelope[RIGHT] = + m_channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask; + } + } + else + { + /* envelope mode off, set all envelope factors to 16 */ + m_channels[ch*3+0].envelope[ LEFT] = + m_channels[ch*3+1].envelope[ LEFT] = + m_channels[ch*3+2].envelope[ LEFT] = + m_channels[ch*3+0].envelope[RIGHT] = + m_channels[ch*3+1].envelope[RIGHT] = + m_channels[ch*3+2].envelope[RIGHT] = 16; + } +} + + +WRITE8_MEMBER( saa1099_device::control_w ) +{ + if ((data & 0xff) > 0x1c) + { + /* Error! */ + logerror("%s: (SAA1099 '%s') Unknown register selected\n", machine().describe_context(), tag()); + } + + m_selected_reg = data & 0x1f; + if (m_selected_reg == 0x18 || m_selected_reg == 0x19) + { + /* clock the envelope channels */ + if (m_env_clock[0]) + envelope_w(0); + if (m_env_clock[1]) + envelope_w(1); + } +} + + +WRITE8_MEMBER( saa1099_device::data_w ) +{ + int reg = m_selected_reg; + int ch; + + /* first update the stream to this point in time */ + m_stream->update(); + + switch (reg) + { + /* channel i amplitude */ + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + ch = reg & 7; + m_channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; + m_channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; + break; + /* channel i frequency */ + case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: + ch = reg & 7; + m_channels[ch].frequency = data & 0xff; + break; + /* channel i octave */ + case 0x10: case 0x11: case 0x12: + ch = (reg - 0x10) << 1; + m_channels[ch + 0].octave = data & 0x07; + m_channels[ch + 1].octave = (data >> 4) & 0x07; + break; + /* channel i frequency enable */ + case 0x14: + m_channels[0].freq_enable = data & 0x01; + m_channels[1].freq_enable = data & 0x02; + m_channels[2].freq_enable = data & 0x04; + m_channels[3].freq_enable = data & 0x08; + m_channels[4].freq_enable = data & 0x10; + m_channels[5].freq_enable = data & 0x20; + break; + /* channel i noise enable */ + case 0x15: + m_channels[0].noise_enable = data & 0x01; + m_channels[1].noise_enable = data & 0x02; + m_channels[2].noise_enable = data & 0x04; + m_channels[3].noise_enable = data & 0x08; + m_channels[4].noise_enable = data & 0x10; + m_channels[5].noise_enable = data & 0x20; + break; + /* noise generators parameters */ + case 0x16: + m_noise_params[0] = data & 0x03; + m_noise_params[1] = (data >> 4) & 0x03; + break; + /* envelope generators parameters */ + case 0x18: case 0x19: + ch = reg - 0x18; + m_env_reverse_right[ch] = data & 0x01; + m_env_mode[ch] = (data >> 1) & 0x07; + m_env_bits[ch] = data & 0x10; + m_env_clock[ch] = data & 0x20; + m_env_enable[ch] = data & 0x80; + /* reset the envelope */ + m_env_step[ch] = 0; + break; + /* channels enable & reset generators */ + case 0x1c: + m_all_ch_enable = data & 0x01; + m_sync_state = data & 0x02; + if (data & 0x02) + { + int i; + + /* Synch & Reset generators */ + logerror("%s: (SAA1099 '%s') -reg 0x1c- Chip reset\n", machine().describe_context(), tag()); + for (i = 0; i < 6; i++) + { + m_channels[i].level = 0; + m_channels[i].counter = 0.0; + } + } + break; + default: /* Error! */ + if (data != 0) + logerror("%s: (SAA1099 '%s') Unknown operation (reg:%02x, data:%02x)\n", machine().describe_context(), tag(), reg, data); + } +} + +WRITE8_MEMBER(saa1099_device::write) +{ + if (offset & 1) + control_w(space, 0, data); + else + data_w(space, 0, data); +} diff --git a/src/hardware/mame/saa1099.h b/src/hardware/mame/saa1099.h new file mode 100644 index 0000000..cce4fb5 --- /dev/null +++ b/src/hardware/mame/saa1099.h @@ -0,0 +1,120 @@ +// license:BSD-3-Clause +// copyright-holders:Juergen Buchmueller, Manuel Abadia +/********************************************** + Philips SAA1099 Sound driver +**********************************************/ + +#ifndef MAME_SOUND_SAA1099_H +#define MAME_SOUND_SAA1099_H + +#pragma once + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_SAA1099_ADD(_tag, _clock) \ + MCFG_DEVICE_ADD(_tag, SAA1099, _clock) +#define MCFG_SAA1099_REPLACE(_tag, _clock) \ + MCFG_DEVICE_REPLACE(_tag, SAA1099, _clock) + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +//Container class for int that just initalizes to 0 +class NullInt { + int value; +public: + operator int& () { + return value; + } + + int& operator= ( int set ) { + value = set; + return value; + } + + NullInt( int set = 0 ) : value( set ) { + } +}; + +// ======================> saa1099_device + +class saa1099_device : public device_t, + public device_sound_interface +{ +public: + saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + DECLARE_WRITE8_MEMBER( control_w ); + DECLARE_WRITE8_MEMBER( data_w ); + + DECLARE_WRITE8_MEMBER( write ); + +//protected: + // device-level overrides + virtual void device_start(); + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); + +private: + struct saa1099_channel + { + saa1099_channel() { + //Quite hacky, but let's see how it goes + memset( this, 0, sizeof( *this ) ); + } + + int frequency ; /* frequency (0x00..0xff) */ + int freq_enable ; /* frequency enable */ + int noise_enable ; /* noise enable */ + int octave ; /* octave (0x00..0x07) */ + int amplitude[2]; /* amplitude (0x00..0x0f) */ + int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ + + /* vars to simulate the square wave */ + double counter ; + double freq ; + int level ; + + }; + + struct saa1099_noise + { + saa1099_noise() { + counter = 0; + freq = 0; + level = 0xFFFFFFFF; + } + + /* vars to simulate the noise generator output */ + double counter; + double freq; + uint32_t level; /* noise polynomial shifter */ + }; + + void envelope_w(int ch); + + sound_stream *m_stream; /* our stream */ + int m_noise_params[2]; /* noise generators parameters */ + int m_env_enable[2]; /* envelope generators enable */ + int m_env_reverse_right[2]; /* envelope reversed for right channel */ + int m_env_mode[2]; /* envelope generators mode */ + int m_env_bits[2]; /* non zero = 3 bits resolution */ + int m_env_clock[2]; /* envelope clock mode (non-zero external) */ + int m_env_step[2]; /* current envelope step */ + int m_all_ch_enable; /* all channels enable */ + int m_sync_state; /* sync all channels */ + int m_selected_reg; /* selected register */ + saa1099_channel m_channels[6]; /* channels */ + saa1099_noise m_noise[2]; /* noise generators */ + double m_sample_rate; + int m_master_clock; +}; + +DECLARE_DEVICE_TYPE(SAA1099, saa1099_device) + +#endif // MAME_SOUND_SAA1099_H diff --git a/src/hardware/mame/sn76496.cpp b/src/hardware/mame/sn76496.cpp new file mode 100644 index 0000000..d11f0ea --- /dev/null +++ b/src/hardware/mame/sn76496.cpp @@ -0,0 +1,518 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria +/*************************************************************************** + + sn76496.c + by Nicola Salmoria + with contributions by others + + Routines to emulate the: + Texas Instruments SN76489, SN76489A, SN76494/SN76496 + ( Also known as, or at least compatible with, the TMS9919 and SN94624.) + and the Sega 'PSG' used on the Master System, Game Gear, and Megadrive/Genesis + This chip is known as the Programmable Sound Generator, or PSG, and is a 4 + channel sound generator, with three squarewave channels and a noise/arbitrary + duty cycle channel. + + Noise emulation for all verified chips should be accurate: + + ** SN76489 uses a 15-bit shift register with taps on bits D and E, output on E, + XOR function. + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + Its output is inverted. + ** SN94624 is the same as SN76489 but lacks the /8 divider on its clock input. + ** SN76489A uses a 15-bit shift register with taps on bits D and E, output on F, + XOR function. + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + Its output is not inverted. + ** SN76494 is the same as SN76489A but lacks the /8 divider on its clock input. + ** SN76496 is identical in operation to the SN76489A, but the audio input on pin 9 is + documented. + All the TI-made PSG chips have an audio input line which is mixed with the 4 channels + of output. (It is undocumented and may not function properly on the sn76489, 76489a + and 76494; the sn76489a input is mentioned in datasheets for the tms5200) + All the TI-made PSG chips act as if the frequency was set to 0x400 if 0 is + written to the frequency register. + ** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps + on bits C and F, output on F + It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle. + (whether it uses an XOR or XNOR needs to be verified, assumed XOR) + (whether output is inverted or not needs to be verified, assumed to be inverted) + ** Sega Game Gear PSG is identical to the SMS3/MD/Genesis one except it has an + extra register for mapping which channels go to which speaker. + The register, connected to a z80 port, means: + for bits 7 6 5 4 3 2 1 0 + L3 L2 L1 L0 R3 R2 R1 R0 + Noise is an XOR function, and audio output is negated before being output. + All the Sega-made PSG chips act as if the frequency was set to 0 if 0 is written + to the frequency register. + ** NCR8496 (as used on the Tandy 1000) is similar to the SN76489 but with a + different noise LFSR pattern: taps on bits A and E, output on E, XNOR function + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + Its output is inverted. + ** PSSJ-3 (as used on the later Tandy 1000 series computers) is the same as the + NCR8496 with the exception that its output is not inverted. + + 28/03/2005 : Sebastien Chevalier + Update th SN76496Write func, according to SN76489 doc found on SMSPower. + - On write with 0x80 set to 0, when LastRegister is other then TONE, + the function is similar than update with 0x80 set to 1 + + 23/04/2007 : Lord Nightmare + Major update, implement all three different noise generation algorithms and a + set_variant call to discern among them. + + 28/04/2009 : Lord Nightmare + Add READY line readback; cleaned up struct a bit. Cleaned up comments. + Add more TODOs. Fixed some unsaved savestate related stuff. + + 04/11/2009 : Lord Nightmare + Changed the way that the invert works (it now selects between XOR and XNOR + for the taps), and added R->OldNoise to simulate the extra 0 that is always + output before the noise LFSR contents are after an LFSR reset. + This fixes SN76489/A to match chips. Added SN94624. + + 14/11/2009 : Lord Nightmare + Removed STEP mess, vastly simplifying the code. Made output bipolar rather + than always above the 0 line, but disabled that code due to pending issues. + + 16/11/2009 : Lord Nightmare + Fix screeching in regulus: When summing together four equal channels, the + size of the max amplitude per channel should be 1/4 of the max range, not + 1/3. Added NCR8496. + + 18/11/2009 : Lord Nightmare + Modify Init functions to support negating the audio output. The gamegear + psg does this. Change gamegear and sega psgs to use XOR rather than XNOR + based on testing. Got rid of R->OldNoise and fixed taps accordingly. + Added stereo support for game gear. + + 15/01/2010 : Lord Nightmare + Fix an issue with SN76489 and SN76489A having the wrong periodic noise periods. + Note that properly emulating the noise cycle bit timing accurately may require + extensive rewriting. + + 24/01/2010: Lord Nightmare + Implement periodic noise as forcing one of the XNOR or XOR taps to 1 or 0 respectively. + Thanks to PlgDavid for providing samples which helped immensely here. + Added true clock divider emulation, so sn94624 and sn76494 run 8x faster than + the others, as in real life. + + 15/02/2010: Lord Nightmare & Michael Zapf (additional testing by PlgDavid) + Fix noise period when set to mirror channel 3 and channel 3 period is set to 0 (tested on hardware for noise, wave needs tests) - MZ + Fix phase of noise on sn94624 and sn76489; all chips use a standard XOR, the only inversion is the output itself - LN, Plgdavid + Thanks to PlgDavid and Michael Zapf for providing samples which helped immensely here. + + 23/02/2011: Lord Nightmare & Enik + Made it so the Sega PSG chips have a frequency of 0 if 0 is written to the + frequency register, while the others have 0x400 as before. Should fix a bug + or two on sega games, particularly Vigilante on Sega Master System. Verified + on SMS hardware. + + 27/06/2012: Michael Zapf + Converted to modern device, legacy devices were gradually removed afterwards. + + 16/09/2015: Lord Nightmare + Fix PSG chips to have volume reg inited on reset to 0x0 based on tests by + ValleyBell. Made Sega PSG chips start up with register 0x3 selected (volume + for channel 2) based on hardware tests by Nemesis. + + 26/08/2018: Lord Nightmare, Qbix, ValleyBell, NewRisingSun + * renamed the NCR8496 to its correct name, based on chip pictures on VGMPF + * fixed NCR8496 behavior on write to mirrored registers; unlike any of the + other variants, the NCR8496 seems to ignore writes to regs 1,3,5,6,7 if 0x80 + is not set. +***TODO: the above is NOT verified yet!*** + * fixed NCR8496's noise lfsr behavior so it is only reset if the mode bit in + register 6 is changed. + * NCR8496's LFSR feedback function is an XNOR, which is now supported + * NCR8496's output is inverted (though PSSJ-3's output is not) + * add PSSJ-3 support for the later Tandy computers. + + TODO: * Implement the TMS9919 - any difference to sn94624? + * Implement the T6W28; has registers in a weird order, needs writes + to be 'sanitized' first. Also is stereo, similar to game gear. + * Factor out common code so that the SAA1099 can share some code. + +***************************************************************************/ + +#include "emu.h" +#include "sn76496.h" + +#define MAX_OUTPUT 0x7fff +//When you go over this create sample +#define RATE_MAX ( 1 << 30) + +sn76496_base_device::sn76496_base_device( + const machine_config &mconfig, + device_type type, + const char *tag, + int feedbackmask, + int noisetap1, + int noisetap2, + bool negate, + bool stereo, + int clockdivider, + bool ncr, + bool sega, + device_t *owner, + uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) + , device_sound_interface(mconfig, *this) +// , m_ready_handler(*this) + , m_feedback_mask(feedbackmask) + , m_whitenoise_tap1(noisetap1) + , m_whitenoise_tap2(noisetap2) + , m_negate(negate) + , m_stereo(stereo) + , m_clock_divider(clockdivider) + , m_ncr_style_psg(ncr) + , m_sega_style_psg(sega) +{ +} + + +sn76496_device::sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN76496, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock) +{ +} + +u8106_device::u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, U8106, tag, 0x4000, 0x01, 0x02, true, false, 8, false, true, owner, clock) +{ +} + +y2404_device::y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, Y2404, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock) +{ +} + +sn76489_device::sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN76489, tag, 0x4000, 0x01, 0x02, true, false, 8, false, true, owner, clock) +{ +} + +sn76489a_device::sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN76489A, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock) +{ +} + +sn76494_device::sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN76494, tag, 0x10000, 0x04, 0x08, false, false, 1, false, true, owner, clock) +{ +} + +sn94624_device::sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN94624, tag, 0x4000, 0x01, 0x02, true, false, 1, false, true, owner, clock) +{ +} + +ncr8496_device::ncr8496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, NCR8496, tag, 0x8000, 0x02, 0x20, true, false, 8, true, true, owner, clock) +{ +} + +pssj3_device::pssj3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, PSSJ3, tag, 0x8000, 0x02, 0x20, false, false, 8, true, true, owner, clock) +{ +} + +gamegear_device::gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, GAMEGEAR, tag, 0x8000, 0x01, 0x08, true, true, 8, false, false, owner, clock) +{ +} + +segapsg_device::segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SEGAPSG, tag, 0x8000, 0x01, 0x08, true, false, 8, false, false, owner, clock) +{ +} + +void sn76496_base_device::device_start() +{ + sample_rate = clock()/2; + rate_add = RATE_MAX; + rate_counter = 0; + + int i; + double out; + int gain; + + //m_ready_handler.resolve_safe(); + + //m_sound = machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), sample_rate); + + for (i = 0; i < 4; i++) m_volume[i] = 0; + + m_last_register = m_sega_style_psg?3:0; // Sega VDP PSG defaults to selected period reg for 2nd channel + for (i = 0; i < 8; i+=2) + { + m_register[i] = 0; + m_register[i + 1] = 0x0; // volume = 0x0 (max volume) on reset; this needs testing on chips other than SN76489A and Sega VDP PSG + } + + for (i = 0; i < 4; i++) + { + m_output[i] = 0; + m_period[i] = 0; + m_count[i] = 0; + } + + m_RNG = m_feedback_mask; + m_output[3] = m_RNG & 1; + + m_cycles_to_ready = 1; // assume ready is not active immediately on init. is this correct? + m_stereo_mask = 0xFF; // all channels enabled + m_current_clock = m_clock_divider-1; + + // set gain + gain = 0; + + gain &= 0xff; + + // increase max output basing on gain (0.2 dB per step) + out = MAX_OUTPUT / 4; // four channels, each gets 1/4 of the total range + while (gain-- > 0) + out *= 1.023292992; // = (10 ^ (0.2/20)) + + // build volume table (2dB per step) + for (i = 0; i < 15; i++) + { + // limit volume to avoid clipping + if (out > MAX_OUTPUT / 4) m_vol_table[i] = MAX_OUTPUT / 4; + else m_vol_table[i] = out; + + out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ + } + m_vol_table[15] = 0; + + m_ready_state = true; + + //register_for_save_states(); +} + +void sn76496_base_device::device_clock_changed() +{ +// m_sound->set_sample_rate(clock()/2); +} + +WRITE8_MEMBER( sn76496_base_device::stereo_w ) +{ +// m_sound->update(); +// if (m_stereo) m_stereo_mask = data; +// else fatalerror("sn76496_base_device: Call to stereo write with mono chip!\n"); +} + +void sn76496_base_device::write(uint8_t data) +{ + int n, r, c; + + // update the output buffer before changing the registers +// m_sound->update(); + + // set number of cycles until READY is active; this is always one + // 'sample', i.e. it equals the clock divider exactly + m_cycles_to_ready = 1; + + if (data & 0x80) + { + r = (data & 0x70) >> 4; + m_last_register = r; + if (((m_ncr_style_psg) && (r == 6)) && ((data&0x04) != (m_register[6]&0x04))) m_RNG = m_feedback_mask; + m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f); + } + else + { + r = m_last_register; + if ((m_ncr_style_psg) && ((r & 1) || (r == 6))) return; // NCR8496 ignores writes to regs 1, 3, 5, 6 and 7 with bit 7 clear + } + + c = r >> 1; + switch (r) + { + case 0: // tone 0: frequency + case 2: // tone 1: frequency + case 4: // tone 2: frequency + if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x0f) | ((data & 0x3f) << 4); + if ((m_register[r] != 0) || (!m_sega_style_psg)) m_period[c] = m_register[r]; + else m_period[c] = 0x400; + + if (r == 4) + { + // update noise shift frequency + if ((m_register[6] & 0x03) == 0x03) m_period[3] = m_period[2]<<1; + } + break; + case 1: // tone 0: volume + case 3: // tone 1: volume + case 5: // tone 2: volume + case 7: // noise: volume + m_volume[c] = m_vol_table[data & 0x0f]; + if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f); + break; + case 6: // noise: frequency, mode + { + if ((data & 0x80) == 0) logerror("sn76496_base_device: write to reg 6 with bit 7 clear; data was %03x, new write is %02x! report this to LN!\n", m_register[6], data); + if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f); + n = m_register[6]; + // N/512,N/1024,N/2048,Tone #3 output + m_period[3] = ((n&3) == 3)? (m_period[2]<<1) : (1 << (5+(n&3))); + if (!(m_ncr_style_psg)) m_RNG = m_feedback_mask; + } + break; + } +} + +WRITE8_MEMBER( sn76496_base_device::write ) +{ + write(data); +} + +inline bool sn76496_base_device::in_noise_mode() +{ + return ((m_register[6] & 4)!=0); +} + +void sn76496_base_device::countdown_cycles() +{ + if (m_cycles_to_ready > 0) + { + m_cycles_to_ready--; + //if (m_ready_state==true) m_ready_handler(CLEAR_LINE); + m_ready_state = false; + } + else + { + //if (m_ready_state==false) m_ready_handler(ASSERT_LINE); + m_ready_state = true; + } +} + +void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + int i; + stream_sample_t *lbuffer = outputs[0]; + stream_sample_t *rbuffer = (m_stereo)? outputs[1] : 0;//nullptr; + + int16_t out; + int16_t out2 = 0; + + while (samples > 0) + { + // clock chip once + if (m_current_clock > 0) // not ready for new divided clock + { + m_current_clock--; + } + else // ready for new divided clock, make a new sample + { + m_current_clock = m_clock_divider-1; + // decrement Cycles to READY by one + countdown_cycles(); + + // handle channels 0,1,2 + for (i = 0; i < 3; i++) + { + m_count[i]--; + if (m_count[i] <= 0) + { + m_output[i] ^= 1; + m_count[i] = m_period[i]; + } + } + + // handle channel 3 + m_count[3]--; + if (m_count[3] <= 0) + { + // if noisemode is 1, both taps are enabled + // if noisemode is 0, the lower tap, whitenoisetap2, is held at 0 + // The != was a bit-XOR (^) before + if (((m_RNG & m_whitenoise_tap1)!=0) != (((m_RNG & m_whitenoise_tap2)!=(m_ncr_style_psg?m_whitenoise_tap2:0)) && in_noise_mode())) + { + m_RNG >>= 1; + m_RNG |= m_feedback_mask; + } + else + { + m_RNG >>= 1; + } + m_output[3] = m_RNG & 1; + + m_count[3] = m_period[3]; + } + } + + //Skip final generation if you don't need an actual sample + rate_counter += rate_add; + if (rate_counter < RATE_MAX) + continue; + rate_counter -= RATE_MAX; + + if (m_stereo) + { + out = ((((m_stereo_mask & 0x10)!=0) && (m_output[0]!=0))? m_volume[0] : 0) + + ((((m_stereo_mask & 0x20)!=0) && (m_output[1]!=0))? m_volume[1] : 0) + + ((((m_stereo_mask & 0x40)!=0) && (m_output[2]!=0))? m_volume[2] : 0) + + ((((m_stereo_mask & 0x80)!=0) && (m_output[3]!=0))? m_volume[3] : 0); + + out2= ((((m_stereo_mask & 0x1)!=0) && (m_output[0]!=0))? m_volume[0] : 0) + + ((((m_stereo_mask & 0x2)!=0) && (m_output[1]!=0))? m_volume[1] : 0) + + ((((m_stereo_mask & 0x4)!=0) && (m_output[2]!=0))? m_volume[2] : 0) + + ((((m_stereo_mask & 0x8)!=0) && (m_output[3]!=0))? m_volume[3] : 0); + } + else + { + out= ((m_output[0]!=0)? m_volume[0]:0) + +((m_output[1]!=0)? m_volume[1]:0) + +((m_output[2]!=0)? m_volume[2]:0) + +((m_output[3]!=0)? m_volume[3]:0); + } + + if (m_negate) { out = -out; out2 = -out2; } + *(lbuffer++) = out; + if (m_stereo) *(rbuffer++) = out2; + samples--; + } +} + + +void sn76496_base_device::convert_samplerate(int32_t target_rate) { + //Simple 10 bit shift for samplerate conversion + rate_add = (int32_t)( RATE_MAX * (target_rate / (double)sample_rate) ); + rate_counter = 0; +} + +void sn76496_base_device::register_for_save_states() +{ + save_item(NAME(m_vol_table)); + save_item(NAME(m_register)); + save_item(NAME(m_last_register)); + save_item(NAME(m_volume)); + save_item(NAME(m_RNG)); +// save_item(NAME(m_clock_divider)); + save_item(NAME(m_current_clock)); +// save_item(NAME(m_feedback_mask)); +// save_item(NAME(m_whitenoise_tap1)); +// save_item(NAME(m_whitenoise_tap2)); +// save_item(NAME(m_negate)); +// save_item(NAME(m_stereo)); + save_item(NAME(m_stereo_mask)); + save_item(NAME(m_period)); + save_item(NAME(m_count)); + save_item(NAME(m_output)); + save_item(NAME(m_cycles_to_ready)); +// save_item(NAME(m_sega_style_psg)); +} + +DEFINE_DEVICE_TYPE(SN76496, sn76496_device, "sn76496", "SN76496") +DEFINE_DEVICE_TYPE(U8106, u8106_device, "u8106", "U8106") +DEFINE_DEVICE_TYPE(Y2404, y2404_device, "y2404", "Y2404") +DEFINE_DEVICE_TYPE(SN76489, sn76489_device, "sn76489", "SN76489") +DEFINE_DEVICE_TYPE(SN76489A, sn76489a_device, "sn76489a", "SN76489A") +DEFINE_DEVICE_TYPE(SN76494, sn76494_device, "sn76494", "SN76494") +DEFINE_DEVICE_TYPE(SN94624, sn94624_device, "sn94624", "SN94624") +DEFINE_DEVICE_TYPE(NCR8496, ncr8496_device, "ncr8496", "NCR8496") +DEFINE_DEVICE_TYPE(PSSJ3, pssj3_device, "pssj3", "PSSJ-3") +DEFINE_DEVICE_TYPE(GAMEGEAR, gamegear_device, "gamegear_psg", "Game Gear PSG") +DEFINE_DEVICE_TYPE(SEGAPSG, segapsg_device, "segapsg", "Sega VDP PSG") + diff --git a/src/hardware/mame/sn76496.h b/src/hardware/mame/sn76496.h new file mode 100644 index 0000000..b464ba7 --- /dev/null +++ b/src/hardware/mame/sn76496.h @@ -0,0 +1,181 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria +#ifndef MAME_SOUND_SN76496_H +#define MAME_SOUND_SN76496_H + +#pragma once + + +DECLARE_DEVICE_TYPE(SN76496, sn76496_device) +DECLARE_DEVICE_TYPE(U8106, u8106_device) +DECLARE_DEVICE_TYPE(Y2404, y2404_device) +DECLARE_DEVICE_TYPE(SN76489, sn76489_device) +DECLARE_DEVICE_TYPE(SN76489A, sn76489a_device) +DECLARE_DEVICE_TYPE(SN76494, sn76494_device) +DECLARE_DEVICE_TYPE(SN94624, sn94624_device) +DECLARE_DEVICE_TYPE(NCR8496, ncr8496_device) +DECLARE_DEVICE_TYPE(PSSJ3, pssj3_device) +DECLARE_DEVICE_TYPE(GAMEGEAR, gamegear_device) +DECLARE_DEVICE_TYPE(SEGAPSG, segapsg_device) + +#if 0 + + +#define MCFG_SN76496_READY_HANDLER(cb) \ + downcast(*device).set_ready_handler((DEVCB_##cb)); + +#endif + +class sn76496_base_device : public device_t, public device_sound_interface +{ +public: + // configuration helpers +// template devcb_base &set_ready_handler(Object &&cb) { return m_ready_handler.set_callback(std::forward(cb)); } + + DECLARE_WRITE8_MEMBER( stereo_w ); + void write(uint8_t data); + DECLARE_WRITE8_MEMBER( write ); +// DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; } +// auto ready_cb() { return m_ready_handler.bind(); } + + void convert_samplerate(int32_t target_rate); +protected: + sn76496_base_device( + const machine_config &mconfig, + device_type type, + const char *tag, + int feedbackmask, + int noisetap1, + int noisetap2, + bool negate, + bool stereo, + int clockdivider, + bool ncr, + bool sega, + device_t *owner, + uint32_t clock); + + virtual void device_start(); + virtual void device_clock_changed(); + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); + +private: + inline bool in_noise_mode(); + void register_for_save_states(); + void countdown_cycles(); + + + + bool m_ready_state; + + //devcb_write_line m_ready_handler; + + //sound_stream* m_sound; + + const int32_t m_feedback_mask; // mask for feedback + const int32_t m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14) + const int32_t m_whitenoise_tap2; // mask for white noise tap 2 (lower one, usually bit 13) + const bool m_negate; // output negate flag + const bool m_stereo; // whether we're dealing with stereo or not + const int32_t m_clock_divider; // clock divider + const bool m_ncr_style_psg; // flag to ignore writes to regs 1,3,5,6,7 with bit 7 low + const bool m_sega_style_psg; // flag to make frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0; the initial register is pointing to 0x3 instead of 0x0; the volume reg is preloaded with 0xF instead of 0x0 + + int32_t m_vol_table[16]; // volume table (for 4-bit to db conversion) + int32_t m_register[8]; // registers + int32_t m_last_register; // last register written + int32_t m_volume[4]; // db volume of voice 0-2 and noise + uint32_t m_RNG; // noise generator LFSR + int32_t m_current_clock; + int32_t m_stereo_mask; // the stereo output mask + int32_t m_period[4]; // Length of 1/2 of waveform + int32_t m_count[4]; // Position within the waveform + int32_t m_output[4]; // 1-bit output of each channel, pre-volume + int32_t m_cycles_to_ready; // number of cycles until the READY line goes active + + //Modifications for easier sample conversion + int32_t sample_rate; + //Sample rate conversion + int32_t rate_add; + int32_t rate_counter; +}; + +// SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf) +class sn76496_device : public sn76496_base_device +{ +public: + sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// U8106 not verified yet. todo: verify; (a custom marked sn76489? only used on mr. do and maybe other universal games) +class u8106_device : public sn76496_base_device +{ +public: + u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// Y2404 not verified yet. todo: verify; (don't be fooled by the Y, it's a TI chip, not Yamaha) +class y2404_device : public sn76496_base_device +{ +public: + y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// NCR8496 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun +class sn76489_device : public sn76496_base_device +{ +public: + sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// PSSJ-3 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun +class pssj3_device : public sn76496_base_device +{ +public: + pssj3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + + +// SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid) +class sn76489a_device : public sn76496_base_device +{ +public: + sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider) +class sn76494_device : public sn76496_base_device +{ +public: + sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid +class sn94624_device : public sn76496_base_device +{ +public: + sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// NCR8496 not verified; info from smspower wiki and vgmpf wiki +class ncr8496_device : public sn76496_base_device +{ +public: + ncr8496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// Verified by Justin Kerk +class gamegear_device : public sn76496_base_device +{ +public: + gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// todo: verify; from smspower wiki, assumed to have same invert as gamegear +class segapsg_device : public sn76496_base_device +{ +public: + segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +#endif // MAME_SOUND_SN76496_H diff --git a/src/hardware/mame/ymdeltat.cpp b/src/hardware/mame/ymdeltat.cpp new file mode 100644 index 0000000..e517013 --- /dev/null +++ b/src/hardware/mame/ymdeltat.cpp @@ -0,0 +1,650 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +/* +** +** File: ymdeltat.c +** +** YAMAHA DELTA-T adpcm sound emulation subroutine +** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B) +** +** Base program is YM2610 emulator by Hiromitsu Shioya. +** Written by Tatsuyuki Satoh +** Improvements by Jarek Burczynski (bujar at mame dot net) +** +** +** History: +** +** 03-08-2003 Jarek Burczynski: +** - fixed BRDY flag implementation. +** +** 24-07-2003 Jarek Burczynski, Frits Hilderink: +** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset +** +** 22-07-2003 Jarek Burczynski, Frits Hilderink: +** - fixed external memory support +** +** 15-06-2003 Jarek Burczynski: +** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08) +** - implemented support for the Limit address register +** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM +** - implemented external memory access (read/write) via the ADPCM data reg reads/writes +** Thanks go to Frits Hilderink for the example code. +** +** 14-06-2003 Jarek Burczynski: +** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO +** - modified EOS handling +** +** 05-04-2003 Jarek Burczynski: +** - implemented partial support for external/processor memory on sample replay +** +** 01-12-2002 Jarek Burczynski: +** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi +** - renamed/removed some YM_DELTAT struct fields +** +** 28-12-2001 Acho A. Tang +** - added EOS status report on ADPCM playback. +** +** 05-08-2001 Jarek Burczynski: +** - now_step is initialized with 0 at the start of play. +** +** 12-06-2001 Jarek Burczynski: +** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC. +** Checked on real YM2610 chip - address register is 24 bits wide. +** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem. +** +** TO DO: +** Check size of the address register on the other chips.... +** +** Version 0.72 +** +** sound chips that have this unit: +** YM2608 OPNA +** YM2610/B OPNB +** Y8950 MSX AUDIO +** +*/ + +#include "emu.h" +#include "ymdeltat.h" + +#define YM_DELTAT_SHIFT (16) + +#define YM_DELTAT_DELTA_MAX (24576) +#define YM_DELTAT_DELTA_MIN (127) +#define YM_DELTAT_DELTA_DEF (127) + +#define YM_DELTAT_DECODE_RANGE 32768 +#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) +#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) + + +/* Forecast to next Forecast (rate = *8) */ +/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ +static const int32_t ym_deltat_decode_tableB1[16] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9, -11, -13, -15, +}; +/* delta to next delta (rate= *64) */ +/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ +static const int32_t ym_deltat_decode_tableB2[16] = { + 57, 57, 57, 57, 77, 102, 128, 153, + 57, 57, 57, 57, 77, 102, 128, 153 +}; + +#if 0 +void YM_DELTAT::BRDY_callback() +{ + logerror("BRDY_callback reached (flag set) !\n"); + + /* set BRDY bit in status register */ + if(status_set_handler) + if(status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); +} +#endif + +uint8_t YM_DELTAT::ADPCM_Read() +{ + uint8_t v = 0; + + /* external memory read */ + if ((portstate & 0xe0) == 0x20) + { + /* two dummy reads */ + if (memread) + { + now_addr = start << 1; + memread--; + return 0; + } + + + if (now_addr != (end << 1)) + { + v = memory[now_addr>>1]; + + /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", now_addr >> 1, v);*/ + + now_addr += 2; /* two nibbles at a time */ + + /* reset BRDY bit in status register, which means we are reading the memory now */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + + /* setup a timer that will callback us in 10 master clock cycles for Y8950 + * in the callback set the BRDY flag to 1 , which means we have another data ready. + * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. + */ + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + } + else + { + /* set EOS bit in status register */ + if (status_set_handler && status_change_EOS_bit) + (status_set_handler)(status_change_which_chip, status_change_EOS_bit); + } + } + + return v; +} + + +/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ +static const uint8_t dram_rightshift[4]={3,0,0,0}; + +/* DELTA-T ADPCM write register */ +void YM_DELTAT::ADPCM_Write(int r, int v) +{ + if (r >= 0x10) return; + reg[r] = v; /* stock data */ + + switch (r) + { + case 0x00: +/* +START: + Accessing *external* memory is started when START bit (D7) is set to "1", so + you must set all conditions needed for recording/playback before starting. + If you access *CPU-managed* memory, recording/playback starts after + read/write of ADPCM data register $08. + +REC: + 0 = ADPCM synthesis (playback) + 1 = ADPCM analysis (record) + +MEMDATA: + 0 = processor (*CPU-managed*) memory (means: using register $08) + 1 = external memory (using start/end/limit registers to access memory: RAM or ROM) + + +SPOFF: + controls output pin that should disable the speaker while ADPCM analysis + +RESET and REPEAT only work with external memory. + + +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + /* handle emulation mode */ + if (emulation_mode == EMULATION_MODE_YM2610) + { + v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */ + } + + portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */ + + if (portstate & 0x80)/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ + { + /* set PCM BUSY bit */ + PCM_BSY = 1; + + /* start ADPCM */ + now_step = 0; + acc = 0; + prev_acc = 0; + adpcml = 0; + adpcmd = YM_DELTAT_DELTA_DEF; + now_data = 0; + + } + + if (portstate & 0x20) /* do we access external memory? */ + { + now_addr = start << 1; + memread = 2; /* two dummy reads needed before accesing external memory via register $08*/ + + /* if yes, then let's check if ADPCM memory is mapped and big enough */ + if (!memory) + { + device->logerror("YM Delta-T ADPCM rom not mapped\n"); + portstate = 0x00; + PCM_BSY = 0; + } + else + { + if (end >= memory_size) /* Check End in Range */ + { + device->logerror("YM Delta-T ADPCM end out of range: $%08x\n", end); + end = memory_size - 1; + } + if (start >= memory_size) /* Check Start in Range */ + { + device->logerror("YM Delta-T ADPCM start out of range: $%08x\n", start); + portstate = 0x00; + PCM_BSY = 0; + } + } + } + else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */ + { + now_addr = 0; + } + + if (portstate & 0x01) + { + portstate = 0x00; + + /* clear PCM BUSY bit (in status register) */ + PCM_BSY = 0; + + /* set BRDY flag */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + } + break; + + case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ + /* handle emulation mode */ + if (emulation_mode == EMULATION_MODE_YM2610) + { + v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */ + } + + pan = &output_pointer[(v >> 6) & 0x03]; + if ((control2 & 3) != (v & 3)) + { + /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ + if (DRAMportshift != dram_rightshift[v & 3]) + { + DRAMportshift = dram_rightshift[v & 3]; + + /* final shift value depends on chip type and memory type selected: + 8 for YM2610 (ROM only), + 5 for ROM for Y8950 and YM2608, + 5 for x8bit DRAMs for Y8950 and YM2608, + 2 for x1bit DRAMs for Y8950 and YM2608. + */ + + /* refresh addresses */ + start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift); + end = (reg[0x5] * 0x0100 | reg[0x4]) << (portshift - DRAMportshift); + end += (1 << (portshift - DRAMportshift)) - 1; + limit = (reg[0xd]*0x0100 | reg[0xc]) << (portshift - DRAMportshift); + } + } + control2 = v; + break; + + case 0x02: /* Start Address L */ + case 0x03: /* Start Address H */ + start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift); + /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",reg[0x2], reg[0x3],start );*/ + break; + + case 0x04: /* Stop Address L */ + case 0x05: /* Stop Address H */ + end = (reg[0x5]*0x0100 | reg[0x4]) << (portshift - DRAMportshift); + end += (1 << (portshift - DRAMportshift)) - 1; + /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",reg[0x4], reg[0x5],end );*/ + break; + + case 0x06: /* Prescale L (ADPCM and Record frq) */ + case 0x07: /* Prescale H */ + break; + + case 0x08: /* ADPCM data */ +/* +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + + /* external memory write */ + if ((portstate & 0xe0) == 0x60) + { + if (memread) + { + now_addr = start << 1; + memread = 0; + } + + /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", now_addr >> 1, v);*/ + + if (now_addr != (end << 1)) + { + memory[now_addr >> 1] = v; + now_addr += 2; /* two nybbles at a time */ + + /* reset BRDY bit in status register, which means we are processing the write */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + + /* setup a timer that will callback us in 10 master clock cycles for Y8950 + * in the callback set the BRDY flag to 1 , which means we have written the data. + * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. + */ + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + + } + else + { + /* set EOS bit in status register */ + if (status_set_handler && status_change_EOS_bit) + (status_set_handler)(status_change_which_chip, status_change_EOS_bit); + } + + return; + } + + /* ADPCM synthesis from CPU */ + if ((portstate & 0xe0) == 0x80) + { + CPU_data = v; + + /* Reset BRDY bit in status register, which means we are full of data */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + return; + } + + break; + + case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ + case 0x0a: /* DELTA-N H */ + delta = (reg[0xa] * 0x0100 | reg[0x9]); + step = uint32_t(double(delta /* *(1<<(YM_DELTAT_SHIFT-16)) */) * freqbase); + /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",reg[0x9], reg[0xa]);*/ + break; + + case 0x0b: /* Output level control (volume, linear) */ + { + const int32_t oldvol = volume; + volume = (v & 0xff) * (output_range / 256) / YM_DELTAT_DECODE_RANGE; +/* v * ((1<<16)>>8) >> 15; +* thus: v * (1<<8) >> 15; +* thus: output_range must be (1 << (15+8)) at least +* v * ((1<<23)>>8) >> 15; +* v * (1<<15) >> 15; +*/ + /*logerror("DELTAT vol = %2x\n",v&0xff);*/ + if (oldvol != 0) + { + adpcml = int(double(adpcml) / double(oldvol) * double(volume)); + } + } + break; + + case 0x0c: /* Limit Address L */ + case 0x0d: /* Limit Address H */ + limit = (reg[0xd] * 0x0100 | reg[0xc]) << (portshift - DRAMportshift); + /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",reg[0xc], reg[0xd],limit );*/ + break; + } +} + +void YM_DELTAT::ADPCM_Reset(int panidx, int mode, device_t *dev) +{ + device = dev; + now_addr = 0; + now_step = 0; + step = 0; + start = 0; + end = 0; + limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */ + volume = 0; + pan = &output_pointer[panidx]; + acc = 0; + prev_acc = 0; + adpcmd = 127; + adpcml = 0; + emulation_mode = uint8_t(mode); + portstate = (emulation_mode == EMULATION_MODE_YM2610) ? 0x20 : 0; + control2 = (emulation_mode == EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */ + DRAMportshift = dram_rightshift[control2 & 3]; + + /* The flag mask register disables the BRDY after the reset, however + ** as soon as the mask is enabled the flag needs to be set. */ + + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); +} + +void YM_DELTAT::postload(uint8_t *regs) +{ + /* to keep adpcml */ + volume = 0; + /* update */ + for (int r = 1; r < 16; r++) + ADPCM_Write(r, regs[r]); + reg[0] = regs[0]; + + /* current rom data */ + if (memory) + now_data = *(memory + (now_addr >> 1)); + +} +void YM_DELTAT::savestate(device_t *device) +{ +#ifdef MAME_EMU_SAVE_H + YM_DELTAT *const DELTAT = this; // makes the save name sensible + device->save_item(NAME(DELTAT->portstate)); + device->save_item(NAME(DELTAT->now_addr)); + device->save_item(NAME(DELTAT->now_step)); + device->save_item(NAME(DELTAT->acc)); + device->save_item(NAME(DELTAT->prev_acc)); + device->save_item(NAME(DELTAT->adpcmd)); + device->save_item(NAME(DELTAT->adpcml)); +#endif +} + + +#define YM_DELTAT_Limit(val,max,min) \ +{ \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +static inline void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT) +{ + uint32_t step; + int data; + + DELTAT->now_step += DELTAT->step; + if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; + DELTAT->now_step &= (1<now_addr == (DELTAT->limit<<1) ) + DELTAT->now_addr = 0; + + if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */ + if( DELTAT->portstate&0x10 ){ + /* repeat start */ + DELTAT->now_addr = DELTAT->start<<1; + DELTAT->acc = 0; + DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; + DELTAT->prev_acc = 0; + }else{ + /* set EOS bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_EOS_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); + + /* clear PCM BUSY bit (reflected in status register) */ + DELTAT->PCM_BSY = 0; + + DELTAT->portstate = 0; + DELTAT->adpcml = 0; + DELTAT->prev_acc = 0; + return; + } + } + + if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f; + else + { + DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1)); + data = DELTAT->now_data >> 4; + } + + DELTAT->now_addr++; + /* 12-06-2001 JB: */ + /* YM2610 address register is 24 bits wide.*/ + /* The "+1" is there because we use 1 bit more for nibble calculations.*/ + /* WARNING: */ + /* Side effect: we should take the size of the mapped ROM into account */ + DELTAT->now_addr &= ( (1<<(24+1))-1); + + /* store accumulator value */ + DELTAT->prev_acc = DELTAT->acc; + + /* Forecast to next Forecast */ + DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); + YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); + + /* delta to next delta */ + DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; + YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); + + /* ElSemi: Fix interpolator. */ + /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/ + + }while(--step); + + } + + /* ElSemi: Fix interpolator. */ + DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); + DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); + DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; + + /* output for work of output channels (outd[OPNxxxx])*/ + *(DELTAT->pan) += DELTAT->adpcml; +} + + + +static inline void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT) +{ + uint32_t step; + int data; + + DELTAT->now_step += DELTAT->step; + if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; + DELTAT->now_step &= (1<now_addr&1 ) + { + data = DELTAT->now_data & 0x0f; + + DELTAT->now_data = DELTAT->CPU_data; + + /* after we used CPU_data, we set BRDY bit in status register, + * which means we are ready to accept another byte of data */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + } + else + { + data = DELTAT->now_data >> 4; + } + + DELTAT->now_addr++; + + /* store accumulator value */ + DELTAT->prev_acc = DELTAT->acc; + + /* Forecast to next Forecast */ + DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); + YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); + + /* delta to next delta */ + DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; + YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); + + + }while(--step); + + } + + /* ElSemi: Fix interpolator. */ + DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); + DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); + DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; + + /* output for work of output channels (outd[OPNxxxx])*/ + *(DELTAT->pan) += DELTAT->adpcml; +} + + + +/* ADPCM B (Delta-T control type) */ +void YM_DELTAT::ADPCM_CALC() +{ +/* +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + + if ( (portstate & 0xe0)==0xa0 ) + { + YM_DELTAT_synthesis_from_external_memory(this); + return; + } + + if ( (portstate & 0xe0)==0x80 ) + { + /* ADPCM synthesis from CPU-managed memory (from reg $08) */ + YM_DELTAT_synthesis_from_CPU_memory(this); /* change output based on data in ADPCM data reg ($08) */ + return; + } + +//todo: ADPCM analysis +// if ( (portstate & 0xe0)==0xc0 ) +// if ( (portstate & 0xe0)==0xe0 ) + + return; +} diff --git a/src/hardware/mame/ymdeltat.h b/src/hardware/mame/ymdeltat.h new file mode 100644 index 0000000..ad9e92e --- /dev/null +++ b/src/hardware/mame/ymdeltat.h @@ -0,0 +1,87 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +#ifndef MAME_SOUND_YMDELTAT_H +#define MAME_SOUND_YMDELTAT_H + +#pragma once + + +typedef void (*STATUS_CHANGE_HANDLER)(void *chip, uint8_t status_bits); + + +/* DELTA-T (adpcm type B) struct */ +struct YM_DELTAT { /* AT: rearranged and tightened structure */ + enum { + EMULATION_MODE_NORMAL = 0, + EMULATION_MODE_YM2610 = 1, + }; + + uint8_t *memory; + int32_t *output_pointer;/* pointer of output pointers */ + int32_t *pan; /* pan : &output_pointer[pan] */ + double freqbase; +#if 0 + double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */ + double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */ +#endif + uint32_t memory_size; + int output_range; + uint32_t now_addr; /* current address */ + uint32_t now_step; /* correct step */ + uint32_t step; /* step */ + uint32_t start; /* start address */ + uint32_t limit; /* limit address */ + uint32_t end; /* end address */ + uint32_t delta; /* delta scale */ + int32_t volume; /* current volume */ + int32_t acc; /* shift Measurement value*/ + int32_t adpcmd; /* next Forecast */ + int32_t adpcml; /* current value */ + int32_t prev_acc; /* leveling value */ + uint8_t now_data; /* current rom data */ + uint8_t CPU_data; /* current data from reg 08 */ + uint8_t portstate; /* port status */ + uint8_t control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */ + uint8_t portshift; /* address bits shift-left: + ** 8 for YM2610, + ** 5 for Y8950 and YM2608 */ + + uint8_t DRAMportshift; /* address bits shift-right: + ** 0 for ROM and x8bit DRAMs, + ** 3 for x1 DRAMs */ + + uint8_t memread; /* needed for reading/writing external memory */ + + /* handlers and parameters for the status flags support */ + STATUS_CHANGE_HANDLER status_set_handler; + STATUS_CHANGE_HANDLER status_reset_handler; + + /* note that different chips have these flags on different + ** bits of the status register + */ + void * status_change_which_chip; /* this chip id */ + uint8_t status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/ + uint8_t status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */ + uint8_t status_change_ZERO_bit; /* 1 if silence lasts for more than 290 milliseconds on ADPCM recording */ + + /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above, + ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608) + */ + uint8_t PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */ + + uint8_t reg[16]; /* adpcm registers */ + uint8_t emulation_mode; /* which chip we're emulating */ + device_t *device; + + /*void BRDY_callback();*/ + + uint8_t ADPCM_Read(); + void ADPCM_Write(int r, int v); + void ADPCM_Reset(int panidx, int mode, device_t *dev); + void ADPCM_CALC(); + + void postload(uint8_t *regs); + void savestate(device_t *device); +}; + +#endif // MAME_SOUND_YMDELTAT_H diff --git a/src/hardware/mame/ymf262.cpp b/src/hardware/mame/ymf262.cpp new file mode 100644 index 0000000..98056c5 --- /dev/null +++ b/src/hardware/mame/ymf262.cpp @@ -0,0 +1,2792 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +/* +** +** File: ymf262.c - software implementation of YMF262 +** FM sound generator type OPL3 +** +** Copyright Jarek Burczynski +** +** Version 0.2 +** + +Revision History: + +03-03-2003: initial release + - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips + - thanks to Stiletto for the datasheets + + Features as listed in 4MF262A6 data sheet: + 1. Registers are compatible with YM3812 (OPL2) FM sound source. + 2. Up to six sounds can be used as four-operator melody sounds for variety. + 3. 18 simultaneous melody sounds, or 15 melody sounds with 5 rhythm sounds (with two operators). + 4. 6 four-operator melody sounds and 6 two-operator melody sounds, or 6 four-operator melody + sounds, 3 two-operator melody sounds and 5 rhythm sounds (with four operators). + 5. 8 selectable waveforms. + 6. 4-channel sound output. + 7. YMF262 compabile DAC (YAC512) is available. + 8. LFO for vibrato and tremolo effedts. + 9. 2 programable timers. + 10. Shorter register access time compared with YM3812. + 11. 5V single supply silicon gate CMOS process. + 12. 24 Pin SOP Package (YMF262-M), 48 Pin SQFP Package (YMF262-S). + + +differences between OPL2 and OPL3 not documented in Yamaha datahasheets: +- sinus table is a little different: the negative part is off by one... + +- in order to enable selection of four different waveforms on OPL2 + one must set bit 5 in register 0x01(test). + on OPL3 this bit is ignored and 4-waveform select works *always*. + (Don't confuse this with OPL3's 8-waveform select.) + +- Envelope Generator: all 15 x rates take zero time on OPL3 + (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates + take zero time) + +- channel calculations: output of operator 1 is in perfect sync with + output of operator 2 on OPL3; on OPL and OPL2 output of operator 1 + is always delayed by one sample compared to output of operator 2 + + +differences between OPL2 and OPL3 shown in datasheets: +- YMF262 does not support CSM mode + + +*/ + +#include "emu.h" +#include "ymf262.h" + + +/* output final shift */ +#if (OPL3_SAMPLE_BITS==16) + #define FINAL_SH (0) + #define MAXOUT (+32767) + #define MINOUT (-32768) +#else + #define FINAL_SH (8) + #define MAXOUT (+127) + #define MINOUT (-128) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = a; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = b; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + + +#define OPL3_TYPE_YMF262 (0) /* 36 operators, 8 waveforms */ + + +struct OPL3_SLOT +{ + uint32_t ar; /* attack rate: AR<<2 */ + uint32_t dr; /* decay rate: DR<<2 */ + uint32_t rr; /* release rate:RR<<2 */ + uint8_t KSR; /* key scale rate */ + uint8_t ksl; /* keyscale level */ + uint8_t ksr; /* key scale rate: kcode>>KSR */ + uint8_t mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + uint32_t Cnt; /* frequency counter */ + uint32_t Incr; /* frequency counter step */ + uint8_t FB; /* feedback shift value */ + uint8_t conn_enum; /* slot output route */ + int32_t *connect; /* slot output pointer */ + int32_t op1_out[2]; /* slot1 output for feedback */ + uint8_t CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + uint8_t eg_type; /* percussive/non-percussive mode */ + uint8_t state; /* phase type */ + uint32_t TL; /* total level: TL << 2 */ + int32_t TLL; /* adjusted now TL */ + int32_t volume; /* envelope counter */ + uint32_t sl; /* sustain level: sl_tab[SL] */ + + uint32_t eg_m_ar; /* (attack state) */ + uint8_t eg_sh_ar; /* (attack state) */ + uint8_t eg_sel_ar; /* (attack state) */ + uint32_t eg_m_dr; /* (decay state) */ + uint8_t eg_sh_dr; /* (decay state) */ + uint8_t eg_sel_dr; /* (decay state) */ + uint32_t eg_m_rr; /* (release state) */ + uint8_t eg_sh_rr; /* (release state) */ + uint8_t eg_sel_rr; /* (release state) */ + + uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ + uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + uint8_t waveform_number; + unsigned int wavetable; + + //unsigned char reserved[128-84];//speedup: pump up the struct size to power of 2 + unsigned char reserved[128-100];//speedup: pump up the struct size to power of 2 + +}; + +struct OPL3_CH +{ + OPL3_SLOT SLOT[2]; + + uint32_t block_fnum; /* block+fnum */ + uint32_t fc; /* Freq. Increment base */ + uint32_t ksl_base; /* KeyScaleLevel Base step */ + uint8_t kcode; /* key code (for key scaling) */ + + /* + there are 12 2-operator channels which can be combined in pairs + to form six 4-operator channel, they are: + 0 and 3, + 1 and 4, + 2 and 5, + 9 and 12, + 10 and 13, + 11 and 14 + */ + uint8_t extended; /* set to 1 if this channel forms up a 4op channel with another channel(only used by first of pair of channels, ie 0,1,2 and 9,10,11) */ + + unsigned char reserved[512-272];//speedup:pump up the struct size to power of 2 + +}; + +/* OPL3 state */ +struct OPL3 +{ + OPL3_CH P_CH[18]; /* OPL3 chips have 18 channels */ + + uint32_t pan[18*4]; /* channels output masks (0xffffffff = enable); 4 masks per one channel */ + uint32_t pan_ctrl_value[18]; /* output control values 1 per one channel (1 value contains 4 masks) */ + + signed int chanout[18]; + signed int phase_modulation; /* phase modulation input (SLOT 2) */ + signed int phase_modulation2; /* phase modulation input (SLOT 3 in 4 operator channels) */ + + uint32_t eg_cnt; /* global envelope generator counter */ + uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/288 (288=8*36) */ + uint32_t eg_timer_add; /* step of eg_timer */ + uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */ + + uint32_t fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + uint32_t LFO_AM; + int32_t LFO_PM; + + uint8_t lfo_am_depth; + uint8_t lfo_pm_depth_range; + uint32_t lfo_am_cnt; + uint32_t lfo_am_inc; + uint32_t lfo_pm_cnt; + uint32_t lfo_pm_inc; + + uint32_t noise_rng; /* 23 bit noise shift register */ + uint32_t noise_p; /* current noise 'phase' */ + uint32_t noise_f; /* current noise period */ + + uint8_t OPL3_mode; /* OPL3 extension enable flag */ + + uint8_t rhythm; /* Rhythm mode */ + + int T[2]; /* timer counters */ + uint8_t st[2]; /* timer enable */ + + uint32_t address; /* address register */ + uint8_t status; /* status flag */ + uint8_t statusmask; /* status mask */ + + uint8_t nts; /* NTS (note select) */ + + /* external event callback handlers */ + OPL3_TIMERHANDLER timer_handler; + device_t *TimerParam; + OPL3_IRQHANDLER IRQHandler; + device_t *IRQParam; + OPL3_UPDATEHANDLER UpdateHandler; + device_t *UpdateParam; + + uint8_t type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + //attotime TimerBase; /* Timer base time (==sampling time)*/ + device_t *device; + + /* Optional handlers */ + void SetTimerHandler(OPL3_TIMERHANDLER handler, device_t *device) + { + timer_handler = handler; + TimerParam = device; + } + void SetIRQHandler(OPL3_IRQHANDLER handler, device_t *device) + { + IRQHandler = handler; + IRQParam = device; + } + void SetUpdateHandler(OPL3_UPDATEHANDLER handler, device_t *device) + { + UpdateHandler = handler; + UpdateParam = device; + } +}; + +} // anonymous namespace + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define DV (0.1875/2.0) +static const double ksl_tab[8*16]= +{ + /* OCT 0 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + /* OCT 1 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, + 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, + /* OCT 2 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, + 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, + 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, + /* OCT 3 */ + 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, + 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, + 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, + 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, + /* OCT 4 */ + 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, + 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, + 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, + 10.875/DV,11.250/DV,11.625/DV,12.000/DV, + /* OCT 5 */ + 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, + 9.000/DV,10.125/DV,10.875/DV,11.625/DV, + 12.000/DV,12.750/DV,13.125/DV,13.500/DV, + 13.875/DV,14.250/DV,14.625/DV,15.000/DV, + /* OCT 6 */ + 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, + 12.000/DV,13.125/DV,13.875/DV,14.625/DV, + 15.000/DV,15.750/DV,16.125/DV,16.500/DV, + 16.875/DV,17.250/DV,17.625/DV,18.000/DV, + /* OCT 7 */ + 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, + 15.000/DV,16.125/DV,16.875/DV,17.625/DV, + 18.000/DV,18.750/DV,19.125/DV,19.500/DV, + 19.875/DV,20.250/DV,20.625/DV,21.000/DV +}; +#undef DV + +/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ +static const uint32_t ksl_shift[4] = { 31, 1, 2, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) (uint32_t) ( db * (2.0/ENV_STEP) ) +static const uint32_t sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const unsigned char eg_inc[15*RATE_STEPS]={ +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 for decay */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/* note that there is no O(13) in this table - it's directly in the code */ +static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +static const uint8_t mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML, + 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML +}; +#undef ML + +/* TL_TAB_LEN is calculated as: + +* (12+1)=13 - sinus amplitude bits (Y axis) +* additional 1: to compensate for calculations of negative part of waveform +* (if we don't add it then the greatest possible _negative_ value would be -2 +* and we really need -1 for waveform #7) +* 2 - sinus sign bit (Y axis) +* TL_RES_LEN - sinus resolution (X axis) +*/ +#define TL_TAB_LEN (13*2*TL_RES_LEN) +static signed int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>4) + +/* sin waveform table in 'decibel' scale */ +/* there are eight waveforms on OPL3 chips */ +static unsigned int sin_tab[SIN_LEN * 8]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (losing precision is important) +*/ + +#define LFO_AM_TAB_ELEMENTS 210 + +static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +static const int8_t lfo_pm_table[8*8*2] = { +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + +/* lock level of common table */ +static int num_lock = 0; + +/* work table */ +#define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1]) +#define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2]) +#define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1]) +#define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2]) + + +static inline void OPL3_SLOT_CONNECT(OPL3 *chip, OPL3_SLOT *slot) { + if (slot->conn_enum == CONN_NULL) { + slot->connect = 0; + } else if (slot->conn_enum >= CONN_CHAN0 && slot->conn_enum < CONN_PHASEMOD) { + slot->connect = &chip->chanout[slot->conn_enum]; + } else if (slot->conn_enum == CONN_PHASEMOD) { + slot->connect = &chip->phase_modulation; + } else if (slot->conn_enum == CONN_PHASEMOD2) { + slot->connect = &chip->phase_modulation2; + } +} + +static inline int limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + + +/* status set and IRQ handling */ +static inline void OPL3_STATUS_SET(OPL3 *chip,int flag) +{ + /* set status flag masking out disabled IRQs */ + chip->status |= (flag & chip->statusmask); + if(!(chip->status & 0x80)) + { + if(chip->status & 0x7f) + { /* IRQ on */ + chip->status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,1); + } + } +} + +/* status reset and IRQ handling */ +static inline void OPL3_STATUS_RESET(OPL3 *chip,int flag) +{ + /* reset status flag */ + chip->status &= ~flag; + if(chip->status & 0x80) + { + if (!(chip->status & 0x7f)) + { + chip->status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,0); + } + } +} + +/* IRQ mask set */ +static inline void OPL3_STATUSMASK_SET(OPL3 *chip,int flag) +{ + chip->statusmask = flag; + /* IRQ handling check */ + OPL3_STATUS_SET(chip,0); + OPL3_STATUS_RESET(chip,0); +} + + +/* advance LFO to next sample */ +static inline void advance_lfo(OPL3 *chip) +{ + uint8_t tmp; + + /* LFO */ + chip->lfo_am_cnt += chip->lfo_am_inc; + if (chip->lfo_am_cnt >= ((uint32_t)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((uint32_t)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; + + if (chip->lfo_am_depth) + chip->LFO_AM = tmp; + else + chip->LFO_AM = tmp>>2; + + chip->lfo_pm_cnt += chip->lfo_pm_inc; + chip->LFO_PM = ((chip->lfo_pm_cnt>>LFO_SH) & 7) | chip->lfo_pm_depth_range; +} + +/* advance to next sample */ +static inline void advance(OPL3 *chip) +{ + OPL3_CH *CH; + OPL3_SLOT *op; + int i; + + chip->eg_timer += chip->eg_timer_add; + + while (chip->eg_timer >= chip->eg_timer_overflow) + { + chip->eg_timer -= chip->eg_timer_overflow; + + chip->eg_cnt++; + + for (i=0; i<9*2*2; i++) + { + CH = &chip->P_CH[i/2]; + op = &CH->SLOT[i&1]; +#if 1 + /* Envelope Generator */ + switch(op->state) + { + case EG_ATT: /* attack phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_ar) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>3; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_dr) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= op->sl ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ +// if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_rr) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_rr) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + default: + break; + } +#endif + } + } + + for (i=0; i<9*2*2; i++) + { + CH = &chip->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Phase Generator */ + if(op->vib) + { + uint8_t block; + unsigned int block_fnum = CH->block_fnum; + + unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->Cnt += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + } + else /* LFO phase modulation = zero */ + { + op->Cnt += op->Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op->Cnt += op->Incr; + } + } + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + chip->noise_p += chip->noise_f; + i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + chip->noise_p &= FREQ_MASK; + while (i) + { + /* + uint32_t j; + j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1; + chip->noise_rng = (j<<22) | (chip->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302; + chip->noise_rng >>= 1; + + i--; + } +} + + +static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + uint32_t p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + uint32_t p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm))>>FREQ_SH) & SIN_MASK)]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + + +#define volume_calc(OP) ((OP)->TLL + ((uint32_t)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask)) + +/* calculate output of a standard 2 operator channel + (or 1st part of a 4-op channel) */ +static inline void chan_calc( OPL3 *chip, OPL3_CH *CH ) +{ + OPL3_SLOT *SLOT; + unsigned int env; + signed int out; + + chip->phase_modulation = 0; + chip->phase_modulation2= 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + SLOT->op1_out[1] = 0; + if (env < ENV_QUIET) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + if (SLOT->connect) { + *SLOT->connect += SLOT->op1_out[1]; + } +//logerror("out0=%5i vol0=%4i ", SLOT->op1_out[1], env ); + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if ((env < ENV_QUIET) && SLOT->connect) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); + +//logerror("out1=%5i vol1=%4i\n", op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable), env ); + +} + +/* calculate output of a 2nd part of 4-op channel */ +static inline void chan_calc_ext( OPL3 *chip, OPL3_CH *CH ) +{ + OPL3_SLOT *SLOT; + unsigned int env; + + chip->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + if (env < ENV_QUIET && SLOT->connect) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation2, SLOT->wavetable ); + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if (env < ENV_QUIET && SLOT->connect) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); + +} + +/* + operators used in the rhythm sounds generation process: + + Envelope Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + +channel operator register number Bass High Snare Tom Top +number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + +*/ + +/* calculate rhythm */ + +static inline void chan_calc_rhythm( OPL3 *chip, OPL3_CH *CH, unsigned int noise ) +{ + OPL3_SLOT *SLOT; + signed int *chanout = chip->chanout; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + chip->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + chip->phase_modulation = SLOT->op1_out[0]; + //else ignore output of operator 1 + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + chanout[6] += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) + // SD (16) channel 7->slot 1 + // TOM (14) channel 8->slot 1 + // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) + + /* Envelope generation based on: */ + // HH channel 7->slot1 + // SD channel 7->slot2 + // TOM channel 8->slot1 + // TOP channel 8->slot2 + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + env = volume_calc(SLOT7_1); + if( env < ENV_QUIET ) + { + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + chanout[7] += op_calc(phase<wavetable) * 2; + } + + /* Snare Drum (verified on real YM3812) */ + env = volume_calc(SLOT7_2); + if( env < ENV_QUIET ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + uint32_t phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + chanout[7] += op_calc(phase<wavetable) * 2; + } + + /* Tom Tom (verified on real YM3812) */ + env = volume_calc(SLOT8_1); + if( env < ENV_QUIET ) + chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; + + /* Top Cymbal (verified on real YM3812) */ + env = volume_calc(SLOT8_2); + if( env < ENV_QUIET ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + uint32_t phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + chanout[8] += op_calc(phase<wavetable) * 2; + } + +} + + +/* generic table initialize */ +static int init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = ~tl_tab[ x*2 + 0 ]; /* this *is* different from OPL2 (verified on real YMF262) */ + + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = ~tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; /* this *is* different from OPL2 (verified on real YMF262) */ + } + #if 0 + logerror("tl %04i", x*2); + for (i=0; i<13; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +0 + i*2*TL_RES_LEN ] ); /* positive */ + logerror("\n"); + + logerror("tl %04i", x*2); + for (i=0; i<13; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +1 + i*2*TL_RES_LEN ] ); /* negative */ + logerror("\n"); + #endif + } + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + + /*logerror("YMF262.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + + /* waveform 4: */ + /* /\ ____/\ ____*/ + /* \/ \/ */ + /* output whole sinus waveform in half the cycle(step=2) and output 0 on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[4*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[4*SIN_LEN+i] = sin_tab[i*2]; + + /* waveform 5: */ + /* /\/\____/\/\____*/ + /* */ + /* output abs(whole sinus) waveform in half the cycle(step=2) and output 0 on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[5*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1) ]; + + /* waveform 6: ____ ____ */ + /* */ + /* ____ ____*/ + /* output maximum in half the cycle and output minimum on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[6*SIN_LEN+i] = 1; /* negative */ + else + sin_tab[6*SIN_LEN+i] = 0; /* positive */ + + /* waveform 7: */ + /* |\____ |\____ */ + /* \| \|*/ + /* output sawtooth waveform */ + + if (i & (1<<(SIN_BITS-1)) ) + x = ((SIN_LEN-1)-i)*16 + 1; /* negative: from 8177 to 1 */ + else + x = i*16; /*positive: from 0 to 8176 */ + + if (x > TL_TAB_LEN) + x = TL_TAB_LEN; /* clip to the allowed range */ + + sin_tab[7*SIN_LEN+i] = x; + + //logerror("YMF262.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); + //logerror("YMF262.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); + //logerror("YMF262.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] ); + //logerror("YMF262.C: sin4[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[4*SIN_LEN+i], tl_tab[sin_tab[4*SIN_LEN+i]] ); + //logerror("YMF262.C: sin5[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[5*SIN_LEN+i], tl_tab[sin_tab[5*SIN_LEN+i]] ); + //logerror("YMF262.C: sin6[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[6*SIN_LEN+i], tl_tab[sin_tab[6*SIN_LEN+i]] ); + //logerror("YMF262.C: sin7[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[7*SIN_LEN+i], tl_tab[sin_tab[7*SIN_LEN+i]] ); + } + /*logerror("YMF262.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ + +#ifdef SAVE_SAMPLE + sample[0]=fopen("sampsum.pcm","wb"); +#endif + + return 1; +} + +static void OPLCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif +} + + + +static void OPL3_initalize(OPL3 *chip) +{ + int i; + + /* frequency base */ + chip->freqbase = (chip->rate) ? ((double)chip->clock / (8.0*36)) / chip->rate : 0; +#if 0 + chip->rate = (double)chip->clock / (8.0*36); + chip->freqbase = 1.0; +#endif + + /* logerror("YMF262: freqbase=%f\n", chip->freqbase); */ + + /* Timer base time */ + //chip->TimerBase = attotime::from_hz(chip->clock) * (8*36); + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + chip->fn_tab[i] = (uint32_t)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +#if 0 + logerror("YMF262.C: fn_tab[%4i] = %08x (dec=%8i)\n", + i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 ); +#endif + } + +#if 0 + for( i=0 ; i < 16 ; i++ ) + { + logerror("YMF262.C: sl_tab[%i] = %08x\n", + i, sl_tab[i] ); + } + for( i=0 ; i < 8 ; i++ ) + { + int j; + logerror("YMF262.C: ksl_tab[oct=%2i] =",i); + for (j=0; j<16; j++) + { + logerror("%08x ", static_cast(ksl_tab[i*16+j]) ); + } + logerror("\n"); + } +#endif + + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; + + /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ + chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; + + /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/ + + /* Noise generator: a step takes 1 sample */ + chip->noise_f = (1.0 / 1.0) * (1<freqbase; + + chip->eg_timer_add = (1<freqbase; + chip->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, chip->eg_timer_overflow);*/ + +} + +static inline void FM_KEYON(OPL3_SLOT *SLOT, uint32_t key_set) +{ + if( !SLOT->key ) + { + /* restart Phase Generator */ + SLOT->Cnt = 0; + /* phase -> Attack */ + SLOT->state = EG_ATT; + } + SLOT->key |= key_set; +} + +static inline void FM_KEYOFF(OPL3_SLOT *SLOT, uint32_t key_clr) +{ + if( SLOT->key ) + { + SLOT->key &= key_clr; + + if( !SLOT->key ) + { + /* phase -> Release */ + if (SLOT->state>EG_REL) + SLOT->state = EG_REL; + } + } +} + +/* update phase increment counter of operator (also update the EG rates if necessary) */ +static inline void CALC_FCSLOT(OPL3_CH *CH,OPL3_SLOT *SLOT) +{ + int ksr; + + /* (frequency) phase increment counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 16+60) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_m_dr = (1<eg_sh_dr)-1; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_m_rr = (1<eg_sh_rr)-1; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + } +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +static inline void set_mul(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = mul_tab[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_type = (v&0x20); + SLOT->vib = (v&0x40); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + + if (chip->OPL3_mode & 1) + { + int chan_no = slot/2; + + /* in OPL3 mode */ + //DO THIS: + //if this is one of the slots of 1st channel forming up a 4-op channel + //do normal operation + //else normal 2 operator function + //OR THIS: + //if this is one of the slots of 2nd channel forming up a 4-op channel + //update it using channel data of 1st channel of a pair + //else normal 2 operator function + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + else + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + break; + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + /* update this SLOT using frequency data for 1st channel of a pair */ + CALC_FCSLOT(CH-3,SLOT); + } + else + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + break; + default: + /* normal */ + CALC_FCSLOT(CH,SLOT); + break; + } + } + else + { + /* in OPL2 mode */ + CALC_FCSLOT(CH,SLOT); + } +} + +/* set ksl & tl */ +static inline void set_ksl_tl(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ksl = ksl_shift[v >> 6]; + SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + if (chip->OPL3_mode & 1) + { + int chan_no = slot/2; + + /* in OPL3 mode */ + //DO THIS: + //if this is one of the slots of 1st channel forming up a 4-op channel + //do normal operation + //else normal 2 operator function + //OR THIS: + //if this is one of the slots of 2nd channel forming up a 4-op channel + //update it using channel data of 1st channel of a pair + //else normal 2 operator function + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + else + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + break; + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + /* update this SLOT using frequency data for 1st channel of a pair */ + SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl); + } + else + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + break; + default: + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + break; + } + } + else + { + /* in OPL2 mode */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + +} + +/* set attack rate & decay rate */ +static inline void set_ar_dr(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT->ar + SLOT->ksr) < 16+60) /* verified on real YMF262 - all 15 x rates take "zero" time */ + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_m_dr = (1<eg_sh_dr)-1; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; +} + +/* set sustain level & release rate */ +static inline void set_sl_rr(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->sl = sl_tab[ v>>4 ]; + + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_m_rr = (1<eg_sh_rr)-1; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; +} + + +static void update_channels(OPL3 *chip, OPL3_CH *CH) +{ + /* update channel passed as a parameter and a channel at CH+=3; */ + if (CH->extended) + { /* we've just switched to combined 4 operator mode */ + + } + else + { /* we've just switched to normal 2 operator mode */ + + } + +} + +/* write a value v to register r on OPL chip */ +static void OPL3WriteReg(OPL3 *chip, int r, int v) +{ + OPL3_CH *CH; + unsigned int ch_offset = 0; + int slot; + int block_fnum; + + if(r&0x100) + { + switch(r) + { + case 0x101: /* test register */ + return; + + case 0x104: /* 6 channels enable */ + { + uint8_t prev; + + CH = &chip->P_CH[0]; /* channel 0 */ + prev = CH->extended; + CH->extended = (v>>0) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 1 */ + prev = CH->extended; + CH->extended = (v>>1) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 2 */ + prev = CH->extended; + CH->extended = (v>>2) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + + + CH = &chip->P_CH[9]; /* channel 9 */ + prev = CH->extended; + CH->extended = (v>>3) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 10 */ + prev = CH->extended; + CH->extended = (v>>4) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 11 */ + prev = CH->extended; + CH->extended = (v>>5) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + + } + return; + + case 0x105: /* OPL3 extensions enable register */ + + chip->OPL3_mode = v&0x01; /* OPL3 mode when bit0=1 otherwise it is OPL2 mode */ + + /* following behaviour was tested on real YMF262, + switching OPL3/OPL2 modes on the fly: + - does not change the waveform previously selected (unless when ....) + - does not update CH.A, CH.B, CH.C and CH.D output selectors (registers c0-c8) (unless when ....) + - does not disable channels 9-17 on OPL3->OPL2 switch + - does not switch 4 operator channels back to 2 operator channels + */ + + return; + + default: + if (r < 0x120) + chip->device->logerror("YMF262: write to unknown register (set#2): %03x value=%02x\n",r,v); + break; + } + + ch_offset = 9; /* register page #2 starts from channel 9 (counting from 0) */ + } + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + + + switch(r&0xe0) + { + case 0x00: /* 00-1f:control */ + switch(r&0x1f) + { + case 0x01: /* test register */ + break; + case 0x02: /* Timer 1 */ + chip->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + chip->T[1] = (256-v)*16; + break; +#if 0 + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v&0x80) + { /* IRQ flags clear */ + OPL3_STATUS_RESET(chip,0x60); + } + else + { /* set IRQ mask ,timer enable */ + uint8_t st1 = v & 1; + uint8_t st2 = (v>>1) & 1; + + /* IRQRST,T1MSK,t2MSK,x,x,x,ST2,ST1 */ + OPL3_STATUS_RESET(chip, v & 0x60); + OPL3_STATUSMASK_SET(chip, (~v) & 0x60 ); + + /* timer 2 */ + if(chip->st[1] != st2) + { + attotime period = st2 ? chip->TimerBase * chip->T[1] : attotime::zero; + chip->st[1] = st2; + if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,1,period); + } + /* timer 1 */ + if(chip->st[0] != st1) + { + attotime period = st1 ? chip->TimerBase * chip->T[0] : attotime::zero; + chip->st[0] = st1; + if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,0,period); + } + } +#endif + break; + case 0x08: /* x,NTS,x,x, x,x,x,x */ + chip->nts = v; + break; + + default: + chip->device->logerror("YMF262: write to unknown register: %02x value=%02x\n",r,v); + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(chip, slot + ch_offset*2, v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(chip, slot + ch_offset*2, v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(chip, slot + ch_offset*2, v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(chip, slot + ch_offset*2, v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + if (ch_offset != 0) /* 0xbd register is present in set #1 only */ + return; + + chip->lfo_am_depth = v & 0x80; + chip->lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + chip->rhythm = v&0x3f; + + if(chip->rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + FM_KEYON (&chip->P_CH[6].SLOT[SLOT1], 2); + FM_KEYON (&chip->P_CH[6].SLOT[SLOT2], 2); + } + else + { + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); + } + /* HH key on/off */ + if(v&0x01) FM_KEYON (&chip->P_CH[7].SLOT[SLOT1], 2); + else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); + /* SD key on/off */ + if(v&0x08) FM_KEYON (&chip->P_CH[7].SLOT[SLOT2], 2); + else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); + /* TOM key on/off */ + if(v&0x04) FM_KEYON (&chip->P_CH[8].SLOT[SLOT1], 2); + else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY key on/off */ + if(v&0x02) FM_KEYON (&chip->P_CH[8].SLOT[SLOT2], 2); + else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); + } + else + { + /* BD key off */ + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); + /* HH key off */ + FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); + /* SD key off */ + FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); + /* TOM key off */ + FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY off */ + FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); + } + return; + } + + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &chip->P_CH[(r&0x0f) + ch_offset]; + + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if (chip->OPL3_mode & 1) + { + int chan_no = (r&0x0f) + ch_offset; + + /* in OPL3 mode */ + //DO THIS: + //if this is 1st channel forming up a 4-op channel + //ALSO keyon/off slots of 2nd channel forming up 4-op channel + //else normal 2 operator function keyon/off + //OR THIS: + //if this is 2nd channel forming up 4-op channel just do nothing + //else normal 2 operator function keyon/off + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + //if this is 1st channel forming up a 4-op channel + //ALSO keyon/off slots of 2nd channel forming up 4-op channel + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + FM_KEYON (&(CH+3)->SLOT[SLOT1], 1); + FM_KEYON (&(CH+3)->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + FM_KEYOFF(&(CH+3)->SLOT[SLOT1],~1); + FM_KEYOFF(&(CH+3)->SLOT[SLOT2],~1); + } + } + else + { + //else normal 2 operator function keyon/off + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + //if this is 2nd channel forming up 4-op channel just do nothing + } + else + { + //else normal 2 operator function keyon/off + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + break; + + default: + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + break; + } + } + else + { + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + uint8_t block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = static_cast(ksl_tab[block_fnum>>6]); + CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YMF262) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (chip->nts&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + if (chip->OPL3_mode & 1) + { + int chan_no = (r&0x0f) + ch_offset; + /* in OPL3 mode */ + //DO THIS: + //if this is 1st channel forming up a 4-op channel + //ALSO update slots of 2nd channel forming up 4-op channel + //else normal 2 operator function keyon/off + //OR THIS: + //if this is 2nd channel forming up 4-op channel just do nothing + //else normal 2 operator function keyon/off + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + //if this is 1st channel forming up a 4-op channel + //ALSO update slots of 2nd channel forming up 4-op channel + + /* refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + (CH+3)->SLOT[SLOT1].TLL = (CH+3)->SLOT[SLOT1].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT1].ksl); + (CH+3)->SLOT[SLOT2].TLL = (CH+3)->SLOT[SLOT2].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT2].ksl); + + /* refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT1]); + CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT2]); + } + else + { + //else normal 2 operator function + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + //if this is 2nd channel forming up 4-op channel just do nothing + } + else + { + //else normal 2 operator function + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + + default: + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + break; + } + } + else + { + /* in OPL2 mode */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + } + break; + + case 0xc0: + /* CH.D, CH.C, CH.B, CH.A, FB(3bits), C */ + if( (r&0xf) > 8) return; + + CH = &chip->P_CH[(r&0xf) + ch_offset]; + + if( chip->OPL3_mode & 1 ) + { + int base = ((r&0xf) + ch_offset) * 4; + + /* OPL3 mode */ + chip->pan[ base ] = (v & 0x10) ? ~0 : 0; /* ch.A */ + chip->pan[ base +1 ] = (v & 0x20) ? ~0 : 0; /* ch.B */ + chip->pan[ base +2 ] = (v & 0x40) ? ~0 : 0; /* ch.C */ + chip->pan[ base +3 ] = (v & 0x80) ? ~0 : 0; /* ch.D */ + } + else + { + int base = ((r&0xf) + ch_offset) * 4; + + /* OPL2 mode - always enabled */ + chip->pan[ base ] = ~0; /* ch.A */ + chip->pan[ base +1 ] = ~0; /* ch.B */ + chip->pan[ base +2 ] = ~0; /* ch.C */ + chip->pan[ base +3 ] = ~0; /* ch.D */ + } + + chip->pan_ctrl_value[ (r&0xf) + ch_offset ] = v; /* store control value for OPL3/OPL2 mode switching on the fly */ + + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + + if( chip->OPL3_mode & 1 ) + { + int chan_no = (r&0x0f) + ch_offset; + + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + uint8_t conn = (CH->SLOT[SLOT1].CON<<1) | ((CH+3)->SLOT[SLOT1].CON<<0); + switch(conn) + { + case 0: + /* 1 -> 2 -> 3 -> 4 - out */ + + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; + break; + case 1: + /* 1 -> 2 -\ + 3 -> 4 -+- out */ + + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; + break; + case 2: + /* 1 -----------\ + 2 -> 3 -> 4 -+- out */ + + CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; + CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; + break; + case 3: + /* 1 ------\ + 2 -> 3 -+- out + 4 ------/ */ + CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; + CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + (CH+3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no + 3; + (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; + break; + } + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + OPL3_SLOT_CONNECT(chip, &(CH+3)->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &(CH+3)->SLOT[SLOT2]); + } + else + { + /* 2 operators mode */ + CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + uint8_t conn = ((CH-3)->SLOT[SLOT1].CON<<1) | (CH->SLOT[SLOT1].CON<<0); + switch(conn) + { + case 0: + /* 1 -> 2 -> 3 -> 4 - out */ + + (CH-3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + break; + case 1: + /* 1 -> 2 -\ + 3 -> 4 -+- out */ + + (CH-3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH-3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no - 3; + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + break; + case 2: + /* 1 -----------\ + 2 -> 3 -> 4 -+- out */ + + (CH-3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no - 3; + (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + break; + case 3: + /* 1 ------\ + 2 -> 3 -+- out + 4 ------/ */ + (CH-3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no - 3; + (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + break; + } + OPL3_SLOT_CONNECT(chip, &(CH-3)->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &(CH-3)->SLOT[SLOT2]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + } + else + { + /* 2 operators mode */ + CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + } + break; + + default: + /* 2 operators mode */ + CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + break; + } + } + else + { + /* OPL2 mode - always 2 operators mode */ + CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + } + break; + + case 0xe0: /* waveform select */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + + slot += ch_offset*2; + + CH = &chip->P_CH[slot/2]; + + + /* store 3-bit value written regardless of current OPL2 or OPL3 mode... (verified on real YMF262) */ + v &= 7; + CH->SLOT[slot&1].waveform_number = v; + + /* ... but select only waveforms 0-3 in OPL2 mode */ + if( !(chip->OPL3_mode & 1) ) + { + v &= 3; /* we're in OPL2 mode */ + } + CH->SLOT[slot&1].wavetable = v * SIN_LEN; + break; + } +} + +/* lock/unlock for common table */ +static int OPL3_LockTable(device_t *device) +{ + num_lock++; + if(num_lock>1) return 0; + + /* first time */ + + if( !init_tables() ) + { + num_lock--; + return -1; + } + + return 0; +} + +static void OPL3_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + + /* last time */ + OPLCloseTable(); +} + +static void OPL3ResetChip(OPL3 *chip) +{ + int c,s; + + chip->eg_timer = 0; + chip->eg_cnt = 0; + + chip->noise_rng = 1; /* noise shift register */ + chip->nts = 0; /* note split */ + OPL3_STATUS_RESET(chip,0x60); + + /* reset with register write */ + OPL3WriteReg(chip,0x01,0); /* test register */ + OPL3WriteReg(chip,0x02,0); /* Timer1 */ + OPL3WriteReg(chip,0x03,0); /* Timer2 */ + OPL3WriteReg(chip,0x04,0); /* IRQ mask clear */ + + +//FIX IT registers 101, 104 and 105 + + +//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) + for(c = 0xff ; c >= 0x20 ; c-- ) + OPL3WriteReg(chip,c,0); +//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) + for(c = 0x1ff ; c >= 0x120 ; c-- ) + OPL3WriteReg(chip,c,0); + + + + /* reset operator parameters */ + for( c = 0 ; c < 9*2 ; c++ ) + { + OPL3_CH *CH = &chip->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +} + +/* Create one of virtual YMF262 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +static OPL3 *OPL3Create(device_t *device, int clock, int rate, int type) +{ + OPL3 *chip; + + if (OPL3_LockTable(device) == -1) return 0; + + /* allocate memory block */ + chip = auto_alloc_clear(device->machine(), OPL3 ); + + chip->device = device; + chip->type = type; + chip->clock = clock; + chip->rate = rate; + + /* init global tables */ + OPL3_initalize(chip); + + /* reset chip */ + OPL3ResetChip(chip); + return chip; +} + +/* Destroy one of virtual YMF262 */ +static void OPL3Destroy(OPL3 *chip) +{ + OPL3_UnLockTable(); + auto_free(chip->device->machine(), chip); +} + + +/* YMF262 I/O interface */ +static int OPL3Write(OPL3 *chip, int a, int v) +{ + /* data bus is 8 bits */ + v &= 0xff; + + + switch(a&3) + { + case 0: /* address port 0 (register set #1) */ + chip->address = v; + break; + + case 1: /* data port - ignore A1 */ + case 3: /* data port - ignore A1 */ + if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0); + OPL3WriteReg(chip,chip->address,v); + break; + + case 2: /* address port 1 (register set #2) */ + + /* verified on real YMF262: + in OPL3 mode: + address line A1 is stored during *address* write and ignored during *data* write. + + in OPL2 mode: + register set#2 writes go to register set#1 (ignoring A1) + verified on registers from set#2: 0x01, 0x04, 0x20-0xef + The only exception is register 0x05. + */ + if( chip->OPL3_mode & 1 ) + { + /* OPL3 mode */ + chip->address = v | 0x100; + } + else + { + /* in OPL2 mode the only accessible in set #2 is register 0x05 */ + if( v==5 ) + chip->address = v | 0x100; + else + chip->address = v; /* verified range: 0x01, 0x04, 0x20-0xef(set #2 becomes set #1 in opl2 mode) */ + } + break; + } + + return chip->status>>7; +} + +static unsigned char OPL3Read(OPL3 *chip,int a) +{ + if( a==0 ) + { + /* status port */ + return chip->status; + } + + return 0x00; /* verified on real YMF262 */ +} + + + +static int OPL3TimerOver(OPL3 *chip,int c) +{ + if( c ) + { /* Timer B */ + OPL3_STATUS_SET(chip,0x20); + } + else + { /* Timer A */ + OPL3_STATUS_SET(chip,0x40); + } + /* reload timer */ +// if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,chip->TimerBase * chip->T[c]); + return chip->status>>7; +} + +static void OPL3_save_state(OPL3 *chip, device_t *device) { +#if 0 + for (int ch=0; ch<18; ch++) { + OPL3_CH *channel = &chip->P_CH[ch]; + device->save_item(NAME(channel->block_fnum), ch); + device->save_item(NAME(channel->fc), ch); + device->save_item(NAME(channel->ksl_base), ch); + device->save_item(NAME(channel->kcode), ch); + device->save_item(NAME(channel->extended), ch); + + for (int sl=0; sl<2; sl++) { + OPL3_SLOT *slot = &channel->SLOT[sl]; + device->save_item(NAME(slot->ar), ch*2+sl); + device->save_item(NAME(slot->dr), ch*2+sl); + device->save_item(NAME(slot->rr), ch*2+sl); + device->save_item(NAME(slot->KSR), ch*2+sl); + device->save_item(NAME(slot->ksl), ch*2+sl); + device->save_item(NAME(slot->ksr), ch*2+sl); + device->save_item(NAME(slot->mul), ch*2+sl); + + device->save_item(NAME(slot->Cnt), ch*2+sl); + device->save_item(NAME(slot->Incr), ch*2+sl); + device->save_item(NAME(slot->FB), ch*2+sl); + device->save_item(NAME(slot->conn_enum), ch*2+sl); + device->save_item(NAME(slot->op1_out), ch*2+sl); + device->save_item(NAME(slot->CON), ch*2+sl); + + device->save_item(NAME(slot->eg_type), ch*2+sl); + device->save_item(NAME(slot->state), ch*2+sl); + device->save_item(NAME(slot->TL), ch*2+sl); + device->save_item(NAME(slot->TLL), ch*2+sl); + device->save_item(NAME(slot->volume), ch*2+sl); + device->save_item(NAME(slot->sl), ch*2+sl); + + device->save_item(NAME(slot->eg_m_ar), ch*2+sl); + device->save_item(NAME(slot->eg_sh_ar), ch*2+sl); + device->save_item(NAME(slot->eg_sel_ar), ch*2+sl); + device->save_item(NAME(slot->eg_m_dr), ch*2+sl); + device->save_item(NAME(slot->eg_sh_dr), ch*2+sl); + device->save_item(NAME(slot->eg_sel_dr), ch*2+sl); + device->save_item(NAME(slot->eg_m_rr), ch*2+sl); + device->save_item(NAME(slot->eg_sh_rr), ch*2+sl); + device->save_item(NAME(slot->eg_sel_rr), ch*2+sl); + + device->save_item(NAME(slot->key), ch*2+sl); + + device->save_item(NAME(slot->AMmask), ch*2+sl); + device->save_item(NAME(slot->vib), ch*2+sl); + + device->save_item(NAME(slot->waveform_number), ch*2+sl); + device->save_item(NAME(slot->wavetable), ch*2+sl); + } + } + + device->save_item(NAME(chip->pan)); + device->save_item(NAME(chip->pan_ctrl_value)); + + device->save_item(NAME(chip->lfo_am_depth)); + device->save_item(NAME(chip->lfo_pm_depth_range)); + + device->save_item(NAME(chip->OPL3_mode)); + device->save_item(NAME(chip->rhythm)); + + device->save_item(NAME(chip->address)); + device->save_item(NAME(chip->status)); + device->save_item(NAME(chip->statusmask)); +#endif +} + +void * ymf262_init(device_t *device, int clock, int rate) +{ + void *chip = OPL3Create(device,clock,rate,OPL3_TYPE_YMF262); + OPL3_save_state((OPL3 *)chip, device); + + return chip; +} + +void ymf262_post_load(void *chip) { + OPL3 *opl3 = (OPL3 *)chip; + for (int ch=0; ch<18; ch++) { + for (int sl=0; sl<2; sl++) { + OPL3_SLOT_CONNECT(opl3, &(opl3->P_CH[ch].SLOT[sl])); + } + } +} + +void ymf262_shutdown(void *chip) +{ + OPL3Destroy((OPL3 *)chip); +} +void ymf262_reset_chip(void *chip) +{ + OPL3ResetChip((OPL3 *)chip); +} + +int ymf262_write(void *chip, int a, int v) +{ + return OPL3Write((OPL3 *)chip, a, v); +} + +unsigned char ymf262_read(void *chip, int a) +{ + /* Note on status register: */ + + /* YM3526(OPL) and YM3812(OPL2) return bit2 and bit1 in HIGH state */ + + /* YMF262(OPL3) always returns bit2 and bit1 in LOW state */ + /* which can be used to identify the chip */ + + /* YMF278(OPL4) returns bit2 in LOW and bit1 in HIGH state ??? info from manual - not verified */ + + return OPL3Read((OPL3 *)chip, a); +} +int ymf262_timer_over(void *chip, int c) +{ + return OPL3TimerOver((OPL3 *)chip, c); +} + +void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER timer_handler, device_t *device) +{ + reinterpret_cast(chip)->SetTimerHandler(timer_handler, device); +} +void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device) +{ + reinterpret_cast(chip)->SetIRQHandler(IRQHandler, device); +} +void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device) +{ + reinterpret_cast(chip)->SetUpdateHandler(UpdateHandler, device); +} + + +/* +** Generate samples for one of the YMF262's +** +** 'which' is the virtual YMF262 number +** '**buffers' is table of 4 pointers to the buffers: CH.A, CH.B, CH.C and CH.D +** 'length' is the number of samples that should be generated +*/ +void ymf262_update_one(void *_chip, OPL3SAMPLE **buffers, int length) +{ + int i; + OPL3 *chip = (OPL3 *)_chip; + signed int *chanout = chip->chanout; + uint8_t rhythm = chip->rhythm&0x20; + + OPL3SAMPLE *ch_a = buffers[0]; + OPL3SAMPLE *ch_b = buffers[1]; + OPL3SAMPLE *ch_c = buffers[2]; + OPL3SAMPLE *ch_d = buffers[3]; + + for( i=0; i < length ; i++ ) + { + int a,b,c,d; + + + advance_lfo(chip); + + /* clear channel outputs */ + memset(chip->chanout, 0, sizeof(chip->chanout)); + +#if 1 + /* register set #1 */ + chan_calc(chip, &chip->P_CH[0]); /* extended 4op ch#0 part 1 or 2op ch#0 */ + if (chip->P_CH[0].extended) + chan_calc_ext(chip, &chip->P_CH[3]); /* extended 4op ch#0 part 2 */ + else + chan_calc(chip, &chip->P_CH[3]); /* standard 2op ch#3 */ + + + chan_calc(chip, &chip->P_CH[1]); /* extended 4op ch#1 part 1 or 2op ch#1 */ + if (chip->P_CH[1].extended) + chan_calc_ext(chip, &chip->P_CH[4]); /* extended 4op ch#1 part 2 */ + else + chan_calc(chip, &chip->P_CH[4]); /* standard 2op ch#4 */ + + + chan_calc(chip, &chip->P_CH[2]); /* extended 4op ch#2 part 1 or 2op ch#2 */ + if (chip->P_CH[2].extended) + chan_calc_ext(chip, &chip->P_CH[5]); /* extended 4op ch#2 part 2 */ + else + chan_calc(chip, &chip->P_CH[5]); /* standard 2op ch#5 */ + + + if(!rhythm) + { + chan_calc(chip, &chip->P_CH[6]); + chan_calc(chip, &chip->P_CH[7]); + chan_calc(chip, &chip->P_CH[8]); + } + else /* Rhythm part */ + { + chan_calc_rhythm(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 ); + } + + /* register set #2 */ + chan_calc(chip, &chip->P_CH[ 9]); + if (chip->P_CH[9].extended) + chan_calc_ext(chip, &chip->P_CH[12]); + else + chan_calc(chip, &chip->P_CH[12]); + + + chan_calc(chip, &chip->P_CH[10]); + if (chip->P_CH[10].extended) + chan_calc_ext(chip, &chip->P_CH[13]); + else + chan_calc(chip, &chip->P_CH[13]); + + + chan_calc(chip, &chip->P_CH[11]); + if (chip->P_CH[11].extended) + chan_calc_ext(chip, &chip->P_CH[14]); + else + chan_calc(chip, &chip->P_CH[14]); + + + /* channels 15,16,17 are fixed 2-operator channels only */ + chan_calc(chip, &chip->P_CH[15]); + chan_calc(chip, &chip->P_CH[16]); + chan_calc(chip, &chip->P_CH[17]); +#endif + + /* accumulator register set #1 */ + a = chanout[0] & chip->pan[0]; + b = chanout[0] & chip->pan[1]; + c = chanout[0] & chip->pan[2]; + d = chanout[0] & chip->pan[3]; +#if 1 + a += chanout[1] & chip->pan[4]; + b += chanout[1] & chip->pan[5]; + c += chanout[1] & chip->pan[6]; + d += chanout[1] & chip->pan[7]; + a += chanout[2] & chip->pan[8]; + b += chanout[2] & chip->pan[9]; + c += chanout[2] & chip->pan[10]; + d += chanout[2] & chip->pan[11]; + + a += chanout[3] & chip->pan[12]; + b += chanout[3] & chip->pan[13]; + c += chanout[3] & chip->pan[14]; + d += chanout[3] & chip->pan[15]; + a += chanout[4] & chip->pan[16]; + b += chanout[4] & chip->pan[17]; + c += chanout[4] & chip->pan[18]; + d += chanout[4] & chip->pan[19]; + a += chanout[5] & chip->pan[20]; + b += chanout[5] & chip->pan[21]; + c += chanout[5] & chip->pan[22]; + d += chanout[5] & chip->pan[23]; + + a += chanout[6] & chip->pan[24]; + b += chanout[6] & chip->pan[25]; + c += chanout[6] & chip->pan[26]; + d += chanout[6] & chip->pan[27]; + a += chanout[7] & chip->pan[28]; + b += chanout[7] & chip->pan[29]; + c += chanout[7] & chip->pan[30]; + d += chanout[7] & chip->pan[31]; + a += chanout[8] & chip->pan[32]; + b += chanout[8] & chip->pan[33]; + c += chanout[8] & chip->pan[34]; + d += chanout[8] & chip->pan[35]; + + /* accumulator register set #2 */ + a += chanout[9] & chip->pan[36]; + b += chanout[9] & chip->pan[37]; + c += chanout[9] & chip->pan[38]; + d += chanout[9] & chip->pan[39]; + a += chanout[10] & chip->pan[40]; + b += chanout[10] & chip->pan[41]; + c += chanout[10] & chip->pan[42]; + d += chanout[10] & chip->pan[43]; + a += chanout[11] & chip->pan[44]; + b += chanout[11] & chip->pan[45]; + c += chanout[11] & chip->pan[46]; + d += chanout[11] & chip->pan[47]; + + a += chanout[12] & chip->pan[48]; + b += chanout[12] & chip->pan[49]; + c += chanout[12] & chip->pan[50]; + d += chanout[12] & chip->pan[51]; + a += chanout[13] & chip->pan[52]; + b += chanout[13] & chip->pan[53]; + c += chanout[13] & chip->pan[54]; + d += chanout[13] & chip->pan[55]; + a += chanout[14] & chip->pan[56]; + b += chanout[14] & chip->pan[57]; + c += chanout[14] & chip->pan[58]; + d += chanout[14] & chip->pan[59]; + + a += chanout[15] & chip->pan[60]; + b += chanout[15] & chip->pan[61]; + c += chanout[15] & chip->pan[62]; + d += chanout[15] & chip->pan[63]; + a += chanout[16] & chip->pan[64]; + b += chanout[16] & chip->pan[65]; + c += chanout[16] & chip->pan[66]; + d += chanout[16] & chip->pan[67]; + a += chanout[17] & chip->pan[68]; + b += chanout[17] & chip->pan[69]; + c += chanout[17] & chip->pan[70]; + d += chanout[17] & chip->pan[71]; +#endif + a >>= FINAL_SH; + b >>= FINAL_SH; + c >>= FINAL_SH; + d >>= FINAL_SH; + + /* limit check */ + a = limit( a , MAXOUT, MINOUT ); + b = limit( b , MAXOUT, MINOUT ); + c = limit( c , MAXOUT, MINOUT ); + d = limit( d , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + ch_a[i] = a; + ch_b[i] = b; + ch_c[i] = c; + ch_d[i] = d; + + advance(chip); + } + +} diff --git a/src/hardware/mame/ymf262.h b/src/hardware/mame/ymf262.h new file mode 100644 index 0000000..b82a89b --- /dev/null +++ b/src/hardware/mame/ymf262.h @@ -0,0 +1,40 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +#ifndef MAME_SOUND_YMF262_H +#define MAME_SOUND_YMF262_H + +#pragma once + +/* select number of output bits: 8 or 16 */ +#define OPL3_SAMPLE_BITS 16 + +typedef stream_sample_t OPL3SAMPLE; +/* +#if (OPL3_SAMPLE_BITS==16) +typedef int16_t OPL3SAMPLE; +#endif +#if (OPL3_SAMPLE_BITS==8) +typedef int8_t OPL3SAMPLE; +#endif +*/ + +typedef void (*OPL3_TIMERHANDLER)(device_t *device,int timer,const attotime &period); +typedef void (*OPL3_IRQHANDLER)(device_t *device,int irq); +typedef void (*OPL3_UPDATEHANDLER)(device_t *device,int min_interval_us); + + +void *ymf262_init(device_t *device, int clock, int rate); +void ymf262_post_load(void *chip); +void ymf262_shutdown(void *chip); +void ymf262_reset_chip(void *chip); +int ymf262_write(void *chip, int a, int v); +unsigned char ymf262_read(void *chip, int a); +int ymf262_timer_over(void *chip, int c); +void ymf262_update_one(void *chip, OPL3SAMPLE **buffers, int length); + +void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER TimerHandler, device_t *device); +void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device); +void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device); + + +#endif // MAME_SOUND_YMF262_H diff --git a/src/hardware/memory.cpp b/src/hardware/memory.cpp index a347c91..5069b8a 100644 --- a/src/hardware/memory.cpp +++ b/src/hardware/memory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -73,7 +73,7 @@ public: LOG_MSG("Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip); } #endif - return 0; + return 0xff; } void writeb(PhysPt addr,Bitu val) { #if C_DEBUG diff --git a/src/hardware/mixer.cpp b/src/hardware/mixer.cpp index 829d99a..4d4be5c 100644 --- a/src/hardware/mixer.cpp +++ b/src/hardware/mixer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -9,15 +9,15 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* +/* Remove the sdl code from here and have it handeld in the sdlmain. That should call the mixer start from there or something. */ @@ -47,12 +47,24 @@ #include "mapper.h" #include "hardware.h" #include "programs.h" +#include "midi.h" #define MIXER_SSIZE 4 -#define MIXER_SHIFT 14 -#define MIXER_REMAIN ((1< MIN_AUDIO) @@ -63,9 +75,12 @@ static INLINE Bit16s MIXER_CLIP(Bits SAMP) { static struct { Bit32s work[MIXER_BUFSIZE][2]; + //Write/Read pointers for the buffer Bitu pos,done; Bitu needed, min_needed, max_needed; - Bit32u tick_add,tick_remain; + //For every millisecond tick how many samples need to be generated + Bit32u tick_add; + Bit32u tick_counter; float mastervol[2]; MixerChannel * channels; bool nosound; @@ -84,6 +99,7 @@ MixerChannel * MIXER_AddChannel(MIXER_Handler handler,Bitu freq,const char * nam chan->next=mixer.channels; chan->SetVolume(1,1); chan->enabled=false; + chan->interpolate = false; mixer.channels=chan; return chan; } @@ -131,65 +147,77 @@ void MixerChannel::Enable(bool _yesno) { if (_yesno==enabled) return; enabled=_yesno; if (enabled) { - freq_index=MIXER_REMAIN; + freq_counter = 0; SDL_LockAudio(); if (donedone) { - Bitu todo=needed-done; - todo *= freq_add; - todo = (todo >> MIXER_SHIFT) + ((todo & MIXER_REMAIN)!=0); - handler(todo); + Bitu left = (needed - done); + left *= freq_add; + left = (left >> FREQ_SHIFT) + ((left & FREQ_MASK)!=0); + handler(left); } } void MixerChannel::AddSilence(void) { if (done inline void MixerChannel::AddSamples(Bitu len, const Type* data) { - Bits diff[2]; - Bitu mixpos=mixer.pos+done; - freq_index&=MIXER_REMAIN; - Bitu pos=0;Bitu new_pos; - - goto thestart; - for (;;) { - new_pos=freq_index >> MIXER_SHIFT; - if (pos=len) return; + //Position where to write the data + Bitu mixpos = mixer.pos + done; + //Position in the incoming data + Bitu pos = 0; + //Mix and data for the full length + while (1) { + //Does new data need to get read? + while (freq_counter >= FREQ_NEXT) { + //Would this overflow the source data, then it's time to leave + if (pos >= len) + return; + freq_counter -= FREQ_NEXT; + prevSample[0] = nextSample[0]; + if (stereo) { + prevSample[1] = nextSample[1]; + } if ( sizeof( Type) == 1) { if (!signeddata) { if (stereo) { - diff[0]=(((Bit8s)(data[pos*2+0] ^ 0x80)) << 8)-last[0]; - diff[1]=(((Bit8s)(data[pos*2+1] ^ 0x80)) << 8)-last[1]; + nextSample[0]=(((Bit8s)(data[pos*2+0] ^ 0x80)) << 8); + nextSample[1]=(((Bit8s)(data[pos*2+1] ^ 0x80)) << 8); } else { - diff[0]=(((Bit8s)(data[pos] ^ 0x80)) << 8)-last[0]; + nextSample[0]=(((Bit8s)(data[pos] ^ 0x80)) << 8); } } else { if (stereo) { - diff[0]=(data[pos*2+0] << 8)-last[0]; - diff[1]=(data[pos*2+1] << 8)-last[1]; + nextSample[0]=(data[pos*2+0] << 8); + nextSample[1]=(data[pos*2+1] << 8); } else { - diff[0]=(data[pos] << 8)-last[0]; + nextSample[0]=(data[pos] << 8); } } //16bit and 32bit both contain 16bit data internally @@ -197,91 +225,109 @@ thestart: if (signeddata) { if (stereo) { if (nativeorder) { - diff[0]=data[pos*2+0]-last[0]; - diff[1]=data[pos*2+1]-last[1]; + nextSample[0]=data[pos*2+0]; + nextSample[1]=data[pos*2+1]; } else { if ( sizeof( Type) == 2) { - diff[0]=(Bit16s)host_readw((HostPt)&data[pos*2+0])-last[0]; - diff[1]=(Bit16s)host_readw((HostPt)&data[pos*2+1])-last[1]; + nextSample[0]=(Bit16s)host_readw((HostPt)&data[pos*2+0]); + nextSample[1]=(Bit16s)host_readw((HostPt)&data[pos*2+1]); } else { - diff[0]=(Bit32s)host_readd((HostPt)&data[pos*2+0])-last[0]; - diff[1]=(Bit32s)host_readd((HostPt)&data[pos*2+1])-last[1]; + nextSample[0]=(Bit32s)host_readd((HostPt)&data[pos*2+0]); + nextSample[1]=(Bit32s)host_readd((HostPt)&data[pos*2+1]); } } } else { if (nativeorder) { - diff[0]=data[pos]-last[0]; + nextSample[0] = data[pos]; } else { if ( sizeof( Type) == 2) { - diff[0]=(Bit16s)host_readw((HostPt)&data[pos])-last[0]; + nextSample[0]=(Bit16s)host_readw((HostPt)&data[pos]); } else { - diff[0]=(Bit32s)host_readd((HostPt)&data[pos])-last[0]; + nextSample[0]=(Bit32s)host_readd((HostPt)&data[pos]); } } } } else { if (stereo) { if (nativeorder) { - diff[0]=(Bits)data[pos*2+0]-32768-last[0]; - diff[1]=(Bits)data[pos*2+1]-32768-last[1]; + nextSample[0]=(Bits)data[pos*2+0]-32768; + nextSample[1]=(Bits)data[pos*2+1]-32768; } else { if ( sizeof( Type) == 2) { - diff[0]=(Bits)host_readw((HostPt)&data[pos*2+0])-32768-last[0]; - diff[1]=(Bits)host_readw((HostPt)&data[pos*2+1])-32768-last[1]; + nextSample[0]=(Bits)host_readw((HostPt)&data[pos*2+0])-32768; + nextSample[1]=(Bits)host_readw((HostPt)&data[pos*2+1])-32768; } else { - diff[0]=(Bits)host_readd((HostPt)&data[pos*2+0])-32768-last[0]; - diff[1]=(Bits)host_readd((HostPt)&data[pos*2+1])-32768-last[1]; + nextSample[0]=(Bits)host_readd((HostPt)&data[pos*2+0])-32768; + nextSample[1]=(Bits)host_readd((HostPt)&data[pos*2+1])-32768; } } } else { if (nativeorder) { - diff[0]=(Bits)data[pos]-32768-last[0]; + nextSample[0]=(Bits)data[pos]-32768; } else { if ( sizeof( Type) == 2) { - diff[0]=(Bits)host_readw((HostPt)&data[pos])-32768-last[0]; + nextSample[0]=(Bits)host_readw((HostPt)&data[pos])-32768; } else { - diff[0]=(Bits)host_readd((HostPt)&data[pos])-32768-last[0]; + nextSample[0]=(Bits)host_readd((HostPt)&data[pos])-32768; } } } } } + //This sample has been handled now, increase position + pos++; } - Bits diff_mul=freq_index & MIXER_REMAIN; - freq_index+=freq_add; - mixpos&=MIXER_BUFMASK; - Bits sample=last[0]+((diff[0]*diff_mul) >> MIXER_SHIFT); - mixer.work[mixpos][0]+=sample*volmul[0]; - if (stereo) sample=last[1]+((diff[1]*diff_mul) >> MIXER_SHIFT); - mixer.work[mixpos][1]+=sample*volmul[1]; - mixpos++;done++; + //Where to write + mixpos &= MIXER_BUFMASK; + Bit32s* write = mixer.work[mixpos]; + if (!interpolate) { + write[0] += prevSample[0] * volmul[0]; + write[1] += (stereo ? prevSample[1] : prevSample[0]) * volmul[1]; + } + else { + Bits diff_mul = freq_counter & FREQ_MASK; + Bits sample = prevSample[0] + (((nextSample[0] - prevSample[0]) * diff_mul) >> FREQ_SHIFT); + write[0] += sample*volmul[0]; + if (stereo) { + sample = prevSample[1] + (((nextSample[1] - prevSample[1]) * diff_mul) >> FREQ_SHIFT); + } + write[1] += sample*volmul[1]; + } + //Prepare for next sample + freq_counter += freq_add; + mixpos++; + done++; } } void MixerChannel::AddStretched(Bitu len,Bit16s * data) { - if (done>=needed) { - LOG_MSG("Can't add, buffer full"); + if (done >= needed) { + LOG_MSG("Can't add, buffer full"); return; } - Bitu outlen=needed-done;Bits diff; - freq_index=0; - Bitu temp_add=(len << MIXER_SHIFT)/outlen; - Bitu mixpos=mixer.pos+done;done=needed; - Bitu pos=0; - diff=data[0]-last[0]; + //Target samples this inputs gets stretched into + Bitu outlen = needed - done; + Bitu index = 0; + Bitu index_add = (len << FREQ_SHIFT)/outlen; + Bitu mixpos = mixer.pos + done; + done = needed; + Bitu pos = 0; + while (outlen--) { - Bitu new_pos=freq_index >> MIXER_SHIFT; - if (pos> FREQ_SHIFT; + if (pos != new_pos) { + pos = new_pos; + //Forward the previous sample + prevSample[0] = data[0]; + data++; } - Bits diff_mul=freq_index & MIXER_REMAIN; - freq_index+=temp_add; - mixpos&=MIXER_BUFMASK; - Bits sample=last[0]+((diff*diff_mul) >> MIXER_SHIFT); - mixer.work[mixpos][0]+=sample*volmul[0]; - mixer.work[mixpos][1]+=sample*volmul[1]; + Bits diff = data[0] - prevSample[0]; + Bits diff_mul = index & FREQ_MASK; + index += index_add; + mixpos &= MIXER_BUFMASK; + Bits sample = prevSample[0] + ((diff * diff_mul) >> FREQ_SHIFT); + mixer.work[mixpos][0] += sample * volmul[0]; + mixer.work[mixpos][1] += sample * volmul[1]; mixpos++; } } @@ -348,11 +394,22 @@ void MixerChannel::FillUp(void) { extern bool ticksLocked; static inline bool Mixer_irq_important(void) { - /* In some states correct timing of the irqs is more important then + /* In some states correct timing of the irqs is more important then * non stuttering audo */ return (ticksLocked || (CaptureState & (CAPTURE_WAVE|CAPTURE_VIDEO))); } +static Bit32u calc_tickadd(Bit32u freq) { +#if TICK_SHIFT >16 + Bit64u freq64 = static_cast(freq); + freq64 = (freq64<(freq64); + return r; +#else + return (freq<1024) + if (added>1024) added=1024; Bitu readpos=(mixer.pos+mixer.done)&MIXER_BUFMASK; for (Bitu i=0;i>MIXER_SHIFT); - mixer.tick_remain&=MIXER_REMAIN; + mixer.tick_counter += mixer.tick_add; + mixer.needed+=(mixer.tick_counter >> TICK_SHIFT); + mixer.tick_counter &= TICK_MASK; SDL_UnlockAudio(); } @@ -404,17 +461,19 @@ static void MIXER_Mix_NoSound(void) { else chan->done=0; } /* Set values for next tick */ - mixer.tick_remain+=mixer.tick_add; - mixer.needed=mixer.tick_remain>>MIXER_SHIFT; - mixer.tick_remain&=MIXER_REMAIN; + mixer.tick_counter += mixer.tick_add; + mixer.needed = (mixer.tick_counter >> TICK_SHIFT); + mixer.tick_counter &= TICK_MASK; mixer.done=0; } -static void MIXER_CallBack(void * userdata, Uint8 *stream, int len) { +static void SDLCALL MIXER_CallBack(void * userdata, Uint8 *stream, int len) { Bitu need=(Bitu)len/MIXER_SSIZE; Bit16s * output=(Bit16s *)stream; Bitu reduce; - Bitu pos, index, index_add; + Bitu pos; + //Local resampling counter to manipulate the data when sending it off to the callback + Bitu index, index_add; Bits sample; /* Enough room in the buffer ? */ if (mixer.done < need) { @@ -422,15 +481,15 @@ static void MIXER_CallBack(void * userdata, Uint8 *stream, int len) { if((need - mixer.done) > (need >>7) ) //Max 1 procent stretch. return; reduce = mixer.done; - index_add = (reduce << MIXER_SHIFT) / need; - mixer.tick_add = ((mixer.freq+mixer.min_needed) << MIXER_SHIFT)/1000; + index_add = (reduce << TICK_SHIFT) / need; + mixer.tick_add = calc_tickadd(mixer.freq+mixer.min_needed); } else if (mixer.done < mixer.max_needed) { Bitu left = mixer.done - need; if (left < mixer.min_needed) { if( !Mixer_irq_important() ) { Bitu needed = mixer.needed - need; Bitu diff = (mixer.min_needed>needed?mixer.min_needed:needed) - left; - mixer.tick_add = ((mixer.freq+(diff*3)) << MIXER_SHIFT)/1000; + mixer.tick_add = calc_tickadd(mixer.freq+(diff*3)); left = 0; //No stretching as we compensate with the tick_add value } else { left = (mixer.min_needed - left); @@ -438,10 +497,10 @@ static void MIXER_CallBack(void * userdata, Uint8 *stream, int len) { } // LOG_MSG("needed underrun need %d, have %d, min %d, left %d", need, mixer.done, mixer.min_needed, left); reduce = need - left; - index_add = (reduce << MIXER_SHIFT) / need; + index_add = (reduce << TICK_SHIFT) / need; } else { reduce = need; - index_add = (1 << MIXER_SHIFT); + index_add = (1 << TICK_SHIFT); // LOG_MSG("regular run need %d, have %d, min %d, left %d", need, mixer.done, mixer.min_needed, left); /* Mixer tick value being updated: @@ -453,32 +512,32 @@ static void MIXER_CallBack(void * userdata, Uint8 *stream, int len) { Bitu diff = left - mixer.min_needed; if(diff > (mixer.min_needed<<1)) diff = mixer.min_needed<<1; if(diff > (mixer.min_needed>>1)) - mixer.tick_add = ((mixer.freq-(diff/5)) << MIXER_SHIFT)/1000; - else if (diff > (mixer.min_needed>>4)) - mixer.tick_add = ((mixer.freq-(diff>>3)) << MIXER_SHIFT)/1000; + mixer.tick_add = calc_tickadd(mixer.freq-(diff/5)); + else if (diff > (mixer.min_needed>>2)) + mixer.tick_add = calc_tickadd(mixer.freq-(diff>>3)); else - mixer.tick_add = (mixer.freq<< MIXER_SHIFT)/1000; + mixer.tick_add = calc_tickadd(mixer.freq); } } else { /* There is way too much data in the buffer */ // LOG_MSG("overflow run need %d, have %d, min %d", need, mixer.done, mixer.min_needed); if (mixer.done > MIXER_BUFSIZE) index_add = MIXER_BUFSIZE - 2*mixer.min_needed; - else + else index_add = mixer.done - 2*mixer.min_needed; - index_add = (index_add << MIXER_SHIFT) / need; + index_add = (index_add << TICK_SHIFT) / need; reduce = mixer.done - 2* mixer.min_needed; - mixer.tick_add = ((mixer.freq-(mixer.min_needed/5)) << MIXER_SHIFT)/1000; + mixer.tick_add = calc_tickadd(mixer.freq-(mixer.min_needed/5)); } /* Reduce done count in all channels */ for (MixerChannel * chan=mixer.channels;chan;chan=chan->next) { if (chan->done>reduce) chan->done-=reduce; else chan->done=0; } - + // Reset mixer.tick_add when irqs are important if( Mixer_irq_important() ) - mixer.tick_add=(mixer.freq<< MIXER_SHIFT)/1000; + mixer.tick_add = calc_tickadd(mixer.freq); mixer.done -= reduce; mixer.needed -= reduce; @@ -487,7 +546,7 @@ static void MIXER_CallBack(void * userdata, Uint8 *stream, int len) { index = 0; if(need != reduce) { while (need--) { - Bitu i = (pos + (index >> MIXER_SHIFT )) & MIXER_BUFMASK; + Bitu i = (pos + (index >> TICK_SHIFT )) & MIXER_BUFMASK; index += index_add; sample=mixer.work[i][0]>>MIXER_VOLSHIFT; *output++=MIXER_CLIP(sample); @@ -553,19 +612,18 @@ public: if (cmd->FindString("MASTER",temp_line,false)) { MakeVolume((char *)temp_line.c_str(),mixer.mastervol[0],mixer.mastervol[1]); } - MixerChannel * chan=mixer.channels; + MixerChannel * chan = mixer.channels; while (chan) { if (cmd->FindString(chan->name,temp_line,false)) { MakeVolume((char *)temp_line.c_str(),chan->volmain[0],chan->volmain[1]); } chan->UpdateVolume(); - chan=chan->next; + chan = chan->next; } if (cmd->FindExist("/NOSHOW")) return; - chan=mixer.channels; WriteOut("Channel Main Main(dB)\n"); ShowVolume("MASTER",mixer.mastervol[0],mixer.mastervol[1]); - for (chan=mixer.channels;chan;chan=chan->next) + for (chan = mixer.channels;chan;chan = chan->next) ShowVolume(chan->name,chan->volmain[0],chan->volmain[1]); } private: @@ -577,15 +635,7 @@ private: } void ListMidi(){ -#if defined (WIN32) - unsigned int total = midiOutGetNumDevs(); - for(unsigned int i=0;iListAll(this); }; }; @@ -640,22 +690,22 @@ void MIXER_Init(Section* sec) { spec.userdata=NULL; spec.samples=(Uint16)mixer.blocksize; - mixer.tick_remain=0; + mixer.tick_counter=0; if (mixer.nosound) { - LOG_MSG("MIXER:No Sound Mode Selected."); - mixer.tick_add=((mixer.freq) << MIXER_SHIFT)/1000; + LOG_MSG("MIXER: No Sound Mode Selected."); + mixer.tick_add=calc_tickadd(mixer.freq); TIMER_AddTickHandler(MIXER_Mix_NoSound); } else if (SDL_OpenAudio(&spec, &obtained) <0 ) { mixer.nosound = true; - LOG_MSG("MIXER:Can't open audio: %s , running in nosound mode.",SDL_GetError()); - mixer.tick_add=((mixer.freq) << MIXER_SHIFT)/1000; + LOG_MSG("MIXER: Can't open audio: %s , running in nosound mode.",SDL_GetError()); + mixer.tick_add=calc_tickadd(mixer.freq); TIMER_AddTickHandler(MIXER_Mix_NoSound); } else { if((mixer.freq != obtained.freq) || (mixer.blocksize != obtained.samples)) - LOG_MSG("MIXER:Got different values from SDL: freq %d, blocksize %d",obtained.freq,obtained.samples); + LOG_MSG("MIXER: Got different values from SDL: freq %d, blocksize %d",obtained.freq,obtained.samples); mixer.freq=obtained.freq; mixer.blocksize=obtained.samples; - mixer.tick_add=(mixer.freq << MIXER_SHIFT)/1000; + mixer.tick_add=calc_tickadd(mixer.freq); TIMER_AddTickHandler(MIXER_Mix); SDL_PauseAudio(0); } diff --git a/src/hardware/mpu401.cpp b/src/hardware/mpu401.cpp index cb24d16..4afa569 100644 --- a/src/hardware/mpu401.cpp +++ b/src/hardware/mpu401.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -30,12 +30,15 @@ bool MIDI_Available(void); static void MPU401_Event(Bitu); static void MPU401_Reset(void); -static void MPU401_EOIHandler(void); +static void MPU401_ResetDone(Bitu); +static void MPU401_EOIHandler(Bitu val=0); +static void MPU401_EOIHandlerDispatch(void); #define MPU401_VERSION 0x15 #define MPU401_REVISION 0x01 #define MPU401_QUEUE 32 #define MPU401_TIMECONSTANT (60000000/1000.0f) +#define MPU401_RESETBUSY 14.0f enum MpuMode { M_UART,M_INTELLIGENT }; enum MpuDataType {T_OVERFLOW,T_MARK,T_MIDI_SYS,T_MIDI_NORM,T_COMMAND}; @@ -72,8 +75,9 @@ static struct { bool wsd,wsm,wsd_start; bool run_irq,irq_pending; bool send_now; + bool eoi_scheduled; Bits data_onoff; - Bitu command_byte; + Bitu command_byte,cmd_pending; Bit8u tmask,cmask,amask; Bit16u midi_mask; Bit16u req_mask; @@ -112,12 +116,21 @@ static void ClrQueue(void) { static Bitu MPU401_ReadStatus(Bitu port,Bitu iolen) { Bit8u ret=0x3f; /* Bits 6 and 7 clear */ + if (mpu.state.cmd_pending) ret|=0x40; if (!mpu.queue_used) ret|=0x80; return ret; } static void MPU401_WriteCommand(Bitu port,Bitu val,Bitu iolen) { - mpu.state.reset=0; + if (mpu.mode==M_UART && val!=0xff) return; + if (mpu.state.reset) { + if (mpu.state.cmd_pending || val!=0xff) { + mpu.state.cmd_pending=val+1; + return; + } + PIC_RemoveEvents(MPU401_ResetDone); + mpu.state.reset=false; + } if (val<=0x2f) { switch (val&3) { /* MIDI stop, start, continue */ case 1: {MIDI_RawOutByte(0xfc);break;} @@ -195,7 +208,7 @@ static void MPU401_WriteCommand(Bitu port,Bitu val,Bitu iolen) { mpu.clock.timebase=192; break; /* Commands with data byte */ - case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6: + case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6: case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef: mpu.state.command_byte=val; break; @@ -239,10 +252,11 @@ static void MPU401_WriteCommand(Bitu port,Bitu val,Bitu iolen) { break; case 0xff: /* Reset MPU-401 */ LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Reset %X",val); - mpu.state.reset=1; - if (CPU_Cycles > 5) { //It came from the desert wants a fast irq - CPU_CycleLeft += CPU_Cycles; - CPU_Cycles = 5; + PIC_AddEvent(MPU401_ResetDone,MPU401_RESETBUSY); + mpu.state.reset=true; + if (mpu.mode==M_UART) { + MPU401_Reset(); + return; //do not send ack in UART mode } MPU401_Reset(); break; @@ -284,7 +298,7 @@ static Bitu MPU401_ReadData(Bitu port,Bitu iolen) { } if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK) { mpu.state.data_onoff=-1; - MPU401_EOIHandler(); + MPU401_EOIHandlerDispatch(); } return ret; } @@ -390,7 +404,7 @@ static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen) { if (val<0xf0) mpu.state.data_onoff++; else { mpu.state.data_onoff=-1; - MPU401_EOIHandler(); + MPU401_EOIHandlerDispatch(); return; } if (val==0) mpu.state.send_now=true; @@ -402,13 +416,13 @@ static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen) { if (val==0xf8 || val==0xf9) mpu.condbuf.type=T_OVERFLOW; mpu.condbuf.value[mpu.condbuf.vlength]=val; mpu.condbuf.vlength++; - if ((val&0xf0)!=0xe0) MPU401_EOIHandler(); + if ((val&0xf0)!=0xe0) MPU401_EOIHandlerDispatch(); else mpu.state.data_onoff++; break; case 2:/* Command byte #2 */ mpu.condbuf.value[mpu.condbuf.vlength]=val; mpu.condbuf.vlength++; - MPU401_EOIHandler(); + MPU401_EOIHandlerDispatch(); break; } return; @@ -420,7 +434,7 @@ static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen) { if (val<0xf0) mpu.state.data_onoff=1; else { mpu.state.data_onoff=-1; - MPU401_EOIHandler(); + MPU401_EOIHandlerDispatch(); return; } if (val==0) mpu.state.send_now=true; @@ -448,7 +462,7 @@ static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen) { mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM; length=mpu.playbuf[mpu.state.channel].length=2; break; - case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: + case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM; length=mpu.playbuf[mpu.state.channel].length=3; break; @@ -461,7 +475,7 @@ static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen) { } } if (!(posd==1 && val>=0xf0)) mpu.playbuf[mpu.state.channel].value[posd-1]=val; - if (posd==length) MPU401_EOIHandler(); + if (posd==length) MPU401_EOIHandlerDispatch(); } } @@ -520,7 +534,7 @@ static void MPU401_Event(Bitu val) { mpu.playbuf[i].counter--; if (mpu.playbuf[i].counter<=0) UpdateTrack(i); } - } + } if (mpu.state.conductor) { mpu.condbuf.counter--; if (mpu.condbuf.counter<=0) UpdateConductor(); @@ -534,13 +548,23 @@ static void MPU401_Event(Bitu val) { } if (!mpu.state.irq_pending && mpu.state.req_mask) MPU401_EOIHandler(); next_event: - PIC_RemoveEvents(MPU401_Event); Bitu new_time; if ((new_time=mpu.clock.tempo*mpu.clock.timebase)==0) return; PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/new_time); } -static void MPU401_EOIHandler(void) { + +static void MPU401_EOIHandlerDispatch(void) { + if (mpu.state.send_now) { + mpu.state.eoi_scheduled=true; + PIC_AddEvent(MPU401_EOIHandler,0.06f); //Possible a bit longer + } + else if (!mpu.state.eoi_scheduled) MPU401_EOIHandler(); +} + +//Updates counters and requests new data on "End of Input" +static void MPU401_EOIHandler(Bitu val) { + mpu.state.eoi_scheduled=false; if (mpu.state.send_now) { mpu.state.send_now=false; if (mpu.state.cond_req) UpdateConductor(); @@ -558,9 +582,18 @@ static void MPU401_EOIHandler(void) { } while ((i++)<16); } +static void MPU401_ResetDone(Bitu) { + mpu.state.reset=false; + if (mpu.state.cmd_pending) { + MPU401_WriteCommand(0x331,mpu.state.cmd_pending-1,1); + mpu.state.cmd_pending=0; + } +} static void MPU401_Reset(void) { PIC_DeActivateIRQ(mpu.irq); mpu.mode=(mpu.intelligent ? M_INTELLIGENT : M_UART); + PIC_RemoveEvents(MPU401_EOIHandler); + mpu.state.eoi_scheduled=false; mpu.state.wsd=false; mpu.state.wsm=false; mpu.state.conductor=false; @@ -572,7 +605,7 @@ static void MPU401_Reset(void) { mpu.state.cmask=0xff; mpu.state.amask=mpu.state.tmask=0; mpu.state.midi_mask=0xffff; - mpu.state.data_onoff=0; + mpu.state.data_onoff=-1; mpu.state.command_byte=0; mpu.state.block_ack=false; mpu.clock.tempo=mpu.clock.old_tempo=100; @@ -605,12 +638,12 @@ public: if (!MIDI_Available()) return; /*Enabled and there is a Midi */ installed = true; - + WriteHandler[0].Install(0x330,&MPU401_WriteData,IO_MB); WriteHandler[1].Install(0x331,&MPU401_WriteCommand,IO_MB); ReadHandler[0].Install(0x330,&MPU401_ReadData,IO_MB); ReadHandler[1].Install(0x331,&MPU401_ReadStatus,IO_MB); - + mpu.queue_used=0; mpu.queue_pos=0; mpu.mode=M_UART; diff --git a/src/hardware/opl.cpp b/src/hardware/opl.cpp index d38de5b..a8f21f7 100644 --- a/src/hardware/opl.cpp +++ b/src/hardware/opl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * OPL2/OPL3 emulation library * * This library is free software; you can redistribute it and/or @@ -26,10 +26,8 @@ #include -#ifdef HW_RVL -#include -#endif #include // rand() +#include // memset() #include "dosbox.h" #include "opl.h" diff --git a/src/hardware/opl.h b/src/hardware/opl.h index 8adcdb1..50291c5 100644 --- a/src/hardware/opl.h +++ b/src/hardware/opl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * OPL2/OPL3 emulation library * * This library is free software; you can redistribute it and/or diff --git a/src/hardware/pci_bus.cpp b/src/hardware/pci_bus.cpp index 88c402a..05e5d67 100644 --- a/src/hardware/pci_bus.cpp +++ b/src/hardware/pci_bus.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/pci_devices.h b/src/hardware/pci_devices.h index 72a73fc..86f5f2d 100644 --- a/src/hardware/pci_devices.h +++ b/src/hardware/pci_devices.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,8 +11,8 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/pcspeaker.cpp b/src/hardware/pcspeaker.cpp index 3dd6be6..9de948e 100644 --- a/src/hardware/pcspeaker.cpp +++ b/src/hardware/pcspeaker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -192,7 +192,7 @@ void PCSPEAKER_SetCounter(Bitu cntr,Bitu mode) { spkr.pit_max=(1000.0f/PIT_TICK_RATE)*cntr; break; case 3: /* Square wave generator */ - if (cntrGet_int("pcrate"); - spkr.pit_max=(1000.0f/PIT_TICK_RATE)*65535; + spkr.pit_mode=3; + spkr.pit_max=(1000.0f/PIT_TICK_RATE)*1320; spkr.pit_half=spkr.pit_max/2; spkr.pit_new_max=spkr.pit_max; spkr.pit_new_half=spkr.pit_half; diff --git a/src/hardware/pic.cpp b/src/hardware/pic.cpp index 14e36ff..9a681d5 100644 --- a/src/hardware/pic.cpp +++ b/src/hardware/pic.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,14 +11,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#include - #include "dosbox.h" #include "inout.h" #include "cpu.h" @@ -29,35 +26,144 @@ #define PIC_QUEUESIZE 512 -struct IRQ_Block { - bool masked; - bool active; - bool inservice; - Bitu vector; -}; - struct PIC_Controller { Bitu icw_words; Bitu icw_index; - Bitu masked; - bool special; bool auto_eoi; bool rotate_on_auto_eoi; bool single; bool request_issr; Bit8u vector_base; + + Bit8u irr; // request register + Bit8u imr; // mask register + Bit8u imrr; // mask register reversed (makes bit tests simpler) + Bit8u isr; // in service register + Bit8u isrr; // in service register reversed (makes bit tests simpler) + Bit8u active_irq; //currently active irq + + + void set_imr(Bit8u val); + + void check_after_EOI(){ + //Update the active_irq as an EOI is likely to change that. + update_active_irq(); + if((irr&imrr)&isrr) check_for_irq(); + } + + void update_active_irq() { + if(isr == 0) {active_irq = 8; return;} + for(Bit8u i = 0, s = 1; i < 8;i++, s<<=1){ + if( isr & s){ + active_irq = i; + return; + } + } + } + + void check_for_irq(){ + const Bit8u possible_irq = (irr&imrr)&isrr; + if (possible_irq) { + const Bit8u a_irq = special?8:active_irq; + for(Bit8u i = 0, s = 1; i < a_irq;i++, s<<=1){ + if ( possible_irq & s ) { + //There is an irq ready to be served => signal master and/or cpu + activate(); + return; + } + } + } + deactivate(); //No irq, remove signal to master and/or cpu + } + + //Signals master/cpu that there is an irq ready. + void activate(); + + //Removes signal to master/cpu that there is an irq ready. + void deactivate(); + + void raise_irq(Bit8u val){ + Bit8u bit = 1 << (val); + if((irr & bit)==0) { //value changed (as it is currently not active) + irr|=bit; + if((bit&imrr)&isrr) { //not masked and not in service + if(special || val < active_irq) activate(); + } + } + } + + void lower_irq(Bit8u val){ + Bit8u bit = 1 << ( val); + if(irr & bit) { //value will change (as it is currently active) + irr&=~bit; + if((bit&imrr)&isrr) { //not masked and not in service + //This irq might have toggled PIC_IRQCheck/caused irq 2 on master, when it was raised. + //If it is active, then recheck it, we can't just deactivate as there might be more IRQS raised. + if(special || val < active_irq) check_for_irq(); + } + } + } + + //handles all bits and logic related to starting this IRQ, it does NOT start the interrupt on the CPU. + void start_irq(Bit8u val); }; -Bitu PIC_Ticks=0; -Bitu PIC_IRQCheck; -Bitu PIC_IRQOnSecondPicActive; -Bitu PIC_IRQActive; - - -static IRQ_Block irqs[16]; static PIC_Controller pics[2]; -static bool PIC_Special_Mode = false; //Saves one compare in the pic_run_irqloop +static PIC_Controller& master = pics[0]; +static PIC_Controller& slave = pics[1]; +Bitu PIC_Ticks = 0; +Bitu PIC_IRQCheck = 0; //Maybe make it a bool and/or ensure 32bit size (x86 dynamic core seems to assume 32 bit variable size) + + +void PIC_Controller::set_imr(Bit8u val) { + if (GCC_UNLIKELY(machine==MCH_PCJR)) { + //irq 6 is a NMI on the PCJR + if (this == &master) val &= ~(1 <<(6)); + } + Bit8u change = (imr) ^ (val); //Bits that have changed become 1. + imr = val; + imrr = ~val; + + //Test if changed bits are set in irr and are not being served at the moment + //Those bits have impact on whether the cpu emulation should be paused or not. + if((irr & change)&isrr) check_for_irq(); +} + +void PIC_Controller::activate() { + //Stops CPU if master, signals master if slave + if(this == &master) { + PIC_IRQCheck = 1; + //cycles 0, take care of the port IO stuff added in raise_irq base caller. + CPU_CycleLeft += CPU_Cycles; + CPU_Cycles = 0; + //maybe when coming from a EOI, give a tiny delay. (for the cpu to pick it up) (see PIC_Activate_IRQ) + } else { + master.raise_irq(2); + } +} + +void PIC_Controller::deactivate() { + //removes irq check value if master, signals master if slave + if(this == &master) { + PIC_IRQCheck = 0; + } else { + master.lower_irq(2); + } +} + +void PIC_Controller::start_irq(Bit8u val){ + irr&=~(1<<(val)); + if (!auto_eoi) { + active_irq = val; + isr |= 1<<(val); + isrr = ~isr; + } else if (GCC_UNLIKELY(rotate_on_auto_eoi)) { + E_Exit("rotate on auto EOI not handled"); + } +} + + struct PICEntry { float index; Bitu value; @@ -73,10 +179,7 @@ static struct { static void write_command(Bitu port,Bitu val,Bitu iolen) { PIC_Controller * pic=&pics[port==0x20 ? 0 : 1]; - Bitu irq_base=port==0x20 ? 0 : 8; - Bitu i; - static Bit16u IRQ_priority_table[16] = - { 0,1,2,8,9,10,11,12,13,14,15,3,4,5,6,7 }; + if (GCC_UNLIKELY(val&0x10)) { // ICW1 issued if (val&0x04) E_Exit("PIC: 4 byte interval not handled"); if (val&0x08) E_Exit("PIC: level triggered mode not handled"); @@ -93,40 +196,24 @@ static void write_command(Bitu port,Bitu val,Bitu iolen) { if (val&0x40) { // special mask select if (val&0x20) pic->special = true; else pic->special = false; - if (pics[0].special || pics[1].special) - PIC_Special_Mode = true; else - PIC_Special_Mode = false; - if (PIC_IRQCheck) { //Recheck irqs - CPU_CycleLeft += CPU_Cycles; - CPU_Cycles = 0; - } + //Check if there are irqs ready to run, as the priority system has possibly been changed. + pic->check_for_irq(); LOG(LOG_PIC,LOG_NORMAL)("port %X : special mask %s",port,(pic->special)?"ON":"OFF"); } } else { // OCW2 issued if (val&0x20) { // EOI commands if (GCC_UNLIKELY(val&0x80)) E_Exit("rotate mode not supported"); if (val&0x40) { // specific EOI - if (PIC_IRQActive==(irq_base+val-0x60U)) { - irqs[PIC_IRQActive].inservice=false; - PIC_IRQActive=PIC_NOIRQ; - for (i=0; i<=15; i++) { - if (irqs[IRQ_priority_table[i]].inservice) { - PIC_IRQActive=IRQ_priority_table[i]; - break; - } - } - } + pic->isr &= ~(1<< ((val-0x60))); + pic->isrr = ~pic->isr; + pic->check_after_EOI(); // if (val&0x80); // perform rotation } else { // nonspecific EOI - if (PIC_IRQActive<(irq_base+8)) { - irqs[PIC_IRQActive].inservice=false; - PIC_IRQActive=PIC_NOIRQ; - for (i=0; i<=15; i++){ - if(GCC_UNLIKELY(irqs[IRQ_priority_table[i]].inservice)) { - PIC_IRQActive=IRQ_priority_table[i]; - break; - } - } + if (pic->active_irq != 8) { + //If there is no irq in service, ignore the call, some games send an eoi to both pics when a sound irq happens (regardless of the irq). + pic->isr &= ~(1 << (pic->active_irq)); + pic->isrr = ~pic->isr; + pic->check_after_EOI(); } // if (val&0x80); // perform rotation } @@ -143,43 +230,13 @@ static void write_command(Bitu port,Bitu val,Bitu iolen) { static void write_data(Bitu port,Bitu val,Bitu iolen) { PIC_Controller * pic=&pics[port==0x21 ? 0 : 1]; - Bitu irq_base=(port==0x21) ? 0 : 8; - Bitu i; - bool old_irq2_mask = irqs[2].masked; switch(pic->icw_index) { case 0: /* mask register */ - LOG(LOG_PIC,LOG_NORMAL)("%d mask %X",port==0x21 ? 0 : 1,val); - for (i=0;i<=7;i++) { - irqs[i+irq_base].masked=(val&(1<0; - if(port==0x21) { - if (irqs[i+irq_base].active && !irqs[i+irq_base].masked) PIC_IRQCheck|=(1 << (i+irq_base)); - else PIC_IRQCheck&=~(1 << (i+irq_base)); - } else { - if (irqs[i+irq_base].active && !irqs[i+irq_base].masked && !irqs[2].masked) PIC_IRQCheck|=(1 << (i+irq_base)); - else PIC_IRQCheck&=~(1 << (i+irq_base)); - } - } - if (machine==MCH_PCJR) { - /* irq6 cannot be disabled as it serves as pseudo-NMI */ - irqs[6].masked=false; - } - if(irqs[2].masked != old_irq2_mask) { - /* Irq 2 mask has changed recheck second pic */ - for(i=8;i<=15;i++) { - if (irqs[i].active && !irqs[i].masked && !irqs[2].masked) PIC_IRQCheck|=(1 << (i)); - else PIC_IRQCheck&=~(1 << (i)); - } - } - if (PIC_IRQCheck) { - CPU_CycleLeft+=CPU_Cycles; - CPU_Cycles=0; - } + pic->set_imr(val); break; case 1: /* icw2 */ LOG(LOG_PIC,LOG_NORMAL)("%d:Base vector %X",port==0x21 ? 0 : 1,val); - for (i=0;i<=7;i++) { - irqs[i+irq_base].vector=(val&0xf8)+i; - }; + pic->vector_base = val&0xf8; if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0; else if(pic->single) pic->icw_index=3; /* skip ICW3 in single mode */ break; @@ -214,82 +271,70 @@ static void write_data(Bitu port,Bitu val,Bitu iolen) { static Bitu read_command(Bitu port,Bitu iolen) { PIC_Controller * pic=&pics[port==0x20 ? 0 : 1]; - Bitu irq_base=(port==0x20) ? 0 : 8; - Bitu i;Bit8u ret=0;Bit8u b=1; - if (pic->request_issr) { - for (i=irq_base;irequest_issr){ + return pic->isr; + } else { + return pic->irr; } - return ret; } + static Bitu read_data(Bitu port,Bitu iolen) { - Bitu irq_base=(port==0x21) ? 0 : 8; - Bitu i;Bit8u ret=0;Bit8u b=1; - for (i=irq_base;i<=irq_base+7;i++) { - if (irqs[i].masked) ret|=b; - b <<= 1; - } - return ret; + PIC_Controller * pic=&pics[port==0x21 ? 0 : 1]; + return pic->imr; } - void PIC_ActivateIRQ(Bitu irq) { - if (GCC_UNLIKELY(CPU_Cycles)) { - // CPU_Cycles nonzero means the interrupt was triggered by an I/O + Bitu t = irq>7 ? (irq - 8): irq; + PIC_Controller * pic=&pics[irq>7 ? 1 : 0]; + + Bit32s OldCycles = CPU_Cycles; + pic->raise_irq(t); //Will set the CPU_Cycles to zero if this IRQ will be handled directly + + if (GCC_UNLIKELY(OldCycles != CPU_Cycles)) { + // if CPU_Cycles have changed, this means that the interrupt was triggered by an I/O // register write rather than an event. // Real hardware executes 0 to ~13 NOPs or comparable instructions // before the processor picks up the interrupt. Let's try with 2 // cycles here. // Required by Panic demo (irq0), It came from the desert (MPU401) // Does it matter if CPU_CycleLeft becomes negative? - CPU_CycleLeft += (CPU_Cycles-2); - CPU_Cycles=2; - } - if( irq < 8 ) { - irqs[irq].active = true; - if (!irqs[irq].masked) { - PIC_IRQCheck|=(1 << irq); - } - } else if (irq < 16) { - irqs[irq].active = true; - PIC_IRQOnSecondPicActive|=(1 << irq); - if (!irqs[irq].masked && !irqs[2].masked) { - PIC_IRQCheck|=(1 << irq); - } + + // It might be an idea to do this always in order to simulate this + // So on write mask and EOI as well. (so inside the activate function) +// CPU_CycleLeft += (CPU_Cycles-2); + CPU_CycleLeft -= 2; + CPU_Cycles = 2; } } void PIC_DeActivateIRQ(Bitu irq) { - if (irq<16) { - irqs[irq].active=false; - PIC_IRQCheck&=~(1 << irq); - PIC_IRQOnSecondPicActive&=~(1 << irq); - } + Bitu t = irq>7 ? (irq - 8): irq; + PIC_Controller * pic=&pics[irq>7 ? 1 : 0]; + pic->lower_irq(t); } -static inline bool PIC_startIRQ(Bitu i) { - /* irqs on second pic only if irq 2 isn't masked */ - if( i > 7 && irqs[2].masked) return false; - irqs[i].active = false; - PIC_IRQCheck&= ~(1 << i); - PIC_IRQOnSecondPicActive&= ~(1 << i); - CPU_HW_Interrupt(irqs[i].vector); - Bitu pic=(i&8)>>3; - if (!pics[pic].auto_eoi) { //irq 0-7 => pic 0 else pic 1 - PIC_IRQActive = i; - irqs[i].inservice = true; - } else if (GCC_UNLIKELY(pics[pic].rotate_on_auto_eoi)) { - E_Exit("rotate on auto EOI not handled"); + +static void slave_startIRQ(){ + Bit8u pic1_irq = 8; + const Bit8u p = (slave.irr & slave.imrr)&slave.isrr; + const Bit8u max = slave.special?8:slave.active_irq; + for(Bit8u i = 0,s = 1;i < max;i++, s<<=1){ + if (p&s){ + pic1_irq = i; + break; + } } - return true; + // Maybe change the E_Exit to a return + if (GCC_UNLIKELY(pic1_irq == 8)) E_Exit("irq 2 is active, but no irq active on the slave PIC."); + + slave.start_irq(pic1_irq); + master.start_irq(2); + CPU_HW_Interrupt(slave.vector_base + pic1_irq); +} + +static void inline master_startIRQ(Bitu i){ + master.start_irq(i); + CPU_HW_Interrupt(master.vector_base + i); } void PIC_runIRQs(void) { @@ -297,70 +342,31 @@ void PIC_runIRQs(void) { if (GCC_UNLIKELY(!PIC_IRQCheck)) return; if (GCC_UNLIKELY(cpudecoder==CPU_Core_Normal_Trap_Run)) return; - static Bitu IRQ_priority_order[16] = - { 0,1,2,8,9,10,11,12,13,14,15,3,4,5,6,7 }; - static Bit16u IRQ_priority_lookup[17] = - { 0,1,2,11,12,13,14,15,3,4,5,6,7,8,9,10,16 }; - Bit16u activeIRQ = PIC_IRQActive; - if (activeIRQ == PIC_NOIRQ) activeIRQ = 16; - /* Get the priority of the active irq */ - Bit16u Priority_Active_IRQ = IRQ_priority_lookup[activeIRQ]; - - Bitu i,j; - /* j is the priority (walker) - * i is the irq at the current priority */ - - /* If one of the pics is in special mode use a check that cares for that. */ - if(!PIC_Special_Mode) { - for (j = 0; j < Priority_Active_IRQ; j++) { - i = IRQ_priority_order[j]; - if (!irqs[i].masked && irqs[i].active) { - if(GCC_LIKELY(PIC_startIRQ(i))) return; - } - } - } else { /* Special mode variant */ - for (j = 0; j<= 15; j++) { - i = IRQ_priority_order[j]; - if ( (j < Priority_Active_IRQ) || (pics[ ((i&8)>>3) ].special) ) { - if (!irqs[i].masked && irqs[i].active) { - /* the irq line is active. it's not masked and - * the irq is allowed priority wise. So let's start it */ - /* If started successfully return, else go for the next */ - if(PIC_startIRQ(i)) return; - } + const Bit8u p = (master.irr & master.imrr)&master.isrr; + const Bit8u max = master.special?8:master.active_irq; + for(Bit8u i = 0,s = 1;i < max;i++, s<<=1){ + if (p&s){ + if (i==2) { //second pic + slave_startIRQ(); + } else { + master_startIRQ(i); } + break; } } + //Disable check variable. + PIC_IRQCheck = 0; } void PIC_SetIRQMask(Bitu irq, bool masked) { - if(irqs[irq].masked == masked) return; /* Do nothing if mask doesn't change */ - bool old_irq2_mask = irqs[2].masked; - irqs[irq].masked=masked; - if(irq < 8) { - if (irqs[irq].active && !irqs[irq].masked) { - PIC_IRQCheck|=(1 << (irq)); - } else { - PIC_IRQCheck&=~(1 << (irq)); - } - } else { - if (irqs[irq].active && !irqs[irq].masked && !irqs[2].masked) { - PIC_IRQCheck|=(1 << (irq)); - } else { - PIC_IRQCheck&=~(1 << (irq)); - } - } - if(irqs[2].masked != old_irq2_mask) { - /* Irq 2 mask has changed recheck second pic */ - for(Bitu i=8;i<=15;i++) { - if (irqs[i].active && !irqs[i].masked && !irqs[2].masked) PIC_IRQCheck|=(1 << (i)); - else PIC_IRQCheck&=~(1 << (i)); - } - } - if (PIC_IRQCheck) { - CPU_CycleLeft+=CPU_Cycles; - CPU_Cycles=0; - } + Bitu t = irq>7 ? (irq - 8): irq; + PIC_Controller * pic=&pics[irq>7 ? 1 : 0]; + //clear bit + Bit8u bit = 1 <<(t); + Bit8u newmask = pic->imr; + newmask &= ~bit; + if (masked) newmask |= bit; + pic->set_imr(newmask); } static void AddEntry(PICEntry * entry) { @@ -463,7 +469,7 @@ void PIC_RemoveEvents(PIC_EventHandler handler) { bool PIC_RunQueue(void) { - /* Check to see if a new milisecond needs to be started */ + /* Check to see if a new millisecond needs to be started */ CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=0; if (CPU_CycleLeft<=0) { @@ -559,11 +565,9 @@ public: PIC_8259A(Section* configuration):Module_base(configuration){ /* Setup pic0 and pic1 with initial values like DOS has normally */ PIC_IRQCheck=0; - PIC_IRQActive=PIC_NOIRQ; PIC_Ticks=0; Bitu i; for (i=0;i<2;i++) { - pics[i].masked=0xff; pics[i].auto_eoi=false; pics[i].rotate_on_auto_eoi=false; pics[i].request_issr=false; @@ -571,24 +575,21 @@ public: pics[i].single=false; pics[i].icw_index=0; pics[i].icw_words=0; + pics[i].irr = pics[i].isr = pics[i].imrr = 0; + pics[i].isrr = pics[i].imr = 0xff; + pics[i].active_irq = 8; } - for (i=0;i<=7;i++) { - irqs[i].active=false; - irqs[i].masked=true; - irqs[i].inservice=false; - irqs[i+8].active=false; - irqs[i+8].masked=true; - irqs[i+8].inservice=false; - irqs[i].vector=0x8+i; - irqs[i+8].vector=0x70+i; - } - irqs[0].masked=false; /* Enable system timer */ - irqs[1].masked=false; /* Enable Keyboard IRQ */ - irqs[2].masked=false; /* Enable second pic */ - irqs[8].masked=false; /* Enable RTC IRQ */ + master.vector_base = 0x08; + slave.vector_base = 0x70; + + PIC_SetIRQMask(0,false); /* Enable system timer */ + PIC_SetIRQMask(1,false); /* Enable system timer */ + PIC_SetIRQMask(2,false); /* Enable second pic */ + PIC_SetIRQMask(8,false); /* Enable RTC IRQ */ + if (machine==MCH_PCJR) { /* Enable IRQ6 (replacement for the NMI for PCJr) */ - irqs[6].masked=false; + PIC_SetIRQMask(6,false); } ReadHandler[0].Install(0x20,read_command,IO_MB); ReadHandler[1].Install(0x21,read_data,IO_MB); @@ -606,6 +607,7 @@ public: pic_queue.free_entry=&pic_queue.entries[0]; pic_queue.next_entry=0; } + ~PIC_8259A(){ } }; diff --git a/src/hardware/sblaster.cpp b/src/hardware/sblaster.cpp index 60f6e67..78f52b7 100644 --- a/src/hardware/sblaster.cpp +++ b/src/hardware/sblaster.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -73,7 +73,7 @@ enum DSP_MODES { MODE_DMA_MASKED }; - + enum DMA_MODES { DSP_DMA_NONE, DSP_DMA_2,DSP_DMA_3,DSP_DMA_4,DSP_DMA_8, @@ -90,7 +90,10 @@ struct SB_INFO { bool stereo,sign,autoinit; DMA_MODES mode; Bitu rate,mul; - Bitu total,left,min; + Bit32u singlesize; //size for single cycle transfers + Bit32u autosize; //size for auto init transfers + Bitu left; //Left in active cycle + Bitu min; Bit64u start; union { Bit8u b8[DMA_BUFSIZE]; @@ -273,12 +276,28 @@ static INLINE void DSP_FlushData(void) { sb.dsp.out.pos=0; } +static double last_dma_callback = 0.0f; + static void DSP_DMA_CallBack(DmaChannel * chan, DMAEvent event) { - if (event==DMA_REACHED_TC) return; + if (chan!=sb.dma.chan || event==DMA_REACHED_TC) return; else if (event==DMA_MASKED) { if (sb.mode==MODE_DMA) { - GenerateDMASound(sb.dma.min); - sb.mode=MODE_DMA_MASKED; + //Catch up to current time, but don't generate an IRQ! + //Fixes problems with later sci games. + double t = PIC_FullIndex() - last_dma_callback; + Bitu s = static_cast(sb.dma.rate * t / 1000.0f); + if (s > sb.dma.min) { + LOG(LOG_SB,LOG_NORMAL)("limiting amount masked to sb.dma.min"); + s = sb.dma.min; + } + Bitu min_size = sb.dma.mul >> SB_SH; + if (!min_size) min_size = 1; + min_size *= 2; + if (sb.dma.left > min_size) { + if (s > (sb.dma.left-min_size)) s = sb.dma.left - min_size; + if (s) GenerateDMASound(s); + } + sb.mode = MODE_DMA_MASKED; // DSP_ChangeMode(MODE_DMA_MASKED); LOG(LOG_SB,LOG_NORMAL)("DMA masked,stopping output, left %d",chan->currcnt); } @@ -290,6 +309,9 @@ static void DSP_DMA_CallBack(DmaChannel * chan, DMAEvent event) { LOG(LOG_SB,LOG_NORMAL)("DMA unmasked,starting output, auto %d block %d",chan->autoinit,chan->basecnt); } } + else { + E_Exit("Unknown sblaster dma event"); + } } #define MIN_ADAPTIVE_STEP_SIZE 0 @@ -394,11 +416,18 @@ INLINE Bit8u decode_ADPCM_3_sample(Bit8u sample,Bit8u & reference,Bits& scale) { static void GenerateDMASound(Bitu size) { Bitu read=0;Bitu done=0;Bitu i=0; + last_dma_callback = PIC_FullIndex(); + //Determine how much you should read if(sb.dma.autoinit) { - if (sb.dma.left <= size) size = sb.dma.left; - } else if (sb.dma.left <= sb.dma.min) size = sb.dma.left; + if (sb.dma.left <= size) + size = sb.dma.left; + } else { + if (sb.dma.left <= sb.dma.min) + size = sb.dma.left; + } + //Read the actual data, process it and send it off to the mixer switch (sb.dma.mode) { case DSP_DMA_2: read=sb.dma.chan->Read(size,sb.dma.buf.b8); @@ -500,21 +529,36 @@ static void GenerateDMASound(Bitu size) { sb.mode=MODE_NONE; return; } + //Check how many bytes were actually read sb.dma.left-=read; if (!sb.dma.left) { PIC_RemoveEvents(END_DMA_Event); - if (sb.dma.mode >= DSP_DMA_16) SB_RaiseIRQ(SB_IRQ_16); - else SB_RaiseIRQ(SB_IRQ_8); + if (sb.dma.mode >= DSP_DMA_16) + SB_RaiseIRQ(SB_IRQ_16); + else + SB_RaiseIRQ(SB_IRQ_8); + if (!sb.dma.autoinit) { - LOG(LOG_SB,LOG_NORMAL)("Single cycle transfer ended"); - sb.mode=MODE_NONE; - sb.dma.mode=DSP_DMA_NONE; + //Not new single cycle transfer waiting? + if (!sb.dma.singlesize) { + LOG(LOG_SB, LOG_NORMAL)("Single cycle transfer ended"); + sb.mode = MODE_NONE; + sb.dma.mode = DSP_DMA_NONE; + } + else { + //A single size transfer is still waiting, handle that now + sb.dma.left = sb.dma.singlesize; + sb.dma.singlesize = 0; + LOG(LOG_SB, LOG_NORMAL)("Switch to Single cycle transfer begun"); + } } else { - sb.dma.left=sb.dma.total; - if (!sb.dma.left) { + if (!sb.dma.autosize) { LOG(LOG_SB,LOG_NORMAL)("Auto-init transfer with 0 size"); sb.mode=MODE_NONE; } + //Continue with a new auto init transfer + sb.dma.left = sb.dma.autosize; + } } } @@ -544,7 +588,9 @@ static void DMA_Silent_Event(Bitu val) { if (!sb.dma.left) { if (sb.dma.mode >= DSP_DMA_16) SB_RaiseIRQ(SB_IRQ_16); else SB_RaiseIRQ(SB_IRQ_8); - if (sb.dma.autoinit) sb.dma.left=sb.dma.total; + //FIX, use the auto to single switch mechanics here as well or find a better way to silence + if (sb.dma.autoinit) + sb.dma.left=sb.dma.autosize; else { sb.mode=MODE_NONE; sb.dma.mode=DSP_DMA_NONE; @@ -555,7 +601,6 @@ static void DMA_Silent_Event(Bitu val) { float delay=(bigger*1000.0f)/sb.dma.rate; PIC_AddEvent(DMA_Silent_Event,delay,bigger); } - } static void END_DMA_Event(Bitu val) { @@ -586,15 +631,16 @@ static void DSP_RaiseIRQEvent(Bitu /*val*/) { SB_RaiseIRQ(SB_IRQ_8); } -static void DSP_DoDMATransfer(DMA_MODES mode,Bitu freq,bool stereo) { +static void DSP_DoDMATransfer(DMA_MODES mode,Bitu freq,bool autoinit, bool stereo) { char const * type; - sb.mode=MODE_DMA_MASKED; + //Fill up before changing state? sb.chan->FillUp(); - sb.dma.left=sb.dma.total; - sb.dma.mode=mode; - sb.dma.stereo=stereo; - sb.irq.pending_8bit=false; - sb.irq.pending_16bit=false; + + //Starting a new transfer will clear any active irqs? + sb.irq.pending_8bit = false; + sb.irq.pending_16bit = false; + PIC_DeActivateIRQ(sb.hw.irq); + switch (mode) { case DSP_DMA_2: type="2-bits ADPCM"; @@ -624,43 +670,67 @@ static void DSP_DoDMATransfer(DMA_MODES mode,Bitu freq,bool stereo) { LOG(LOG_SB,LOG_ERROR)("DSP:Illegal transfer mode %d",mode); return; } - if (sb.dma.stereo) sb.dma.mul*=2; + + //Going from an active autoinit into a single cycle + if (sb.mode >= MODE_DMA && sb.dma.autoinit && !autoinit) { + //Don't do anything, the total will flip over on the next transfer + } + //Just a normal single cycle transfer + else if (!autoinit) { + sb.dma.left = sb.dma.singlesize; + sb.dma.singlesize = 0; + } + //Going into an autoinit transfer + else { + //Transfer full cycle again + sb.dma.left = sb.dma.autosize; + } + sb.dma.autoinit = autoinit; + sb.dma.mode = mode; + sb.dma.stereo = stereo; + //Double the reading speed for stereo mode + if (sb.dma.stereo) + sb.dma.mul*=2; sb.dma.rate=(sb.freq*sb.dma.mul) >> SB_SH; sb.dma.min=(sb.dma.rate*3)/1000; sb.chan->SetFreq(freq); - sb.dma.mode=mode; + PIC_RemoveEvents(END_DMA_Event); + //Set to be masked, the dma call can change this again. + sb.mode = MODE_DMA_MASKED; sb.dma.chan->Register_Callback(DSP_DMA_CallBack); + #if (C_DEBUG) - LOG(LOG_SB,LOG_NORMAL)("DMA Transfer:%s %s %s freq %d rate %d size %d", + LOG(LOG_SB, LOG_NORMAL)("DMA Transfer:%s %s %s freq %d rate %d size %d", type, - sb.dma.stereo ? "Stereo" : "Mono", - sb.dma.autoinit ? "Auto-Init" : "Single-Cycle", - freq,sb.dma.rate,sb.dma.total - ); + stereo ? "Stereo" : "Mono", + autoinit ? "Auto-Init" : "Single-Cycle", + freq, sb.dma.rate, sb.dma.left + ); +#else + type = *&type; #endif } static void DSP_PrepareDMA_Old(DMA_MODES mode,bool autoinit,bool sign) { - sb.dma.autoinit=autoinit; sb.dma.sign=sign; - if (!autoinit) sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); + if (!autoinit) + sb.dma.singlesize=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); sb.dma.chan=GetDMAChannel(sb.hw.dma8); - DSP_DoDMATransfer(mode,sb.freq / (sb.mixer.stereo ? 2 : 1),sb.mixer.stereo); + DSP_DoDMATransfer(mode,sb.freq / (sb.mixer.stereo ? 2 : 1), autoinit, sb.mixer.stereo); } static void DSP_PrepareDMA_New(DMA_MODES mode,Bitu length,bool autoinit,bool stereo) { Bitu freq=sb.freq; + //equal length if data format and dma channel are both 16-bit or 8-bit - sb.dma.total=length; - sb.dma.autoinit=autoinit; if (mode==DSP_DMA_16) { if (sb.hw.dma16!=0xff) { sb.dma.chan=GetDMAChannel(sb.hw.dma16); if (sb.dma.chan==NULL) { sb.dma.chan=GetDMAChannel(sb.hw.dma8); mode=DSP_DMA_16_ALIASED; - sb.dma.total<<=1; + length *= 2; } } else { sb.dma.chan=GetDMAChannel(sb.hw.dma8); @@ -668,10 +738,17 @@ static void DSP_PrepareDMA_New(DMA_MODES mode,Bitu length,bool autoinit,bool ste //UNDOCUMENTED: //In aliased mode sample length is written to DSP as number of //16-bit samples so we need double 8-bit DMA buffer length - sb.dma.total<<=1; + length *= 2; } } else sb.dma.chan=GetDMAChannel(sb.hw.dma8); - DSP_DoDMATransfer(mode,freq,stereo); + //Set the length to the correct register depending on mode + if (autoinit) { + sb.dma.autosize = length; + } + else { + sb.dma.singlesize = length; + } + DSP_DoDMATransfer(mode,freq,autoinit,stereo); } @@ -699,13 +776,15 @@ static void DSP_Reset(void) { DSP_ChangeMode(MODE_NONE); DSP_FlushData(); + sb.dsp.cmd=DSP_NO_COMMAND; sb.dsp.cmd_len=0; sb.dsp.in.pos=0; sb.dsp.write_busy=0; PIC_RemoveEvents(DSP_FinishReset); sb.dma.left=0; - sb.dma.total=0; + sb.dma.singlesize=0; + sb.dma.autosize = 0; sb.dma.stereo=false; sb.dma.sign=false; sb.dma.autoinit=false; @@ -758,6 +837,16 @@ static void DSP_ADC_CallBack(DmaChannel * /*chan*/, DMAEvent event) { ch->Register_Callback(0); } +static void DSP_ChangeRate(Bitu freq) { + if (sb.freq!=freq && sb.dma.mode!=DSP_DMA_NONE) { + sb.chan->FillUp(); + sb.chan->SetFreq(freq / (sb.mixer.stereo ? 2 : 1)); + sb.dma.rate=(freq*sb.dma.mul) >> SB_SH; + sb.dma.min=(sb.dma.rate*3)/1000; + } + sb.freq=freq; +} + Bitu DEBUG_EnableDebugger(void); #define DSP_SB16_ONLY if (sb.type != SBT_16) { LOG(LOG_SB,LOG_ERROR)("DSP:Command %2X requires SB16",sb.dsp.cmd); break; } @@ -825,9 +914,10 @@ static void DSP_DoCommand(void) { } break; case 0x24: /* Singe Cycle 8-Bit DMA ADC */ - sb.dma.left=sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); + //Directly write to left? + sb.dma.left = 1 + sb.dsp.in.data[0] + (sb.dsp.in.data[1] << 8); sb.dma.sign=false; - LOG(LOG_SB,LOG_ERROR)("DSP:Faked ADC for %d bytes",sb.dma.total); + LOG(LOG_SB,LOG_ERROR)("DSP:Faked ADC for %d bytes",sb.dma.left); GetDMAChannel(sb.hw.dma8)->Register_Callback(DSP_ADC_CallBack); break; case 0x14: /* Singe Cycle 8-Bit DMA DAC */ @@ -845,21 +935,17 @@ static void DSP_DoCommand(void) { if (sb.midi == true) MIDI_RawOutByte(sb.dsp.in.data[0]); break; case 0x40: /* Set Timeconstant */ - sb.freq=(1000000 / (256 - sb.dsp.in.data[0])); - /* Nasty kind of hack to allow runtime changing of frequency */ - if (sb.dma.mode != DSP_DMA_NONE && sb.dma.autoinit) { - DSP_PrepareDMA_Old(sb.dma.mode,sb.dma.autoinit,sb.dma.sign); - } + DSP_ChangeRate(1000000 / (256 - sb.dsp.in.data[0])); break; case 0x41: /* Set Output Samplerate */ case 0x42: /* Set Input Samplerate */ + /* Note: 0x42 is handled like 0x41, needed by Fasttracker II */ DSP_SB16_ONLY; - sb.freq=(sb.dsp.in.data[0] << 8) | sb.dsp.in.data[1]; + DSP_ChangeRate((sb.dsp.in.data[0] << 8) | sb.dsp.in.data[1]); break; case 0x48: /* Set DMA Block Size */ DSP_SB2_ABOVE; - //TODO Maybe check limit for new irq? - sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); + sb.dma.autosize=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); break; case 0x75: /* 075h : Single Cycle 4-bit ADPCM Reference */ sb.adpcm.haveref=true; @@ -903,6 +989,7 @@ static void DSP_DoCommand(void) { DSP_SB16_ONLY; case 0xd0: /* Halt 8-bit DMA */ // DSP_ChangeMode(MODE_NONE); + LOG(LOG_SB, LOG_NORMAL)("Halt DMA Command"); // Games sometimes already program a new dma before stopping, gives noise if (sb.mode==MODE_NONE) { // possibly different code here that does not switch to MODE_DMA_PAUSE @@ -925,6 +1012,7 @@ static void DSP_DoCommand(void) { case 0xd6: /* Continue DMA 16-bit */ DSP_SB16_ONLY; case 0xd4: /* Continue DMA 8-bit*/ + LOG(LOG_SB, LOG_NORMAL)("Continue DMA command"); if (sb.mode==MODE_DMA_PAUSE) { sb.mode=MODE_DMA_MASKED; if (sb.dma.chan!=NULL) sb.dma.chan->Register_Callback(DSP_DMA_CallBack); @@ -934,7 +1022,7 @@ static void DSP_DoCommand(void) { DSP_SB16_ONLY; case 0xda: /* Exit Autoinitialize 8-bit */ DSP_SB2_ABOVE; - /* Set mode to single transfer so it ends with current block */ + LOG(LOG_SB, LOG_NORMAL)("Exit Autoinit command"); sb.dma.autoinit=false; //Should stop itself break; case 0xe0: /* DSP Identification - SB2.0+ */ @@ -984,11 +1072,14 @@ static void DSP_DoCommand(void) { DSP_AddData(sb.dsp.test_register);; break; case 0xf2: /* Trigger 8bit IRQ */ - SB_RaiseIRQ(SB_IRQ_8); + //Small delay in order to emulate the slowness of the DSP, fixes Llamatron 2012 and Lemmings 3D + PIC_AddEvent(&DSP_RaiseIRQEvent,0.01f); + LOG(LOG_SB, LOG_NORMAL)("Trigger 8bit IRQ command"); break; case 0xf3: /* Trigger 16bit IRQ */ DSP_SB16_ONLY; SB_RaiseIRQ(SB_IRQ_16); + LOG(LOG_SB, LOG_NORMAL)("Trigger 16bit IRQ command"); break; case 0xf8: /* Undocumented, pre-SB16 only */ DSP_FlushData(); @@ -1089,21 +1180,33 @@ static Bit8u DSP_ReadData(void) { return sb.dsp.out.lastval; } -//The soundblaster manual says 2.0 Db steps but we'll go for a bit less -#define CALCVOL(_VAL) (float)pow(10.0f,((float)(31-_VAL)*-1.3f)/20) +static float calc_vol(Bit8u amount) { + Bit8u count = 31 - amount; + float db = static_cast(count); + if (sb.type == SBT_PRO1 || sb.type == SBT_PRO2) { + if (count) { + if (count < 16) db -= 1.0f; + else if (count > 16) db += 1.0f; + if (count == 24) db += 2.0f; + if (count > 27) return 0.0f; //turn it off. + } + } else { //Give the rest, the SB16 scale, as we don't have data. + db *= 2.0f; + if (count > 20) db -= 1.0f; + } + return (float) pow (10.0f,-0.05f * db); +} static void CTMIXER_UpdateVolumes(void) { if (!sb.mixer.enabled) return; MixerChannel * chan; - //adjust to get linear master volume slider in trackers - chan=MIXER_FindChannel("SB"); - if (chan) chan->SetVolume(float(sb.mixer.master[0])/31.0f*CALCVOL(sb.mixer.dac[0]), - float(sb.mixer.master[1])/31.0f*CALCVOL(sb.mixer.dac[1])); - chan=MIXER_FindChannel("FM"); - if (chan) chan->SetVolume(float(sb.mixer.master[0])/31.0f*CALCVOL(sb.mixer.fm[0]), - float(sb.mixer.master[1])/31.0f*CALCVOL(sb.mixer.fm[1])); - chan=MIXER_FindChannel("CDAUDIO"); - if (chan) chan->SetVolume(float(sb.mixer.master[0])/31.0f*CALCVOL(sb.mixer.cda[0]), - float(sb.mixer.master[1])/31.0f*CALCVOL(sb.mixer.cda[1])); + float m0 = calc_vol(sb.mixer.master[0]); + float m1 = calc_vol(sb.mixer.master[1]); + chan = MIXER_FindChannel("SB"); + if (chan) chan->SetVolume(m0 * calc_vol(sb.mixer.dac[0]), m1 * calc_vol(sb.mixer.dac[1])); + chan = MIXER_FindChannel("FM"); + if (chan) chan->SetVolume(m0 * calc_vol(sb.mixer.fm[0]) , m1 * calc_vol(sb.mixer.fm[1]) ); + chan = MIXER_FindChannel("CDAUDIO"); + if (chan) chan->SetVolume(m0 * calc_vol(sb.mixer.cda[0]), m1 * calc_vol(sb.mixer.cda[1])); } static void CTMIXER_Reset(void) { @@ -1122,8 +1225,9 @@ static void CTMIXER_Reset(void) { _WHICH_[0]= ((((_VAL_) & 0xf0) >> 3)|(sb.type==SBT_16 ? 1:3)); \ _WHICH_[1]= ((((_VAL_) & 0x0f) << 1)|(sb.type==SBT_16 ? 1:3)); \ -#define MAKEPROVOL(_WHICH_) \ - ((((_WHICH_[0] & 0x1e) << 3) | ((_WHICH_[1] & 0x1e) >> 1)) & (sb.type==SBT_16 ? 0xff:0xee)) +#define MAKEPROVOL(_WHICH_) \ + ((((_WHICH_[0] & 0x1e) << 3) | ((_WHICH_[1] & 0x1e) >> 1)) | \ + ((sb.type==SBT_PRO1 || sb.type==SBT_PRO2) ? 0x11:0)) static void DSP_ChangeStereo(bool stereo) { if (!sb.dma.stereo && stereo) { @@ -1523,6 +1627,7 @@ private: else if (!strcasecmp(omode,"opl2")) opl_mode=OPL_opl2; else if (!strcasecmp(omode,"dualopl2")) opl_mode=OPL_dualopl2; else if (!strcasecmp(omode,"opl3")) opl_mode=OPL_opl3; + else if (!strcasecmp(omode,"opl3gold")) opl_mode=OPL_opl3gold; /* Else assume auto */ else { switch (type) { @@ -1578,6 +1683,7 @@ public: // fall-through case OPL_dualopl2: case OPL_opl3: + case OPL_opl3gold: OPL_Init(section,oplmode); break; } @@ -1635,6 +1741,7 @@ public: // fall-through case OPL_dualopl2: case OPL_opl3: + case OPL_opl3gold: OPL_ShutDown(m_configuration); break; } diff --git a/src/hardware/serialport/directserial.cpp b/src/hardware/serialport/directserial.cpp index 4504d58..c6ef6e4 100644 --- a/src/hardware/serialport/directserial.cpp +++ b/src/hardware/serialport/directserial.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/serialport/directserial.h b/src/hardware/serialport/directserial.h index 05370ea..8c25235 100644 --- a/src/hardware/serialport/directserial.h +++ b/src/hardware/serialport/directserial.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/serialport/libserial.cpp b/src/hardware/serialport/libserial.cpp index c780e9d..b7f524f 100644 --- a/src/hardware/serialport/libserial.cpp +++ b/src/hardware/serialport/libserial.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -41,7 +41,7 @@ bool SERIAL_open(const char* portname, COMPORT* port) { // open the port in NT object space (recommended by Microsoft) // allows the user to open COM10+ and custom port names. - int len = strlen(portname); + size_t len = strlen(portname); if(len > 240) { SetLastError(ERROR_BUFFER_OVERFLOW); free(cp); @@ -125,7 +125,7 @@ void SERIAL_close(COMPORT port) { free(port); } -void SERIAL_getErrorString(char* buffer, int length) { +void SERIAL_getErrorString(char* buffer, size_t length) { int error = GetLastError(); if(length < 50) return; memset(buffer,0,length); @@ -141,7 +141,7 @@ void SERIAL_getErrorString(char* buffer, int length) { const char* err5text = "The specified port is already in use.\n"; const char* err2text = "The specified port does not exist.\n"; - int sysmsg_offset = 0; + size_t sysmsg_offset = 0; if(error == 5) { sysmsg_offset = strlen(err5text); @@ -151,10 +151,13 @@ void SERIAL_getErrorString(char* buffer, int length) { sysmsg_offset = strlen(err2text); memcpy(buffer,err2text,sysmsg_offset); } - - if((length - sysmsg_offset - strlen((const char*)sysmessagebuffer)) >= 0) + + // Go for length > so there will be bytes left afterwards. + // (which are 0 due to memset, thus the buffer is 0 terminated + if ( length > (sysmsg_offset + strlen((const char*)sysmessagebuffer)) ) { memcpy(buffer + sysmsg_offset, sysmessagebuffer, - strlen((const char*)sysmessagebuffer)); + strlen((const char*)sysmessagebuffer)); + } LocalFree(sysmessagebuffer); } @@ -284,8 +287,9 @@ bool SERIAL_open(const char* portname, COMPORT* port) { cp->breakstatus=false; - int len = strlen(portname); + size_t len = strlen(portname); if(len > 240) { + free(cp); ///////////////////////////////////SetLastError(ERROR_BUFFER_OVERFLOW); return false; } @@ -296,7 +300,7 @@ bool SERIAL_open(const char* portname, COMPORT* port) { if (cp->porthandle < 0) goto cleanup_error; result = tcgetattr(cp->porthandle,&cp->backup); - if (result==-1) goto cleanup_error; + if (result == -1) goto cleanup_error; // get port settings termios termInfo; @@ -331,7 +335,7 @@ void SERIAL_close(COMPORT port) { free(port); } -void SERIAL_getErrorString(char* buffer, int length) { +void SERIAL_getErrorString(char* buffer, size_t length) { int error = errno; if(length < 50) return; memset(buffer,0,length); @@ -341,7 +345,7 @@ void SERIAL_getErrorString(char* buffer, int length) { const char* err5text = "The specified port is already in use.\n"; const char* err2text = "The specified port does not exist.\n"; - int sysmsg_offset = 0; + size_t sysmsg_offset = 0; if(error == EBUSY) { sysmsg_offset = strlen(err5text); @@ -482,11 +486,13 @@ void SERIAL_setRTS(COMPORT port, bool value) { #define INCL_DOSDEVIOCTL #define INCL_DOSPROCESS #include +#include +#include +#include struct _COMPORT { HFILE porthandle; - bool breakstatus; - DCBINFO backup; + DCBINFO orig_dcb; }; // TODO: THIS IS INCOMPLETE and UNTESTED. @@ -495,18 +501,18 @@ bool SERIAL_open(const char* portname, COMPORT* port) { COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT)); if(cp == NULL) return false; cp->porthandle=0; - cp->breakstatus=false; + USHORT errors = 0; ULONG ulAction = 0; - APIRET rc = DosOpen(portname, &cp->porthandle, + ULONG ulParmLen = sizeof(DCBINFO); + APIRET rc = DosOpen((PSZ)portname, &cp->porthandle, &ulAction, 0L, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | OPEN_FLAGS_SEQUENTIAL, 0L); if (rc != NO_ERROR) { goto cleanup_error; } - ULONG ulParmLen = sizeof(DCBINFO); - rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, + rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_GETDCBINFO, 0, 0, 0, &cp->orig_dcb, ulParmLen, &ulParmLen); if ( rc != NO_ERROR) { goto cleanup_error; @@ -517,18 +523,17 @@ bool SERIAL_open(const char* portname, COMPORT* port) { newdcb.usWriteTimeout = 0; newdcb.usReadTimeout = 0; //65535; - newdcb.fbCtlHndShake = dcb.fbFlowReplace = 0; + newdcb.fbCtlHndShake = cp->orig_dcb.fbFlowReplace = 0; newdcb.fbTimeout = 6; - rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO, + rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_SETDCBINFO, &newdcb, ulParmLen, &ulParmLen, 0, 0, 0); if ( rc != NO_ERROR) { goto cleanup_error; } - USHORT errors = 0; ulParmLen = sizeof(errors); - rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETCOMMERROR, + rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMERROR, 0, 0, 0, &errors, ulParmLen, &ulParmLen); if ( rc != NO_ERROR) { goto cleanup_error; @@ -539,12 +544,12 @@ bool SERIAL_open(const char* portname, COMPORT* port) { cleanup_error: // TODO error string - rc value - if (cp->porthandle != 0) CloseHandle(cp->porthandle); + if (cp->porthandle != 0) DosClose(cp->porthandle); free(cp); return false; } -void SERIAL_getErrorString(char* buffer, int length) { +void SERIAL_getErrorString(char* buffer, size_t length) { sprintf(buffer, "TODO: error handling is not fun"); } void SERIAL_close(COMPORT port) { @@ -553,14 +558,12 @@ void SERIAL_close(COMPORT port) { if (port->porthandle != 0) { DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETDCBINFO, &port->orig_dcb, ulParmLen, &ulParmLen, 0, 0, 0); - SetCmmState(port->porthandle, &port->orig_dcb); DosClose (port->porthandle); } free(port); } bool SERIAL_sendchar(COMPORT port, char data) { ULONG bytesWritten = 0; - if(port->breakstatus) return true; // does OS/2 need this? APIRET rc = DosWrite(port->porthandle, &data, 1, &bytesWritten); if (rc == NO_ERROR && bytesWritten > 0) return true; @@ -571,7 +574,7 @@ void SERIAL_setBREAK(COMPORT port, bool value) { USHORT error; ULONG ulParmLen = sizeof(error); DosDevIOCtl(port->porthandle, IOCTL_ASYNC, - value? ASYNC_SETBREAKON:ASYNC_SETBREAKOFF, + value ? ASYNC_SETBREAKON : ASYNC_SETBREAKOFF, 0,0,0, &error, ulParmLen, &ulParmLen); } @@ -656,7 +659,7 @@ bool SERIAL_setCommParameters(COMPORT port, setbaud.baud = baudrate; setbaud.fraction = 0; ULONG ulParmLen = sizeof(setbaud); - APIRET rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE, + APIRET rc = DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE, &setbaud, ulParmLen, &ulParmLen, 0, 0, 0); if (rc != NO_ERROR) { return false; @@ -697,7 +700,7 @@ bool SERIAL_setCommParameters(COMPORT port, } // set it ulParmLen = sizeof(paramline); - rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETLINECTRL, + rc = DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETLINECTRL, ¶mline, ulParmLen, &ulParmLen, 0, 0, 0); if ( rc != NO_ERROR) return false; diff --git a/src/hardware/serialport/libserial.h b/src/hardware/serialport/libserial.h index 05a674a..e3e5a29 100644 --- a/src/hardware/serialport/libserial.h +++ b/src/hardware/serialport/libserial.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,17 +11,18 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include typedef struct _COMPORT *COMPORT; bool SERIAL_open(const char* portname, COMPORT* port); void SERIAL_close(COMPORT port); -void SERIAL_getErrorString(char* buffer, int length); +void SERIAL_getErrorString(char* buffer, size_t length); #define SERIAL_1STOP 1 #define SERIAL_2STOP 2 diff --git a/src/hardware/serialport/misc_util.cpp b/src/hardware/serialport/misc_util.cpp index 769da4d..b0eed83 100644 --- a/src/hardware/serialport/misc_util.cpp +++ b/src/hardware/serialport/misc_util.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* $Id $ */ @@ -64,7 +64,11 @@ TCPClientSocket::TCPClientSocket(int platformsocket) { ((struct _TCPsocketX*)nativetcpstruct)->sflag=0; ((struct _TCPsocketX*)nativetcpstruct)->channel=(SOCKET) platformsocket; sockaddr_in sa; +#ifdef OS2 + int sz; +#else socklen_t sz; +#endif sz=sizeof(sa); if(getpeername(platformsocket, (sockaddr *)(&sa), &sz)==0) { ((struct _TCPsocketX*)nativetcpstruct)-> diff --git a/src/hardware/serialport/misc_util.h b/src/hardware/serialport/misc_util.h index a7e805a..a15c693 100644 --- a/src/hardware/serialport/misc_util.h +++ b/src/hardware/serialport/misc_util.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/serialport/nullmodem.cpp b/src/hardware/serialport/nullmodem.cpp index 4f91f9d..95fc232 100644 --- a/src/hardware/serialport/nullmodem.cpp +++ b/src/hardware/serialport/nullmodem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -148,7 +148,7 @@ CNullModem::CNullModem(Bitu id, CommandLine* cmd):CSerial (id, cmd) { setCTS(dtrrespect||transparent); setDSR(dtrrespect||transparent); setRI(false); - setCD(clientsocket > 0); // CD on if connection established + setCD(clientsocket != 0); // CD on if connection established } CNullModem::~CNullModem() { diff --git a/src/hardware/serialport/nullmodem.h b/src/hardware/serialport/nullmodem.h index 5bf7df4..15859e5 100644 --- a/src/hardware/serialport/nullmodem.h +++ b/src/hardware/serialport/nullmodem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/serialport/serialdummy.cpp b/src/hardware/serialport/serialdummy.cpp index eea35ca..a3aaf9e 100644 --- a/src/hardware/serialport/serialdummy.cpp +++ b/src/hardware/serialport/serialdummy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/serialport/serialdummy.h b/src/hardware/serialport/serialdummy.h index 90a950b..9df7c2c 100644 --- a/src/hardware/serialport/serialdummy.h +++ b/src/hardware/serialport/serialdummy.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/serialport/serialport.cpp b/src/hardware/serialport/serialport.cpp index 884038b..7f86279 100644 --- a/src/hardware/serialport/serialport.cpp +++ b/src/hardware/serialport/serialport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -719,82 +719,86 @@ Bitu CSerial::Read_MCR () { void CSerial::Write_MCR (Bit8u data) { // WARNING: At the time setRTSDTR is called rts and dsr members are still wrong. - if(data&FIFO_FLOWCONTROL) LOG_MSG("Warning: tried to activate hardware handshake."); - bool temp_dtr = data & MCR_DTR_MASK? true:false; - bool temp_rts = data & MCR_RTS_MASK? true:false; - bool temp_op1 = data & MCR_OP1_MASK? true:false; - bool temp_op2 = data & MCR_OP2_MASK? true:false; - bool temp_loopback = data & MCR_LOOPBACK_Enable_MASK? true:false; - if(loopback!=temp_loopback) { - if(temp_loopback) setRTSDTR(false,false); - else setRTSDTR(temp_rts,temp_dtr); + if (data&FIFO_FLOWCONTROL) LOG_MSG("Warning: tried to activate hardware handshake."); + bool new_dtr = data & MCR_DTR_MASK? true:false; + bool new_rts = data & MCR_RTS_MASK? true:false; + bool new_op1 = data & MCR_OP1_MASK? true:false; + bool new_op2 = data & MCR_OP2_MASK? true:false; + bool new_loopback = data & MCR_LOOPBACK_Enable_MASK? true:false; + if (loopback != new_loopback) { + if (new_loopback) setRTSDTR(false,false); + else setRTSDTR(new_rts,new_dtr); } - if (temp_loopback) { // is on: + if (new_loopback) { // is on: // DTR->DSR // RTS->CTS // OP1->RI // OP2->CD - if(temp_dtr!=dtr && !d_dsr) { - d_dsr=true; + if (new_dtr != dtr && !d_dsr) { + d_dsr = true; rise (MSR_PRIORITY); } - if(temp_rts!=rts && !d_cts) { - d_cts=true; + if (new_rts != rts && !d_cts) { + d_cts = true; rise (MSR_PRIORITY); } - if(temp_op1!=op1 && !d_ri) { + if (new_op1 != op1 && !d_ri) { // interrupt only at trailing edge - if(!temp_op1) { - d_ri=true; + if (!new_op1) { + d_ri = true; rise (MSR_PRIORITY); } } - if(temp_op2!=op2 && !d_cd) { - d_cd=true; + if (new_op2 != op2 && !d_cd) { + d_cd = true; rise (MSR_PRIORITY); } } else { // loopback is off - if(temp_rts!=rts) { + if (new_rts != rts) { // RTS difference - if(temp_dtr!=dtr) { + if (new_dtr != dtr) { // both difference #if SERIAL_DEBUG - log_ser(dbg_modemcontrol,"RTS %x.",temp_rts); - log_ser(dbg_modemcontrol,"DTR %x.",temp_dtr); + log_ser(dbg_modemcontrol,"RTS %x.",new_rts); + log_ser(dbg_modemcontrol,"DTR %x.",new_dtr); #endif - setRTSDTR(temp_rts, temp_dtr); + setRTSDTR(new_rts, new_dtr); } else { // only RTS #if SERIAL_DEBUG - log_ser(dbg_modemcontrol,"RTS %x.",temp_rts); + log_ser(dbg_modemcontrol,"RTS %x.",new_rts); #endif - setRTS(temp_rts); + setRTS(new_rts); } - } else if(temp_dtr!=dtr) { + } else if (new_dtr != dtr) { // only DTR #if SERIAL_DEBUG - log_ser(dbg_modemcontrol,"%DTR %x.",temp_dtr); + log_ser(dbg_modemcontrol,"%DTR %x.",new_dtr); #endif - setDTR(temp_dtr); + setDTR(new_dtr); } } - // interrupt logic: if OP2 is 0, the IRQ line is tristated (pulled high) - if((!op2) && temp_op2) { + // interrupt logic: if new_OP2 is 0, the IRQ line is tristated (pulled high) + // which turns off the IRQ generation. + if ((!op2) && new_op2) { // irq has been enabled (tristate high -> irq level) - if(!irq_active) PIC_DeActivateIRQ(irq); - } else if(op2 && (!temp_op2)) { - if(!irq_active) PIC_ActivateIRQ(irq); + // Generate one if ComputeInterrupts has set irq_active to true + if (irq_active) PIC_ActivateIRQ(irq); + } else if (op2 && (!new_op2)) { + // irq has been disabled (irq level -> tristate) + // Remove the IRQ signal if the irq was being generated before + if (irq_active) PIC_DeActivateIRQ(irq); } - dtr=temp_dtr; - rts=temp_rts; - op1=temp_op1; - op2=temp_op2; - loopback=temp_loopback; + dtr=new_dtr; + rts=new_rts; + op1=new_op1; + op2=new_op2; + loopback=new_loopback; } /*****************************************************************************/ @@ -1126,7 +1130,7 @@ CSerial::CSerial(Bitu id, CommandLine* cmd) { txOverrunErrors=0; overrunIF0=0; breakErrors=0; - + for (Bitu i = 0; i <= 7; i++) { WriteHandler[i].Install (i + base, SERIAL_Write, IO_MB); ReadHandler[i].Install (i + base, SERIAL_Read, IO_MB); @@ -1136,8 +1140,10 @@ CSerial::CSerial(Bitu id, CommandLine* cmd) { bool CSerial::getBituSubstring(const char* name,Bitu* data, CommandLine* cmd) { std::string tmpstring; if(!(cmd->FindStringBegin(name,tmpstring,false))) return false; - const char* tmpchar=tmpstring.c_str(); - if(sscanf(tmpchar,"%u",data)!=1) return false; + const char* tmpchar = tmpstring.c_str(); + unsigned int d = 0; + if(sscanf(tmpchar,"%u",&d) != 1) return false; + *data = static_cast(d); return true; } diff --git a/src/hardware/serialport/softmodem.cpp b/src/hardware/serialport/softmodem.cpp index ed9d7a1..63625b3 100644 --- a/src/hardware/serialport/softmodem.cpp +++ b/src/hardware/serialport/softmodem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -270,7 +270,7 @@ void CSerialModem::EnterIdleState(void){ } // get rid of everything if(serversocket) { - while(waitingclientsocket=serversocket->Accept()) + while( (waitingclientsocket=serversocket->Accept()) ) delete waitingclientsocket; } else if (listenport) { @@ -362,18 +362,23 @@ void CSerialModem::DoCommand() { helper[0]=0; helper--; } + + //Large enough scope, so the buffers are still valid when reaching Dail. + char buffer[128]; + char obuffer[128]; if (strlen(foundstr) >= 12) { // Check if supplied parameter only consists of digits bool isNum = true; - for (Bitu i=0; i '9') isNum = false; if (isNum) { // Parameter is a number with at least 12 digits => this cannot // be a valid IP/name // Transform by adding dots - char buffer[128]; - Bitu j = 0; - for (Bitu i=0; i #include - -#define MAX_OUTPUT 0x7fff -#define STEP 0x10000 - -/* Formulas for noise generator */ -/* bit0 = output */ - -/* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */ -#define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */ - -/* noise feedback for periodic noise mode */ -//#define FB_PNOISE 0x10000 /* 16bit rorate */ -#define FB_PNOISE 0x08000 /* JH 981127 - fixes Do Run Run */ - -/* -0x08000 is definitely wrong. The Master System conversion of Marble Madness -uses periodic noise as a baseline. With a 15-bit rotate, the bassline is -out of tune. -The 16-bit rotate has been confirmed against a real PAL Sega Master System 2. -Hope that helps the System E stuff, more news on the PSG as and when! -*/ - -/* noise generator start preset (for periodic noise) */ -#define NG_PRESET 0x0f35 +#include "mame/emu.h" +#include "mame/sn76496.h" -struct SN76496 { - int SampleRate; - unsigned int UpdateStep; - int VolTable[16]; /* volume table */ - int Register[8]; /* registers */ - int LastRegister; /* last register written */ - int Volume[4]; /* volume of voice 0-2 and noise */ - unsigned int RNG; /* noise generator */ - int NoiseFB; /* noise feedback mask */ - int Period[4]; - int Count[4]; - int Output[4]; -}; - -static struct SN76496 sn; +#define SOUND_CLOCK (14318180 / 4) #define TDAC_DMA_BUFSIZE 1024 @@ -99,221 +63,41 @@ static struct { } dac; } tandy; +static sn76496_device device_sn76496(machine_config(), 0, 0, SOUND_CLOCK ); +static ncr8496_device device_ncr8496(machine_config(), 0, 0, SOUND_CLOCK); + +static sn76496_base_device* activeDevice = &device_ncr8496; +#define device (*activeDevice) static void SN76496Write(Bitu /*port*/,Bitu data,Bitu /*iolen*/) { - struct SN76496 *R = &sn; - tandy.last_write=PIC_Ticks; if (!tandy.enabled) { tandy.chan->Enable(true); tandy.enabled=true; } + device.write(data); - /* update the output buffer before changing the registers */ - - if (data & 0x80) - { - int r = (data & 0x70) >> 4; - int c = r/2; - - R->LastRegister = r; - R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); - switch (r) - { - case 0: /* tone 0 : frequency */ - case 2: /* tone 1 : frequency */ - case 4: /* tone 2 : frequency */ - R->Period[c] = R->UpdateStep * R->Register[r]; - if (R->Period[c] == 0) R->Period[c] = 0x3fe; - if (r == 4) - { - /* update noise shift frequency */ - if ((R->Register[6] & 0x03) == 0x03) - R->Period[3] = 2 * R->Period[2]; - } - break; - case 1: /* tone 0 : volume */ - case 3: /* tone 1 : volume */ - case 5: /* tone 2 : volume */ - case 7: /* noise : volume */ - R->Volume[c] = R->VolTable[data & 0x0f]; - break; - case 6: /* noise : frequency, mode */ - { - int n = R->Register[6]; - R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE; - n &= 3; - /* N/512,N/1024,N/2048,Tone #3 output */ - R->Period[3] = (n == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+n)); - - /* reset noise shifter */ -// R->RNG = NG_PRESET; -// R->Output[3] = R->RNG & 1; - } - break; - } - } - else - { - int r = R->LastRegister; - int c = r/2; - - switch (r) - { - case 0: /* tone 0 : frequency */ - case 2: /* tone 1 : frequency */ - case 4: /* tone 2 : frequency */ - R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4); - R->Period[c] = R->UpdateStep * R->Register[r]; - if (R->Period[c] == 0) R->Period[c] = 0x3fe; - if (r == 4) - { - /* update noise shift frequency */ - if ((R->Register[6] & 0x03) == 0x03) - R->Period[3] = 2 * R->Period[2]; - } - break; - } - } +// LOG_MSG("3voice write %X at time %7.3f",data,PIC_FullIndex()); } static void SN76496Update(Bitu length) { + //Disable the channel if it's been quiet for a while if ((tandy.last_write+5000)Enable(false); + return; } - int i; - struct SN76496 *R = &sn; - Bit16s * buffer=(Bit16s *)MixTemp; + const Bitu MAX_SAMPLES = 2048; + if (length > MAX_SAMPLES) + return; + Bit16s buffer[MAX_SAMPLES]; + Bit16s* outputs = buffer; - /* If the volume is 0, increase the counter */ - for (i = 0;i < 4;i++) - { - if (R->Volume[i] == 0) - { - /* note that I do count += length, NOT count = length + 1. You might think */ - /* it's the same since the volume is 0, but doing the latter could cause */ - /* interferencies when the program is rapidly modulating the volume. */ - if (R->Count[i] <= (int)length*STEP) R->Count[i] += length*STEP; - } - } - - Bitu count=length; - while (count) - { - int vol[4]; - unsigned int out; - int left; - - - /* vol[] keeps track of how long each square wave stays */ - /* in the 1 position during the sample period. */ - vol[0] = vol[1] = vol[2] = vol[3] = 0; - - for (i = 0;i < 3;i++) - { - if (R->Output[i]) vol[i] += R->Count[i]; - R->Count[i] -= STEP; - /* Period[i] is the half period of the square wave. Here, in each */ - /* loop I add Period[i] twice, so that at the end of the loop the */ - /* square wave is in the same status (0 or 1) it was at the start. */ - /* vol[i] is also incremented by Period[i], since the wave has been 1 */ - /* exactly half of the time, regardless of the initial position. */ - /* If we exit the loop in the middle, Output[i] has to be inverted */ - /* and vol[i] incremented only if the exit status of the square */ - /* wave is 1. */ - while (R->Count[i] <= 0) - { - R->Count[i] += R->Period[i]; - if (R->Count[i] > 0) - { - R->Output[i] ^= 1; - if (R->Output[i]) vol[i] += R->Period[i]; - break; - } - R->Count[i] += R->Period[i]; - vol[i] += R->Period[i]; - } - if (R->Output[i]) vol[i] -= R->Count[i]; - } - - left = STEP; - do - { - int nextevent; - - - if (R->Count[3] < left) nextevent = R->Count[3]; - else nextevent = left; - - if (R->Output[3]) vol[3] += R->Count[3]; - R->Count[3] -= nextevent; - if (R->Count[3] <= 0) - { - if (R->RNG & 1) R->RNG ^= R->NoiseFB; - R->RNG >>= 1; - R->Output[3] = R->RNG & 1; - R->Count[3] += R->Period[3]; - if (R->Output[3]) vol[3] += R->Period[3]; - } - if (R->Output[3]) vol[3] -= R->Count[3]; - - left -= nextevent; - } while (left > 0); - - out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] + - vol[2] * R->Volume[2] + vol[3] * R->Volume[3]; - - if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP; - - *(buffer++) = (Bit16s)(out / STEP); - - count--; - } - tandy.chan->AddSamples_m16(length,(Bit16s *)MixTemp); + device_sound_interface::sound_stream stream; + static_cast(device).sound_stream_update(stream, 0, &outputs, length); + tandy.chan->AddSamples_m16(length, buffer); } - - -static void SN76496_set_clock(int clock) { - struct SN76496 *R = &sn; - - /* the base clock for the tone generators is the chip clock divided by 16; */ - /* for the noise generator, it is clock / 256. */ - /* Here we calculate the number of steps which happen during one sample */ - /* at the given sample rate. No. of events = sample rate / (clock/16). */ - /* STEP is a multiplier used to turn the fraction into a fixed point */ - /* number. */ - R->UpdateStep = (unsigned int)(((double)STEP * R->SampleRate * 16) / clock); -} - - -static void SN76496_set_gain(int gain) { - struct SN76496 *R = &sn; - int i; - double out; - - gain &= 0xff; - - /* increase max output basing on gain (0.2 dB per step) */ - out = MAX_OUTPUT / 3; - while (gain-- > 0) - out *= 1.023292992; /* = (10 ^ (0.2/20)) */ - - /* build volume table (2dB per step) */ - for (i = 0;i < 15;i++) - { - /* limit volume to avoid clipping */ - if (out > MAX_OUTPUT / 3) R->VolTable[i] = MAX_OUTPUT / 3; - else R->VolTable[i] = (int)out; - - out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ - } - R->VolTable[15] = 0; -} - - - bool TS_Get_Address(Bitu& tsaddr, Bitu& tsirq, Bitu& tsdma) { tsaddr=0; tsirq =0; @@ -405,7 +189,7 @@ static void TandyDACWrite(Bitu port,Bitu data,Bitu /*iolen*/) { } break; case 0xc6: - tandy.dac.frequency = tandy.dac.frequency & 0xf00 | (Bit8u)(data&0xff); + tandy.dac.frequency = (tandy.dac.frequency & 0xf00) | (Bit8u)(data & 0xff); switch (tandy.dac.mode&3) { case 0: // joystick mode @@ -418,7 +202,7 @@ static void TandyDACWrite(Bitu port,Bitu data,Bitu /*iolen*/) { } break; case 0xc7: - tandy.dac.frequency = tandy.dac.frequency & 0x00ff | (((Bit8u)(data&0xf))<<8); + tandy.dac.frequency = (tandy.dac.frequency & 0x00ff) | (((Bit8u)(data & 0xf)) << 8); tandy.dac.amplitude = (Bit8u)(data>>5); switch (tandy.dac.mode&3) { case 0: @@ -492,6 +276,10 @@ public: enable_hw_tandy_dac=false; } + //Select the correct tandy chip implementation + if (machine == MCH_PCJR) activeDevice = &device_sn76496; + else activeDevice = &device_ncr8496; + real_writeb(0x40,0xd4,0x00); if (IS_TANDY_ARCH) { /* enable tandy sound if tandy=true/auto */ @@ -548,26 +336,9 @@ public: tandy.enabled=false; real_writeb(0x40,0xd4,0xff); /* BIOS Tandy DAC initialization value */ - Bitu i; - struct SN76496 *R = &sn; - R->SampleRate = sample_rate; - SN76496_set_clock(3579545); - for (i = 0;i < 4;i++) R->Volume[i] = 0; - R->LastRegister = 0; - for (i = 0;i < 8;i+=2) - { - R->Register[i] = 0; - R->Register[i + 1] = 0x0f; /* volume = 0 */ - } - - for (i = 0;i < 4;i++) - { - R->Output[i] = 0; - R->Period[i] = R->Count[i] = R->UpdateStep; - } - R->RNG = NG_PRESET; - R->Output[3] = R->RNG & 1; - SN76496_set_gain(0x1); + ((device_t&)device).device_start(); + device.convert_samplerate(sample_rate); + } ~TANDYSOUND(){ } }; diff --git a/src/hardware/timer.cpp b/src/hardware/timer.cpp index e6b4db7..ac07064 100644 --- a/src/hardware/timer.cpp +++ b/src/hardware/timer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -345,6 +345,8 @@ static void write_p43(Bitu /*port*/,Bitu val,Bitu /*iolen*/) { } else { PIC_DeActivateIRQ(0); } + } else if (latch == 2) { + PCSPEAKER_SetCounter(0,3); } pit[latch].new_mode = true; } @@ -400,6 +402,10 @@ void TIMER_SetGate2(bool in) { gate2 = in; //Set it here so the counter_latch above works } +bool TIMER_GetOutput2(void) { + return counter_output(2); +} + class TIMER:public Module_base{ private: IO_ReadHandleObject ReadHandler[4]; diff --git a/src/hardware/vga.cpp b/src/hardware/vga.cpp index da5960c..386faee 100644 --- a/src/hardware/vga.cpp +++ b/src/hardware/vga.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/vga_attr.cpp b/src/hardware/vga_attr.cpp index f23be28..ea7b17c 100644 --- a/src/hardware/vga_attr.cpp +++ b/src/hardware/vga_attr.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -23,22 +23,70 @@ #define attr(blah) vga.attr.blah -void VGA_ATTR_SetPalette(Bit8u index,Bit8u val) { - vga.attr.palette[index] = val; - if (vga.attr.mode_control & 0x80) val = (val&0xf) | (vga.attr.color_select << 4); - val &= 63; - val |= (vga.attr.color_select & 0xc) << 4; - if (GCC_UNLIKELY(machine==MCH_EGA)) { - if ((vga.crtc.vertical_total | ((vga.crtc.overflow & 1) << 8)) == 260) { - // check for intensity bit - if (val&0x10) val|=0x38; - else { - val&=0x7; - // check for special brown - if (val==6) val=0x14; +void VGA_ATTR_SetEGAMonitorPalette(EGAMonitorMode m) { + // palette bit assignment: + // bit | pin | EGA | CGA | monochrome + // ----+-----+------------+-----------+------------ + // 0 | 5 | blue | blue | nc + // 1 | 4 | green | green* | nc + // 2 | 3 | red | red* | nc + // 3 | 7 | blue sec. | nc | video + // 4 | 6 | green sec. | intensity | intensity + // 5 | 2 | red sec. | nc | nc + // 6-7 | not used + // * additive color brown instead of yellow + switch(m) { + case CGA: + //LOG_MSG("Monitor CGA"); + for (Bitu i=0;i<64;i++) { + vga.dac.rgb[i].red=((i & 0x4)? 0x2a:0) + ((i & 0x10)? 0x15:0); + vga.dac.rgb[i].blue=((i & 0x1)? 0x2a:0) + ((i & 0x10)? 0x15:0); + + // replace yellow with brown + if ((i & 0x17) == 0x6) vga.dac.rgb[i].green = 0x15; + else vga.dac.rgb[i].green = + ((i & 0x2)? 0x2a:0) + ((i & 0x10)? 0x15:0); } - } + break; + case EGA: + //LOG_MSG("Monitor EGA"); + for (Bitu i=0;i<64;i++) { + vga.dac.rgb[i].red=((i & 0x4)? 0x2a:0) + ((i & 0x20)? 0x15:0); + vga.dac.rgb[i].green=((i & 0x2)? 0x2a:0) + ((i & 0x10)? 0x15:0); + vga.dac.rgb[i].blue=((i & 0x1)? 0x2a:0) + ((i & 0x8)? 0x15:0); + } + break; + case MONO: + //LOG_MSG("Monitor MONO"); + for (Bitu i=0;i<64;i++) { + Bit8u value = ((i & 0x8)? 0x2a:0) + ((i & 0x10)? 0x15:0); + vga.dac.rgb[i].red = vga.dac.rgb[i].green = + vga.dac.rgb[i].blue = value; + } + break; } + + // update the mappings + for (Bit8u i=0;i<0x10;i++) + VGA_ATTR_SetPalette(i,vga.attr.palette[i]); +} + +void VGA_ATTR_SetPalette(Bit8u index, Bit8u val) { + // the attribute table stores only 6 bits + val &= 63; + vga.attr.palette[index] = val; + + // apply the plane mask + val = vga.attr.palette[index & vga.attr.color_plane_enable]; + + // replace bits 4-5 if configured + if (vga.attr.mode_control & 0x80) + val = (val&0xf) | (vga.attr.color_select << 4); + + // set bits 6 and 7 (not relevant for EGA) + val |= (vga.attr.color_select & 0xc) << 4; + + // apply VGA_DAC_CombineColor(index,val); } @@ -126,7 +174,14 @@ void write_p3c0(Bitu /*port*/,Bitu val,Bitu iolen) { break; case 0x12: /* Color Plane Enable Register */ /* Why disable colour planes? */ - attr(color_plane_enable)=(Bit8u)val; + /* To support weird modes. */ + if ((attr(color_plane_enable)^val) & 0xf) { + // in case the plane enable bits change... + attr(color_plane_enable)=(Bit8u)val; + for (Bit8u i=0;i<0x10;i++) + VGA_ATTR_SetPalette(i,vga.attr.palette[i]); + } else + attr(color_plane_enable)=(Bit8u)val; /* 0 Bit plane 0 is enabled if set. 1 Bit plane 1 is enabled if set. @@ -180,9 +235,8 @@ void write_p3c0(Bitu /*port*/,Bitu val,Bitu iolen) { } if (attr(color_select) ^ val) { attr(color_select)=(Bit8u)val; - for (Bit8u i=0;i<0x10;i++) { + for (Bit8u i=0;i<0x10;i++) VGA_ATTR_SetPalette(i,vga.attr.palette[i]); - } } /* 0-1 If 3C0h index 10h bit 7 is set these 2 bits are used as bits 4-5 of diff --git a/src/hardware/vga_crtc.cpp b/src/hardware/vga_crtc.cpp index 9906c72..541ea13 100644 --- a/src/hardware/vga_crtc.cpp +++ b/src/hardware/vga_crtc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -242,7 +242,7 @@ void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen) { break; case 0x12: /* Vertical Display End Register */ if (val!=crtc(vertical_display_end)) { - if (abs((Bits)val-(Bits)crtc(vertical_display_end))<3) { + if (abs(static_cast((Bits)val-(Bits)crtc(vertical_display_end)))<3) { // delay small vde changes a bit to avoid screen resizing // if they are reverted in a short timeframe PIC_RemoveEvents(VGA_SetupDrawing); diff --git a/src/hardware/vga_dac.cpp b/src/hardware/vga_dac.cpp index dbc04e1..80f6f36 100644 --- a/src/hardware/vga_dac.cpp +++ b/src/hardware/vga_dac.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dosbox.h" @@ -96,6 +96,7 @@ static void write_p3c8(Bitu port,Bitu val,Bitu iolen) { vga.dac.write_index=val; vga.dac.pel_index=0; vga.dac.state=DAC_WRITE; + vga.dac.read_index= val - 1; } static Bitu read_p3c8(Bitu port, Bitu iolen){ @@ -213,19 +214,5 @@ void VGA_SetupDAC(void) { IO_RegisterReadHandler(0x3c8,read_p3c8,IO_MB); IO_RegisterWriteHandler(0x3c9,write_p3c9,IO_MB); IO_RegisterReadHandler(0x3c9,read_p3c9,IO_MB); - } else if (machine==MCH_EGA) { - for (Bitu i=0;i<64;i++) { - if ((i&4)>0) vga.dac.rgb[i].red=0x2a; - else vga.dac.rgb[i].red=0; - if ((i&32)>0) vga.dac.rgb[i].red+=0x15; - - if ((i&2)>0) vga.dac.rgb[i].green=0x2a; - else vga.dac.rgb[i].green=0; - if ((i&16)>0) vga.dac.rgb[i].green+=0x15; - - if ((i&1)>0) vga.dac.rgb[i].blue=0x2a; - else vga.dac.rgb[i].blue=0; - if ((i&8)>0) vga.dac.rgb[i].blue+=0x15; - } } } diff --git a/src/hardware/vga_draw.cpp b/src/hardware/vga_draw.cpp index e4a57ef..168d9dc 100644 --- a/src/hardware/vga_draw.cpp +++ b/src/hardware/vga_draw.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -79,38 +79,28 @@ static Bit8u * VGA_Draw_CGA16_Line(Bitu vidstart, Bitu line) { const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift); #define CGA16_READER(OFF) (base[(vidstart +(OFF))& (8*1024 -1)]) Bit32u * draw=(Bit32u *)TempLine; - //Generate a temporary bitline to calculate the avarage - //over bit-2 bit-1 bit bit+1. - //Combine this number with the current colour to get - //an unique index in the pallette. Or it with bit 7 as they are stored - //in the upperpart to keep them from interfering the regular cga stuff + //There are 640 hdots in each line of the screen. + //The color of an even hdot always depends on only 4 bits of video RAM. + //The color of an odd hdot depends on 4 bits of video RAM in + //1-hdot-per-pixel modes and 6 bits of video RAM in 2-hdot-per-pixel + //modes. We always assume 6 and use duplicate palette entries in + //1-hdot-per-pixel modes so that we can use the same routine for all + //composite modes. + temp[1] = (CGA16_READER(0) >> 6) & 3; + for(Bitu x = 2; x < 640; x+=2) { + temp[x] = (temp[x-1] & 0xf); + temp[x+1] = (temp[x] << 2) | ((( CGA16_READER(x>>3)) >> (6-(x&6)) )&3); + } + temp[640] = temp[639] & 0xf; + temp[641] = temp[640] << 2; + temp[642] = temp[641] & 0xf; - for(Bitu x = 0; x < 640; x++) - temp[x+2] = (( CGA16_READER(x>>3)>> (7-(x&7)) )&1) << 4; - //shift 4 as that is for the index. - Bitu i = 0,temp1,temp2,temp3,temp4; + Bitu i = 2; for (Bitu x=0;x>= 4; - - temp1 = temp[i] + temp[i+1] + temp[i+2] + temp[i+3]; i++; - temp2 = temp[i] + temp[i+1] + temp[i+2] + temp[i+3]; i++; - temp3 = temp[i] + temp[i+1] + temp[i+2] + temp[i+3]; i++; - temp4 = temp[i] + temp[i+1] + temp[i+2] + temp[i+3]; i++; - - *draw++ = 0x80808080|(temp1|val1) | - ((temp2|val1) << 8) | - ((temp3|val1) <<16) | - ((temp4|val1) <<24); - temp1 = temp[i] + temp[i+1] + temp[i+2] + temp[i+3]; i++; - temp2 = temp[i] + temp[i+1] + temp[i+2] + temp[i+3]; i++; - temp3 = temp[i] + temp[i+1] + temp[i+2] + temp[i+3]; i++; - temp4 = temp[i] + temp[i+1] + temp[i+2] + temp[i+3]; i++; - *draw++ = 0x80808080|(temp1|val2) | - ((temp2|val2) << 8) | - ((temp3|val2) <<16) | - ((temp4|val2) <<24); + *draw++ = 0xc0708030 | temp[i] | (temp[i+1] << 8) | (temp[i+2] << 16) | (temp[i+3] << 24); + i += 4; + *draw++ = 0xc0708030 | temp[i] | (temp[i+1] << 8) | (temp[i+2] << 16) | (temp[i+3] << 24); + i += 4; } return TempLine; #undef CGA16_READER @@ -207,10 +197,30 @@ static Bit8u * VGA_Draw_Linear_Line(Bitu vidstart, Bitu /*line*/) { } static Bit8u * VGA_Draw_Xlat16_Linear_Line(Bitu vidstart, Bitu /*line*/) { - Bit8u *ret = &vga.draw.linear_base[ vidstart & vga.draw.linear_mask ]; + Bitu offset = vidstart & vga.draw.linear_mask; + Bit8u *ret = &vga.draw.linear_base[offset]; Bit16u* temps = (Bit16u*) TempLine; - for(Bitu i = 0; i < vga.draw.line_length; i++) { - temps[i]=vga.dac.xlat16[ret[i]]; + + // see VGA_Draw_Linear_Line + if (GCC_UNLIKELY((vga.draw.line_length + offset)& ~vga.draw.linear_mask)) { + Bitu end = (offset + vga.draw.line_length) & vga.draw.linear_mask; + + // assuming lines not longer than 4096 pixels + Bitu wrapped_len = end & 0xFFF; + Bitu unwrapped_len = vga.draw.line_length-wrapped_len; + + // unwrapped chunk: to top of memory block + for(Bitu i = 0; i < unwrapped_len; i++) + temps[i]=vga.dac.xlat16[ret[i]]; + + // wrapped chunk: from base of memory block + for(Bitu i = 0; i < wrapped_len; i++) + temps[i + unwrapped_len]=vga.dac.xlat16[vga.draw.linear_base[i]]; + + } else { + for(Bitu i = 0; i < vga.draw.line_length; i++) { + temps[i]=vga.dac.xlat16[ret[i]]; + } } return TempLine; } @@ -385,7 +395,7 @@ static Bit8u * VGA_TEXT_Draw_Line(Bitu vidstart, Bitu line) { *draw++=(fg&mask1) | (bg&~mask1); *draw++=(fg&mask2) | (bg&~mask2); } - if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x8)) goto skip_cursor; + if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x10)) goto skip_cursor; font_addr = (vga.draw.cursor.address-vidstart) >> 1; if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) { if (line> 1; if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) { if (line= vga.draw.cursor.sline) && + if ((vga.draw.cursor.count&0x10) && (line >= vga.draw.cursor.sline) && (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) { // the adress of the attribute that makes up the cell the cursor is in Bits attr_addr = (vga.draw.cursor.address-vidstart) >> 1; @@ -554,7 +564,7 @@ static Bit8u* VGA_TEXT_Xlat16_Draw_Line(Bitu vidstart, Bitu line) { } } // draw the text mode cursor if needed - if ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) && + if ((vga.draw.cursor.count&0x10) && (line >= vga.draw.cursor.sline) && (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) { // the adress of the attribute that makes up the cell the cursor is in Bits attr_addr = (vga.draw.cursor.address-vidstart) >> 1; @@ -824,7 +834,9 @@ static void VGA_VerticalTimer(Bitu /*val*/) { E_Exit("This new machine needs implementation in VGA_VerticalTimer too."); break; } + //Check if we can actually render, else skip the rest (frameskip) + vga.draw.cursor.count++; // Do this here, else the cursor speed depends on the frameskip if (vga.draw.vga_override || !RENDER_StartUpdate()) return; @@ -892,7 +904,7 @@ static void VGA_VerticalTimer(Bitu /*val*/) { else vga.draw.linear_mask = 0x3fff; // CGA, Tandy 4 pages vga.draw.cursor.address=vga.config.cursor_start*2; vga.draw.address *= 2; - vga.draw.cursor.count++; + //vga.draw.cursor.count++; //Moved before the frameskip test. /* check for blinking and blinking change delay */ FontMask[1]=(vga.draw.blinking & (vga.draw.cursor.count >> 4)) ? 0 : 0xffffffff; @@ -902,7 +914,6 @@ static void VGA_VerticalTimer(Bitu /*val*/) { || !vga.draw.blinking) ? true:false; break; case M_HERC_GFX: - break; case M_CGA4:case M_CGA2: vga.draw.address=(vga.draw.address*2)&0x1fff; break; @@ -937,7 +948,7 @@ static void VGA_VerticalTimer(Bitu /*val*/) { vga.draw.parts_left = vga.draw.parts_total; PIC_AddEvent(VGA_DrawPart,(float)vga.draw.delay.parts + draw_skip,vga.draw.parts_lines); break; - case LINE: + case DRAWLINE: case EGALINE: if (GCC_UNLIKELY(vga.draw.lines_done < vga.draw.lines_total)) { LOG(LOG_VGAMISC,LOG_NORMAL)( "Lines left: %d", @@ -1033,7 +1044,7 @@ void VGA_SetupDrawing(Bitu /*val*/) { case MCH_CGA: case MCH_PCJR: case MCH_TANDY: - vga.draw.mode = LINE; + vga.draw.mode = DRAWLINE; break; case MCH_EGA: // Note: The Paradise SVGA uses the same panning mechanism as EGA @@ -1041,7 +1052,7 @@ void VGA_SetupDrawing(Bitu /*val*/) { break; case MCH_VGA: if (svgaCard==SVGA_None) { - vga.draw.mode = LINE; + vga.draw.mode = DRAWLINE; break; } // fall-through @@ -1243,6 +1254,29 @@ void VGA_SetupDrawing(Bitu /*val*/) { // Display end vga.draw.delay.vdend = vdend * vga.draw.delay.htotal; + // EGA frequency dependent monitor palette + if (machine == MCH_EGA) { + if (vga.misc_output & 1) { + // EGA card is in color mode + if ((1.0f/vga.draw.delay.htotal) > 19.0f) { + // 64 color EGA mode + VGA_ATTR_SetEGAMonitorPalette(EGA); + } else { + // 16 color CGA mode compatibility + VGA_ATTR_SetEGAMonitorPalette(CGA); + } + } else { + // EGA card in monochrome mode + // It is not meant to be autodetected that way, you either + // have a monochrome or color monitor connected and + // the EGA switches configured appropriately. + // But this would only be a problem if a program sets + // the adapter to monochrome mode and still expects color output. + // Such a program should be shot to the moon... + VGA_ATTR_SetEGAMonitorPalette(MONO); + } + } + vga.draw.parts_total=VGA_PARTS; /* 6 Horizontal Sync Polarity. Negative if set @@ -1278,7 +1312,9 @@ void VGA_SetupDrawing(Bitu /*val*/) { break; case 3: //480 line mode, filled with 525 total default: - pheight = (480.0 / 480.0) * ( 525.0 / vtotal ); + //Allow 527 total ModeX to have exact 1:1 aspect + target_total = (vga.mode==M_VGA && vtotal==527) ? 527.0 : 525.0; + pheight = (480.0 / 480.0) * ( target_total / vtotal ); break; } @@ -1371,6 +1407,7 @@ void VGA_SetupDrawing(Bitu /*val*/) { vga.draw.linear_mask = (vga.vmemwrap<<1) - 1; break; case M_CGA16: + aspect_ratio=1.2; doubleheight=true; vga.draw.blocks=width*2; width<<=4; @@ -1389,7 +1426,6 @@ void VGA_SetupDrawing(Bitu /*val*/) { VGA_DrawLine=VGA_Draw_1BPP_Line; break; case M_TEXT: - aspect_ratio=1.0; vga.draw.blocks=width; doublewidth=(vga.seq.clocking_mode & 0x8) > 0; if ((IS_VGA_ARCH) && (svgaCard==SVGA_None)) { @@ -1400,6 +1436,7 @@ void VGA_SetupDrawing(Bitu /*val*/) { } else { vga.draw.char9dot = true; width*=9; + aspect_ratio*=1.125; } VGA_DrawLine=VGA_TEXT_Xlat16_Draw_Line; bpp=16; @@ -1411,9 +1448,9 @@ void VGA_SetupDrawing(Bitu /*val*/) { } break; case M_HERC_GFX: - aspect_ratio=1.5; vga.draw.blocks=width*2; width*=16; + aspect_ratio=((double)width/(double)height)*(3.0/4.0); VGA_DrawLine=VGA_Draw_1BPP_Line; break; case M_TANDY2: @@ -1459,14 +1496,14 @@ void VGA_SetupDrawing(Bitu /*val*/) { break; case M_TANDY_TEXT: doublewidth=(vga.tandy.mode_control & 0x1)==0; - aspect_ratio=1; + aspect_ratio=1.2; doubleheight=true; vga.draw.blocks=width; width<<=3; VGA_DrawLine=VGA_TEXT_Draw_Line; break; case M_HERC_TEXT: - aspect_ratio=1; + aspect_ratio=((double)480)/((double)350); vga.draw.blocks=width; width<<=3; VGA_DrawLine=VGA_TEXT_Herc_Draw_Line; diff --git a/src/hardware/vga_gfx.cpp b/src/hardware/vga_gfx.cpp index 55c7cc1..86d3520 100644 --- a/src/hardware/vga_gfx.cpp +++ b/src/hardware/vga_gfx.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/vga_memory.cpp b/src/hardware/vga_memory.cpp index ecb2f5c..e52bff0 100644 --- a/src/hardware/vga_memory.cpp +++ b/src/hardware/vga_memory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -730,6 +730,20 @@ public: } }; +class VGA_HERC_Handler : public PageHandler { +public: + VGA_HERC_Handler() { + flags=PFLAG_READABLE|PFLAG_WRITEABLE; + } + HostPt GetHostReadPt(Bitu phys_page) { + // The 4kB map area is repeated in the 32kB range + return &vga.mem.linear[0]; + } + HostPt GetHostWritePt(Bitu phys_page) { + return GetHostReadPt( phys_page ); + } +}; + class VGA_Empty_Handler : public PageHandler { public: VGA_Empty_Handler() { @@ -754,6 +768,7 @@ static struct vg { VGA_UnchainedEGA_Handler uega; VGA_UnchainedVGA_Handler uvga; VGA_PCJR_Handler pcjr; + VGA_HERC_Handler herc; VGA_LIN4_Handler lin4; VGA_LFB_Handler lfb; VGA_LFBChanges_Handler lfbchanges; @@ -779,17 +794,24 @@ void VGA_SetupHandlers(void) { switch (machine) { case MCH_CGA: case MCH_PCJR: + MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty ); + MEM_SetPageHandler( VGA_PAGE_B0, 8, &vgaph.empty ); MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.pcjr ); goto range_done; case MCH_HERC: + MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty ); vgapages.base=VGA_PAGE_B0; if (vga.herc.enable_bits & 0x2) { vgapages.mask=0xffff; MEM_SetPageHandler(VGA_PAGE_B0,16,&vgaph.map); } else { vgapages.mask=0x7fff; - /* With hercules in 32kb mode it leaves a memory hole on 0xb800 */ - MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.map); + // With hercules in 32kB mode it leaves a memory hole on 0xb800 + // and has MDA-compatible address wrapping when graphics are disabled + if (vga.herc.enable_bits & 0x1) + MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.map); + else + MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.herc); MEM_SetPageHandler(VGA_PAGE_B8,8,&vgaph.empty); } goto range_done; @@ -806,7 +828,7 @@ void VGA_SetupHandlers(void) { } else { vga.tandy.draw_base = TANDY_VIDBASE( vga.tandy.draw_bank * 16 * 1024); vga.tandy.mem_base = TANDY_VIDBASE( vga.tandy.mem_bank * 16 * 1024); - MEM_SetPageHandler( 0xb8, 8, &vgaph.tandy ); + MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.tandy ); } goto range_done; // MEM_SetPageHandler(vga.tandy.mem_bank<<2,vga.tandy.is_32k_mode ? 0x08 : 0x04,range_handler); @@ -884,21 +906,21 @@ void VGA_SetupHandlers(void) { vgapages.base = VGA_PAGE_A0; vgapages.mask = 0xffff; MEM_SetPageHandler( VGA_PAGE_A0, 16, newHandler ); - MEM_ResetPageHandler( VGA_PAGE_B0, 16); + MEM_SetPageHandler( VGA_PAGE_B0, 16, &vgaph.empty ); break; case 2: vgapages.base = VGA_PAGE_B0; vgapages.mask = 0x7fff; MEM_SetPageHandler( VGA_PAGE_B0, 8, newHandler ); - MEM_ResetPageHandler( VGA_PAGE_A0, 16 ); - MEM_ResetPageHandler( VGA_PAGE_B8, 8 ); + MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty ); + MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.empty ); break; case 3: vgapages.base = VGA_PAGE_B8; vgapages.mask = 0x7fff; MEM_SetPageHandler( VGA_PAGE_B8, 8, newHandler ); - MEM_ResetPageHandler( VGA_PAGE_A0, 16 ); - MEM_ResetPageHandler( VGA_PAGE_B0, 8 ); + MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty ); + MEM_SetPageHandler( VGA_PAGE_B0, 8, &vgaph.empty ); break; } if(svgaCard == SVGA_S3Trio && (vga.s3.ext_mem_ctrl & 0x10)) diff --git a/src/hardware/vga_misc.cpp b/src/hardware/vga_misc.cpp index 45d8ea7..646a536 100644 --- a/src/hardware/vga_misc.cpp +++ b/src/hardware/vga_misc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -30,7 +30,7 @@ void vga_write_p3d5(Bitu port,Bitu val,Bitu iolen); Bitu vga_read_p3d5(Bitu port,Bitu iolen); Bitu vga_read_p3da(Bitu port,Bitu iolen) { - Bit8u retval=0; + Bit8u retval=4; // bit 2 set, needed by Blues Brothers double timeInFrame = PIC_FullIndex()-vga.draw.delay.framestart; vga.internal.attrindex=false; @@ -57,34 +57,26 @@ Bitu vga_read_p3da(Bitu port,Bitu iolen) { static void write_p3c2(Bitu port,Bitu val,Bitu iolen) { vga.misc_output=val; - if (val & 0x1) { - IO_RegisterWriteHandler(0x3d4,vga_write_p3d4,IO_MB); - IO_RegisterReadHandler(0x3d4,vga_read_p3d4,IO_MB); - IO_RegisterReadHandler(0x3da,vga_read_p3da,IO_MB); - IO_RegisterWriteHandler(0x3d5,vga_write_p3d5,IO_MB); - IO_RegisterReadHandler(0x3d5,vga_read_p3d5,IO_MB); + Bitu base=(val & 0x1) ? 0x3d0 : 0x3b0; + Bitu free=(val & 0x1) ? 0x3b0 : 0x3d0; + Bitu first=2, last=2; + if (machine==MCH_EGA) {first=0; last=3;} - IO_FreeWriteHandler(0x3b4,IO_MB); - IO_FreeReadHandler(0x3b4,IO_MB); - IO_FreeWriteHandler(0x3b5,IO_MB); - IO_FreeReadHandler(0x3b5,IO_MB); - IO_FreeReadHandler(0x3ba,IO_MB); - } else { - IO_RegisterWriteHandler(0x3b4,vga_write_p3d4,IO_MB); - IO_RegisterReadHandler(0x3b4,vga_read_p3d4,IO_MB); - IO_RegisterReadHandler(0x3ba,vga_read_p3da,IO_MB); - - IO_RegisterWriteHandler(0x3b5,vga_write_p3d5,IO_MB); - IO_RegisterReadHandler(0x3b5,vga_read_p3d5,IO_MB); - - - IO_FreeWriteHandler(0x3d4,IO_MB); - IO_FreeReadHandler(0x3d4,IO_MB); - IO_FreeWriteHandler(0x3d5,IO_MB); - IO_FreeReadHandler(0x3d5,IO_MB); - IO_FreeReadHandler(0x3da,IO_MB); + for (Bitu i=first; i<=last; i++) { + IO_RegisterWriteHandler(base+i*2,vga_write_p3d4,IO_MB); + IO_RegisterReadHandler(base+i*2,vga_read_p3d4,IO_MB); + IO_RegisterWriteHandler(base+i*2+1,vga_write_p3d5,IO_MB); + IO_RegisterReadHandler(base+i*2+1,vga_read_p3d5,IO_MB); + IO_FreeWriteHandler(free+i*2,IO_MB); + IO_FreeReadHandler(free+i*2,IO_MB); + IO_FreeWriteHandler(free+i*2+1,IO_MB); + IO_FreeReadHandler(free+i*2+1,IO_MB); } + + IO_RegisterReadHandler(base+0xa,vga_read_p3da,IO_MB); + IO_FreeReadHandler(free+0xa,IO_MB); + /* 0 If set Color Emulation. Base Address=3Dxh else Mono Emulation. Base Address=3Bxh. 2-3 Clock Select. 0: 25MHz, 1: 28MHz diff --git a/src/hardware/vga_other.cpp b/src/hardware/vga_other.cpp index 51b98ed..63be10a 100644 --- a/src/hardware/vga_other.cpp +++ b/src/hardware/vga_other.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -176,6 +176,9 @@ static void write_lightpen(Bitu port,Bitu val,Bitu) { } static double hue_offset = 0.0; +static Bit8u cga_comp = 0; +static bool new_cga = 0; + static Bit8u cga16_val = 0; static void update_cga16_color(void); static Bit8u herc_pal = 0; @@ -186,57 +189,151 @@ static void cga16_color_select(Bit8u val) { } static void update_cga16_color(void) { -// Algorithm provided by NewRisingSun -// His/Her algorithm is more complex and gives better results than the one below -// However that algorithm doesn't fit in our vga pallette. -// Therefore a simple variant is used, but the colours are bit lighter. +// New algorithm based on code by reenigne +// Works in all CGA graphics modes/color settings and can simulate older and newer CGA revisions + static const double tau = 6.28318531; // == 2*pi + static const double ns = 567.0/440; // degrees of hue shift per nanosecond -// It uses an avarage over the bits to give smooth transitions from colour to colour -// This is represented by the j variable. The i variable gives the 16 colours -// The draw handler calculates the needed avarage and combines this with the colour -// to match an entry that is generated here. + double tv_brightness = 0.0; // hardcoded for simpler implementation + double tv_saturation = (new_cga ? 0.7 : 0.6); - int baseR=0, baseG=0, baseB=0; - double sinhue,coshue,hue,basehue = 50.0; - double I,Q,Y,pixelI,pixelQ,R,G,B; - Bitu colorBit1,colorBit2,colorBit3,colorBit4,index; + bool bw = (vga.tandy.mode_control&4) != 0; + bool color_sel = (cga16_val&0x20) != 0; + bool background_i = (cga16_val&0x10) != 0; // Really foreground intensity, but this is what the CGA schematic calls it. + bool bpp1 = (vga.tandy.mode_control&0x10) != 0; + Bit8u overscan = cga16_val&0x0f; // aka foreground colour in 1bpp mode - if (cga16_val & 0x01) baseB += 0xa8; - if (cga16_val & 0x02) baseG += 0xa8; - if (cga16_val & 0x04) baseR += 0xa8; - if (cga16_val & 0x08) { baseR += 0x57; baseG += 0x57; baseB += 0x57; } - if (cga16_val & 0x20) basehue = 35.0; + double chroma_coefficient = new_cga ? 0.29 : 0.72; + double b_coefficient = new_cga ? 0.07 : 0; + double g_coefficient = new_cga ? 0.22 : 0; + double r_coefficient = new_cga ? 0.1 : 0; + double i_coefficient = new_cga ? 0.32 : 0.28; + double rgbi_coefficients[0x10]; + for (int c = 0; c < 0x10; c++) { + double v = 0; + if ((c & 1) != 0) + v += b_coefficient; + if ((c & 2) != 0) + v += g_coefficient; + if ((c & 4) != 0) + v += r_coefficient; + if ((c & 8) != 0) + v += i_coefficient; + rgbi_coefficients[c] = v; + } - hue = (basehue + hue_offset)*0.017453239; - sinhue = sin(hue); - coshue = cos(hue); + // The pixel clock delay calculation is not accurate for 2bpp, but the difference is small and a more accurate calculation would be too slow. + static const double rgbi_pixel_delay = 15.5*ns; + static const double chroma_pixel_delays[8] = { + 0, // Black: no chroma + 35*ns, // Blue: no XORs + 44.5*ns, // Green: XOR on rising and falling edges + 39.5*ns, // Cyan: XOR on falling but not rising edge + 44.5*ns, // Red: XOR on rising and falling edges + 39.5*ns, // Magenta: XOR on falling but not rising edge + 44.5*ns, // Yellow: XOR on rising and falling edges + 39.5*ns}; // White: XOR on falling but not rising edge + double pixel_clock_delay; + int o = overscan == 0 ? 15 : overscan; + if (overscan == 8) + pixel_clock_delay = rgbi_pixel_delay; + else { + double d = rgbi_coefficients[o]; + pixel_clock_delay = (chroma_pixel_delays[o & 7]*chroma_coefficient + rgbi_pixel_delay*d)/(chroma_coefficient + d); + } + pixel_clock_delay -= 21.5*ns; // correct for delay of color burst - for(Bitu i = 0; i < 16;i++) { - for(Bitu j = 0;j < 5;j++) { - index = 0x80|(j << 4)|i; //use upperpart of vga pallette - colorBit4 = (i&1)>>0; - colorBit3 = (i&2)>>1; - colorBit2 = (i&4)>>2; - colorBit1 = (i&8)>>3; + double hue_adjust = (-(90-33)-hue_offset+pixel_clock_delay)*tau/360.0; + double chroma_signals[8][4]; + for (Bit8u i=0; i<4; i++) { + chroma_signals[0][i] = 0; + chroma_signals[7][i] = 1; + for (Bit8u j=0; j<6; j++) { + static const double phases[6] = { + 270 - 21.5*ns, // blue + 135 - 29.5*ns, // green + 180 - 21.5*ns, // cyan + 0 - 21.5*ns, // red + 315 - 29.5*ns, // magenta + 90 - 21.5*ns}; // yellow/burst + // All the duty cycle fractions are the same, just under 0.5 as the rising edge is delayed 2ns more than the falling edge. + static const double duty = 0.5 - 2*ns/360.0; - //calculate lookup table - I = 0; Q = 0; - I += (double) colorBit1; - Q += (double) colorBit2; - I -= (double) colorBit3; - Q -= (double) colorBit4; - Y = (double) j / 4.0; //calculated avarage is over 4 bits + // We have a rectangle wave with period 1 (in units of the reciprocal of the color burst frequency) and duty + // cycle fraction "duty" and phase "phase". We band-limit this wave to frequency 2 and sample it at intervals of 1/4. + // We model our band-limited wave with 4 frequency components: + // f(x) = a + b*sin(x*tau) + c*cos(x*tau) + d*sin(x*2*tau) + // Then: + // a = integral(0, 1, f(x)*dx) = duty + // b = 2*integral(0, 1, f(x)*sin(x*tau)*dx) = 2*integral(0, duty, sin(x*tau)*dx) = 2*(1-cos(x*tau))/tau + // c = 2*integral(0, 1, f(x)*cos(x*tau)*dx) = 2*integral(0, duty, cos(x*tau)*dx) = 2*sin(duty*tau)/tau + // d = 2*integral(0, 1, f(x)*sin(x*2*tau)*dx) = 2*integral(0, duty, sin(x*4*pi)*dx) = 2*(1-cos(2*tau*duty))/(2*tau) + double a = duty; + double b = 2.0*(1.0-cos(duty*tau))/tau; + double c = 2.0*sin(duty*tau)/tau; + double d = 2.0*(1.0-cos(duty*2*tau))/(2*tau); - pixelI = I * 1.0 / 3.0; //I* tvSaturnation / 3.0 - pixelQ = Q * 1.0 / 3.0; //Q* tvSaturnation / 3.0 - I = pixelI*coshue + pixelQ*sinhue; - Q = pixelQ*coshue - pixelI*sinhue; + double x = (phases[j] + 21.5*ns + pixel_clock_delay)/360.0 + i/4.0; - R = Y + 0.956*I + 0.621*Q; if (R < 0.0) R = 0.0; if (R > 1.0) R = 1.0; - G = Y - 0.272*I - 0.647*Q; if (G < 0.0) G = 0.0; if (G > 1.0) G = 1.0; - B = Y - 1.105*I + 1.702*Q; if (B < 0.0) B = 0.0; if (B > 1.0) B = 1.0; + chroma_signals[j+1][i] = a + b*sin(x*tau) + c*cos(x*tau) + d*sin(x*2*tau); + } + } + Bitu CGApal[4] = { + overscan, + static_cast(2 + (color_sel||bw ? 1 : 0) + (background_i ? 8 : 0)), + static_cast(4 + (color_sel&&!bw? 1 : 0) + (background_i ? 8 : 0)), + static_cast(6 + (color_sel||bw ? 1 : 0) + (background_i ? 8 : 0)) + }; + for (Bit8u x=0; x<4; x++) { // Position of pixel in question + bool even = (x & 1) == 0; + for (Bit8u bits=0; bits<(even ? 0x10 : 0x40); ++bits) { + double Y=0, I=0, Q=0; + for (Bit8u p=0; p<4; p++) { // Position within color carrier cycle + // generate pixel pattern. + Bit8u rgbi; + if (bpp1) + rgbi = ((bits >> (3-p)) & (even ? 1 : 2)) != 0 ? overscan : 0; + else + if (even) + rgbi = CGApal[(bits >> (2-(p&2)))&3]; + else + rgbi = CGApal[(bits >> (4-((p+1)&6)))&3]; + Bit8u c = rgbi & 7; + if (bw && c != 0) + c = 7; - RENDER_SetPal((Bit8u)index,static_cast(R*baseR),static_cast(G*baseG),static_cast(B*baseB)); + // calculate composite output + double chroma = chroma_signals[c][(p+x)&3]*chroma_coefficient; + double composite = chroma + rgbi_coefficients[rgbi]; + + Y+=composite; + if (!bw) { // burst on + I+=composite*2*cos(hue_adjust + (p+x)*tau/4.0); + Q+=composite*2*sin(hue_adjust + (p+x)*tau/4.0); + } + } + + double contrast = 1 - tv_brightness; + + Y = (contrast*Y/4.0) + tv_brightness; if (Y>1.0) Y=1.0; if (Y<0.0) Y=0.0; + I = (contrast*I/4.0) * tv_saturation; if (I>0.5957) I=0.5957; if (I<-0.5957) I=-0.5957; + Q = (contrast*Q/4.0) * tv_saturation; if (Q>0.5226) Q=0.5226; if (Q<-0.5226) Q=-0.5226; + + static const double gamma = 2.2; + + double R = Y + 0.9563*I + 0.6210*Q; R = (R - 0.075) / (1-0.075); if (R<0) R=0; if (R>1) R=1; + double G = Y - 0.2721*I - 0.6474*Q; G = (G - 0.075) / (1-0.075); if (G<0) G=0; if (G>1) G=1; + double B = Y - 1.1069*I + 1.7046*Q; B = (B - 0.075) / (1-0.075); if (B<0) B=0; if (B>1) B=1; + R = pow(R, gamma); + G = pow(G, gamma); + B = pow(B, gamma); + + int r = static_cast(255*pow( 1.5073*R -0.3725*G -0.0832*B, 1/gamma)); if (r<0) r=0; if (r>255) r=255; + int g = static_cast(255*pow(-0.0275*R +0.9350*G +0.0670*B, 1/gamma)); if (g<0) g=0; if (g>255) g=255; + int b = static_cast(255*pow(-0.0272*R -0.0401*G +1.1677*B, 1/gamma)); if (b<0) b=0; if (b>255) b=255; + + Bit8u index = bits | ((x & 1) == 0 ? 0x30 : 0x80) | ((x & 2) == 0 ? 0x40 : 0); + RENDER_SetPal(index,r,g,b); } } } @@ -284,6 +381,8 @@ static void write_cga_color_select(Bitu val) { vga.tandy.border_color = val & 0xf; vga.attr.overscan_color = 0; break; + default: //Else unhandled values warning + break; } } @@ -294,12 +393,18 @@ static void write_cga(Bitu port,Bitu val,Bitu /*iolen*/) { vga.attr.disabled = (val&0x8)? 0: 1; if (vga.tandy.mode_control & 0x2) { // graphics mode if (vga.tandy.mode_control & 0x10) {// highres mode - if (!(val & 0x4)) { // burst on - VGA_SetMode(M_CGA16); // composite ntsc 160x200 16 color mode + if (cga_comp==1 || (cga_comp==0 && !(val&0x4))) { // composite display + VGA_SetMode(M_CGA16); // composite ntsc 640x200 16 color mode } else { VGA_SetMode(M_TANDY2); } - } else VGA_SetMode(M_TANDY4); // lowres mode + } else { // lowres mode + if (cga_comp==1) { // composite display + VGA_SetMode(M_CGA16); // composite ntsc 640x200 16 color mode + } else { + VGA_SetMode(M_TANDY4); + } + } write_cga_color_select(vga.tandy.color_select); } else { @@ -313,15 +418,29 @@ static void write_cga(Bitu port,Bitu val,Bitu /*iolen*/) { } } +static void CGAModel(bool pressed) { + if (!pressed) return; + new_cga = !new_cga; + update_cga16_color(); + LOG_MSG("%s model CGA selected", new_cga ? "Late" : "Early"); +} + +static void Composite(bool pressed) { + if (!pressed) return; + if (++cga_comp>2) cga_comp=0; + LOG_MSG("Composite output: %s",(cga_comp==0)?"auto":((cga_comp==1)?"on":"off")); + // switch RGB and Composite if in graphics mode + if (vga.tandy.mode_control & 0x2) + write_cga(0x3d8,vga.tandy.mode_control,1); +} + static void tandy_update_palette() { // TODO mask off bits if needed if (machine == MCH_TANDY) { switch (vga.mode) { case M_TANDY2: VGA_SetCGA2Table(vga.attr.palette[0], - //vga.attr.palette[vga.tandy.color_select&0xf]); - vga.attr.palette[0xf]); - //VGA_SetCGA2Table(vga.attr.palette[0xf],vga.attr.palette[0]); + vga.attr.palette[vga.tandy.color_select&0xf]); break; case M_TANDY4: if (vga.tandy.gfx_control & 0x8) { @@ -339,7 +458,7 @@ static void tandy_update_palette() { r_mask &= ~1; } VGA_SetCGA4Table( - vga.attr.palette[0], + vga.attr.palette[vga.tandy.color_select&0xf], vga.attr.palette[(2|color_set)& vga.tandy.palette_mask], vga.attr.palette[(4|(color_set& r_mask))& vga.tandy.palette_mask], vga.attr.palette[(6|color_set)& vga.tandy.palette_mask]); @@ -404,6 +523,7 @@ static void PCJr_FindMode(void) { if (vga.mode==M_TANDY16) VGA_SetModeNow(M_TANDY4); else VGA_SetMode(M_TANDY4); } + tandy_update_palette(); } else { VGA_SetMode(M_TANDY_TEXT); } @@ -469,17 +589,19 @@ static void write_tandy_reg(Bit8u val) { static void write_tandy(Bitu port,Bitu val,Bitu /*iolen*/) { switch (port) { case 0x3d8: - vga.tandy.mode_control=(Bit8u)val; - if (val&0x8) vga.attr.disabled &= ~1; - else vga.attr.disabled |= 1; - TandyCheckLineMask(); - VGA_SetBlinking(val & 0x20); - TANDY_FindMode(); + val &= 0x3f; // only bits 0-6 are used + if (vga.tandy.mode_control ^ val) { + vga.tandy.mode_control=(Bit8u)val; + if (val&0x8) vga.attr.disabled &= ~1; + else vga.attr.disabled |= 1; + TandyCheckLineMask(); + VGA_SetBlinking(val & 0x20); + TANDY_FindMode(); + VGA_StartResize(); + } break; case 0x3d9: vga.tandy.color_select=val; - if (vga.mode==M_TANDY2) vga.attr.palette[0xf] = vga.tandy.color_select&0xf; - else vga.attr.palette[0] = vga.tandy.color_select&0xf; // Pirates! tandy_update_palette(); break; case 0x3da: @@ -699,6 +821,8 @@ void VGA_SetupOther(void) { IO_RegisterWriteHandler(0x3d9,write_cga,IO_MB); MAPPER_AddHandler(IncreaseHue,MK_f11,MMOD2,"inchue","Inc Hue"); MAPPER_AddHandler(DecreaseHue,MK_f11,0,"dechue","Dec Hue"); + MAPPER_AddHandler(CGAModel,MK_f11,MMOD1|MMOD2,"cgamodel","CGA Model"); + MAPPER_AddHandler(Composite,MK_f12,0,"cgacomp","CGA Comp"); } if (machine==MCH_TANDY) { write_tandy( 0x3df, 0x0, 0 ); @@ -713,9 +837,6 @@ void VGA_SetupOther(void) { write_pcjr( 0x3df, 0x7 | (0x7 << 3), 0 ); IO_RegisterWriteHandler(0x3da,write_pcjr,IO_MB); IO_RegisterWriteHandler(0x3df,write_pcjr,IO_MB); - // additional CRTC access documented - IO_RegisterWriteHandler(0x3d0,write_crtc_index_other,IO_MB); - IO_RegisterWriteHandler(0x3d1,write_crtc_data_other,IO_MB); } if (machine==MCH_HERC) { Bitu base=0x3b0; @@ -733,8 +854,7 @@ void VGA_SetupOther(void) { IO_RegisterWriteHandler(0x3b8,write_hercules,IO_MB); IO_RegisterWriteHandler(0x3bf,write_hercules,IO_MB); IO_RegisterReadHandler(0x3ba,read_herc_status,IO_MB); - } - if (machine==MCH_CGA) { + } else if (!IS_EGAVGA_ARCH) { Bitu base=0x3d0; for (Bitu port_ct=0; port_ct<4; port_ct++) { IO_RegisterWriteHandler(base+port_ct*2,write_crtc_index_other,IO_MB); @@ -743,12 +863,5 @@ void VGA_SetupOther(void) { IO_RegisterReadHandler(base+port_ct*2+1,read_crtc_data_other,IO_MB); } } - if (IS_TANDY_ARCH) { - Bitu base=0x3d4; - IO_RegisterWriteHandler(base,write_crtc_index_other,IO_MB); - IO_RegisterWriteHandler(base+1,write_crtc_data_other,IO_MB); - IO_RegisterReadHandler(base,read_crtc_index_other,IO_MB); - IO_RegisterReadHandler(base+1,read_crtc_data_other,IO_MB); - } } diff --git a/src/hardware/vga_paradise.cpp b/src/hardware/vga_paradise.cpp index 3f6b294..f2f1334 100644 --- a/src/hardware/vga_paradise.cpp +++ b/src/hardware/vga_paradise.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/vga_s3.cpp b/src/hardware/vga_s3.cpp index d79b42b..b0347ea 100644 --- a/src/hardware/vga_s3.cpp +++ b/src/hardware/vga_s3.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/vga_seq.cpp b/src/hardware/vga_seq.cpp index 3c41bdd..7ce5ab2 100644 --- a/src/hardware/vga_seq.cpp +++ b/src/hardware/vga_seq.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/hardware/vga_tseng.cpp b/src/hardware/vga_tseng.cpp index 0983c23..14f38e7 100644 --- a/src/hardware/vga_tseng.cpp +++ b/src/hardware/vga_tseng.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -352,11 +352,11 @@ void FinishSetMode_ET4K(Bitu crtc_base, VGA_ModeExtraData* modeData) { // Select SVGA clock to get close to 60Hz (not particularly clean implementation) if (modeData->modeNo > 0x13) { - Bitu target = modeData->vtotal*8*modeData->htotal*60; + Bits target = static_cast(modeData->vtotal * 8 * modeData->htotal * 60); Bitu best = 1; - Bits dist = 100000000; - for (Bitu i=0; i<16; i++) { - Bits cdiff=abs((Bits)(target-et4k.clockFreq[i])); + int dist = 100000000; + for (Bitu i = 0; i < 16; i++) { + int cdiff = abs( static_cast(target - static_cast(et4k.clockFreq[i])) ); if (cdiff < dist) { best = i; dist = cdiff; @@ -701,8 +701,10 @@ void FinishSetMode_ET3K(Bitu crtc_base, VGA_ModeExtraData* modeData) { IO_Write(crtc_base,0x25);IO_Write(crtc_base+1,et4k_ver_overflow); // Clear remaining ext CRTC registers - for (Bitu i=0x16; i<=0x21; i++) - IO_Write(crtc_base,i);IO_Write(crtc_base+1,0); + for (Bitu i=0x16; i<=0x21; i++) { + IO_Write(crtc_base,i); + IO_Write(crtc_base+1,0); + } IO_Write(crtc_base,0x23);IO_Write(crtc_base+1,0); IO_Write(crtc_base,0x24);IO_Write(crtc_base+1,0); // Clear ext SEQ @@ -714,11 +716,11 @@ void FinishSetMode_ET3K(Bitu crtc_base, VGA_ModeExtraData* modeData) { // Select SVGA clock to get close to 60Hz (not particularly clean implementation) if (modeData->modeNo > 0x13) { - Bitu target = modeData->vtotal*8*modeData->htotal*60; + Bits target = static_cast(modeData->vtotal * 8 * modeData->htotal * 60); Bitu best = 1; - Bits dist = 100000000; - for (Bitu i=0; i<8; i++) { - Bits cdiff = abs((Bits)(target-et3k.clockFreq[i])); + int dist = 100000000; + for (Bitu i = 0; i < 8; i++) { + int cdiff = abs( static_cast(target - static_cast(et3k.clockFreq[i])) ); if (cdiff < dist) { best = i; dist = cdiff; diff --git a/src/hardware/vga_xga.cpp b/src/hardware/vga_xga.cpp index a8bc4d3..58470ab 100644 --- a/src/hardware/vga_xga.cpp +++ b/src/hardware/vga_xga.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -352,11 +352,11 @@ void XGA_DrawLineBresenham(Bitu val) { // Probably a lot easier way to do this, but this works. dminor = (Bits)((Bit16s)xga.desty); - if(xga.desty&0x2000) dminor |= 0xffffe000; + if(xga.desty&0x2000) dminor |= ~0x1fff; dminor >>= 1; destxtmp=(Bits)((Bit16s)xga.destx); - if(xga.destx&0x2000) destxtmp |= 0xffffe000; + if(xga.destx&0x2000) destxtmp |= ~0x1fff; dmajor = -(destxtmp - (dminor << 1)) >> 1; @@ -374,7 +374,7 @@ void XGA_DrawLineBresenham(Bitu val) { sy = -1; } e = (Bits)((Bit16s)xga.ErrTerm); - if(xga.ErrTerm&0x2000) e |= 0xffffe000; + if(xga.ErrTerm&0x2000) e |= ~0x1fff; xat = xga.curx; yat = xga.cury; diff --git a/src/ints/bios.cpp b/src/ints/bios.cpp index 4fdbec5..48bc53d 100644 --- a/src/ints/bios.cpp +++ b/src/ints/bios.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -32,7 +32,12 @@ #include "setup.h" #include "serialport.h" #include + +#if defined(DB_HAVE_CLOCK_GETTIME) && ! defined(WIN32) +//time.h is already included +#else #include +#endif #ifdef HW_RVL #include @@ -492,7 +497,6 @@ static Bitu INT11_Handler(void) { #endif static void BIOS_HostTimeSync() { - /* Setup time and date */ #ifdef HW_RVL time_t rawtime; time(&rawtime); @@ -511,12 +515,23 @@ static void BIOS_HostTimeSync() { loctime->tm_sec*1000+ millitm))*(((double)PIT_TICK_RATE/65536.0)/1000.0)); #else + Bit32u milli = 0; +#if defined(DB_HAVE_CLOCK_GETTIME) && ! defined(WIN32) + struct timespec tp; + clock_gettime(CLOCK_REALTIME,&tp); + + struct tm *loctime; + loctime = localtime(&tp.tv_sec); + milli = (Bit32u) (tp.tv_nsec / 1000000); +#else + /* Setup time and date */ struct timeb timebuffer; ftime(&timebuffer); struct tm *loctime; loctime = localtime (&timebuffer.time); - + milli = (Bit32u) timebuffer.millitm; +#endif /* loctime->tm_hour = 23; loctime->tm_min = 59; @@ -534,7 +549,7 @@ static void BIOS_HostTimeSync() { loctime->tm_hour*3600*1000+ loctime->tm_min*60*1000+ loctime->tm_sec*1000+ - timebuffer.millitm))*(((double)PIT_TICK_RATE/65536.0)/1000.0)); + milli))*(((double)PIT_TICK_RATE/65536.0)/1000.0)); #endif mem_writed(BIOS_TIMER,ticks); } @@ -599,10 +614,6 @@ static Bitu INT17_Handler(void) { case 0x02: /* PRINTER: Get Status */ reg_ah=0; break; - case 0x20: /* Some sort of printerdriver install check*/ - break; - default: - E_Exit("Unhandled INT 17 call %2X",reg_ah); }; return CBRET_NONE; } @@ -732,9 +743,6 @@ static Bitu INT14_Handler(void) { static Bitu INT15_Handler(void) { static Bit16u biosConfigSeg=0; switch (reg_ah) { - case 0x06: - LOG(LOG_BIOS,LOG_NORMAL)("INT15 Unkown Function 6"); - break; case 0xC0: /* Get Configuration*/ { if (biosConfigSeg==0) biosConfigSeg = DOS_GetMemory(1); //We have 16 bytes @@ -920,6 +928,12 @@ static Bitu INT15_Handler(void) { reg_bx=0x00aa; // mouse // fall through case 0x05: // initialize + if ((reg_al==0x05) && (reg_bh!=0x03)) { + // non-standard data packet sizes not supported + CALLBACK_SCF(true); + reg_ah=2; + break; + } Mouse_SetPS2State(false); CALLBACK_SCF(false); reg_ah=0; @@ -974,6 +988,28 @@ static Bitu INT15_Handler(void) { return CBRET_NONE; } +static Bitu Default_IRQ_Handler(void) { + IO_WriteB(0x20,0x0b); + Bit8u master_isr=IO_ReadB(0x20); + if (master_isr) { + IO_WriteB(0xa0,0x0b); + Bit8u slave_isr=IO_ReadB(0xa0); + if (slave_isr) { + IO_WriteB(0xa1,IO_ReadB(0xa1)|slave_isr); + IO_WriteB(0xa0,0x20); + } else IO_WriteB(0x21,IO_ReadB(0x21)|(master_isr&~4)); + IO_WriteB(0x20,0x20); +#if C_DEBUG + Bit16u irq=0,isr=master_isr; + if (slave_isr) isr=slave_isr<<8; + while (isr>>=1) irq++; + LOG(LOG_BIOS,LOG_WARN)("Unexpected IRQ %u",irq); +#endif + } else master_isr=0xff; + mem_writeb(BIOS_LAST_UNEXPECTED_IRQ,master_isr); + return CBRET_NONE; +} + static Bitu Reboot_Handler(void) { // switch to text mode, notify user (let's hope INT10 still works) const char* const text = "\n\n Reboot requested, quitting now."; @@ -1110,6 +1146,21 @@ public: CALLBACK_Setup(call_irq2,NULL,CB_IRET_EOI_PIC1,Real2Phys(BIOS_DEFAULT_IRQ2_LOCATION),"irq 2 bios"); RealSetVec(0x0a,BIOS_DEFAULT_IRQ2_LOCATION); + /* Default IRQ handler */ + Bitu call_irq_default=CALLBACK_Allocate(); + CALLBACK_Setup(call_irq_default,&Default_IRQ_Handler,CB_IRET,"irq default"); + RealSetVec(0x0b,CALLBACK_RealPointer(call_irq_default)); // IRQ 3 + RealSetVec(0x0c,CALLBACK_RealPointer(call_irq_default)); // IRQ 4 + RealSetVec(0x0d,CALLBACK_RealPointer(call_irq_default)); // IRQ 5 + RealSetVec(0x0f,CALLBACK_RealPointer(call_irq_default)); // IRQ 7 + RealSetVec(0x72,CALLBACK_RealPointer(call_irq_default)); // IRQ 10 + RealSetVec(0x73,CALLBACK_RealPointer(call_irq_default)); // IRQ 11 + + // INT 05h: Print Screen + // IRQ1 handler calls it when PrtSc key is pressed; does nothing unless hooked + phys_writeb(Real2Phys(BIOS_DEFAULT_INT5_LOCATION),0xcf); + RealSetVec(0x05,BIOS_DEFAULT_INT5_LOCATION); + /* Some hardcoded vectors */ phys_writeb(Real2Phys(BIOS_DEFAULT_HANDLER_LOCATION),0xcf); /* bios default interrupt vector location -> IRET */ phys_writew(Real2Phys(RealGetVec(0x12))+0x12,0x20); //Hack for Jurresic @@ -1256,9 +1307,12 @@ public: } // PS2 mouse config |= 0x04; + // DMA *not* supported - Ancient Art of War CGA uses this to identify PCjr + if (machine==MCH_PCJR) config |= 0x100; // Gameport config |= 0x1000; mem_writew(BIOS_CONFIGURATION,config); + if (IS_EGAVGA_ARCH) config &= ~0x30; //EGA/VGA startup display mode differs in CMOS CMOS_SetRegister(0x14,(Bit8u)(config&0xff)); //Should be updated on changes /* Setup extended memory size */ IO_Write(0x70,0x30); @@ -1313,6 +1367,7 @@ void BIOS_SetComPorts(Bit16u baseaddr[]) { equipmentword &= (~0x0E00); equipmentword |= (portcount << 9); mem_writew(BIOS_CONFIGURATION,equipmentword); + if (IS_EGAVGA_ARCH) equipmentword &= ~0x30; //EGA/VGA startup display mode differs in CMOS CMOS_SetRegister(0x14,(Bit8u)(equipmentword&0xff)); //Should be updated on changes } diff --git a/src/ints/bios_disk.cpp b/src/ints/bios_disk.cpp index 952987e..bb12bb9 100644 --- a/src/ints/bios_disk.cpp +++ b/src/ints/bios_disk.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,22 +11,23 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dosbox.h" #include "callback.h" #include "bios.h" +#include "bios_disk.h" #include "regs.h" #include "mem.h" #include "dos_inc.h" /* for Drives[] */ #include "../dos/drives.h" #include "mapper.h" -#define MAX_DISK_IMAGES 4 + diskGeo DiskGeometryList[] = { { 160, 8, 1, 40, 0}, @@ -57,7 +58,7 @@ void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word /* 2 floppys and 2 harddrives, max */ imageDisk *imageDiskList[MAX_DISK_IMAGES]; imageDisk *diskSwap[MAX_SWAPPABLE_DISKS]; -Bits swapPosition; +Bit32s swapPosition; void updateDPT(void) { Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; @@ -100,9 +101,9 @@ void incrementFDD(void) { void swapInDisks(void) { bool allNull = true; - Bits diskcount = 0; - Bits swapPos = swapPosition; - int i; + Bit32s diskcount = 0; + Bit32s swapPos = swapPosition; + Bit32s i; /* Check to make sure that there is at least one setup image */ for(i=0;i0)?0x00:0x05); } -imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) { +imageDisk::imageDisk(FILE *imgFile, const char *imgName, Bit32u imgSizeK, bool isHardDisk) { heads = 0; cylinders = 0; sectors = 0; sector_size = 512; current_fpos = 0; + last_action = NONE; diskimg = imgFile; fseek(diskimg,0,SEEK_SET); - memset(diskname,0,512); - if(strlen((const char *)imgName) > 511) { - memcpy(diskname, imgName, 511); - } else { - strcpy((char *)diskname, (const char *)imgName); - } - + safe_strncpy(diskname, imgName, sizeof(diskname)); active = false; hardDrive = isHardDisk; if(!isHardDisk) { @@ -262,7 +260,7 @@ Bit32u imageDisk::getSectSize(void) { return sector_size; } -static Bitu GetDosDriveNumber(Bitu biosNum) { +static Bit8u GetDosDriveNumber(Bit8u biosNum) { switch(biosNum) { case 0x0: return 0x0; @@ -281,7 +279,7 @@ static Bitu GetDosDriveNumber(Bitu biosNum) { } } -static bool driveInactive(Bitu driveNum) { +static bool driveInactive(Bit8u driveNum) { if(driveNum>=(2 + MAX_HDD_IMAGES)) { LOG(LOG_BIOS,LOG_ERROR)("Disk %d non-existant", driveNum); last_status = 0x01; @@ -307,7 +305,7 @@ static bool driveInactive(Bitu driveNum) { static Bitu INT13_DiskHandler(void) { Bit16u segat, bufptr; Bit8u sectbuf[512]; - Bitu drivenum; + Bit8u drivenum; Bitu i,t; last_drive = reg_dl; drivenum = GetDosDriveNumber(reg_dl); @@ -321,6 +319,8 @@ static Bitu INT13_DiskHandler(void) { //drivenum = 0; //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum); + + // NOTE: the 0xff error code returned in some cases is questionable; 0x01 seems more correct switch(reg_ah) { case 0x0: /* Reset disk */ { @@ -333,14 +333,14 @@ static Bitu INT13_DiskHandler(void) { if ((machine==MCH_CGA) || (machine==MCH_PCJR)) { /* those bioses call floppy drive reset for invalid drive values */ if (((imageDiskList[0]) && (imageDiskList[0]->active)) || ((imageDiskList[1]) && (imageDiskList[1]->active))) { - if (reg_dl<0x80) reg_ip++; + if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++; last_status = 0x00; CALLBACK_SCF(false); } } return CBRET_NONE; } - if (reg_dl<0x80) reg_ip++; + if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++; last_status = 0x00; CALLBACK_SCF(false); } @@ -362,8 +362,19 @@ static Bitu INT13_DiskHandler(void) { return CBRET_NONE; } if (!any_images) { - // Inherit the Earth cdrom (uses it as disk test) + if (drivenum >= DOS_DRIVES || !Drives[drivenum] || Drives[drivenum]->isRemovable()) { + reg_ah = 0x01; + CALLBACK_SCF(true); + return CBRET_NONE; + } + // Inherit the Earth cdrom and Amberstar use it as a disk test if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) { + if (reg_ch==0) { + PhysPt ptr = PhysMake(SegValue(es),reg_bx); + // write some MBR data into buffer for Amberstar installer + mem_writeb(ptr+0x1be,0x80); // first partition is active + mem_writeb(ptr+0x1c2,0x06); // first partition is FAT16B + } reg_ah = 0; CALLBACK_SCF(false); return CBRET_NONE; @@ -425,7 +436,10 @@ static Bitu INT13_DiskHandler(void) { CALLBACK_SCF(true); return CBRET_NONE; } - if(driveInactive(drivenum)) return CBRET_NONE; + if(driveInactive(drivenum)) { + reg_ah = last_status; + return CBRET_NONE; + } /* TODO: Finish coding this section */ /* @@ -449,6 +463,15 @@ static Bitu INT13_DiskHandler(void) { //reg_al = 0x00; /* CRC verify succeeded */ CALLBACK_SCF(false); + break; + case 0x05: /* Format track */ + if (driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + reg_ah = 0x00; + CALLBACK_SCF(false); break; case 0x08: /* Get drive parameters */ if(driveInactive(drivenum)) { @@ -484,6 +507,48 @@ static Bitu INT13_DiskHandler(void) { reg_ah = 0x00; CALLBACK_SCF(false); break; + case 0x15: /* Get disk type */ + /* Korean Powerdolls uses this to detect harddrives */ + LOG(LOG_BIOS,LOG_WARN)("INT13: Get disktype used!"); + if (any_images) { + if(driveInactive(drivenum)) { + last_status = 0x07; + reg_ah = last_status; + CALLBACK_SCF(true); + return CBRET_NONE; + } + Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; + imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); + Bit64u largesize = tmpheads*tmpcyl*tmpsect*tmpsize; + largesize/=512; + Bit32u ts = static_cast(largesize); + reg_ah = (drivenum <2)?1:3; //With 2 for floppy MSDOS starts calling int 13 ah 16 + if(reg_ah == 3) { + reg_cx = static_cast(ts >>16); + reg_dx = static_cast(ts & 0xffff); + } + CALLBACK_SCF(false); + } else { + if (drivenum >10].handle=NULL_HANDLE; emm_segmentmappings[segment>>10].page=NULL_PAGE; } - for (Bitu i=0;i<4;i++) + for (Bitu i=0;i<4;i++) PAGING_MapPage(segment*16/4096+i,segment*16/4096+i); PAGING_ClearTLB(); return EMM_NO_ERROR; } /* Check for valid handle */ if (!ValidHandle(handle)) return EMM_INVALID_HANDLE; - + if (log_page=0) && (tphysPage>10].handle=handle; emm_segmentmappings[segment>>10].page=log_page; } - + MemHandle memh=MEM_NextHandleAt(emm_handles[handle].mem,log_page*4);; for (Bitu i=0;i<4;i++) { PAGING_MapPage(segment*16/4096+i,memh); @@ -697,7 +697,7 @@ static Bitu INT67_Handler(void) { Bitu i; switch (reg_ah) { case 0x40: /* Get Status */ - reg_ah=EMM_NO_ERROR; + reg_ah=EMM_NO_ERROR; break; case 0x41: /* Get PageFrame Segment */ reg_bx=EMM_PAGEFRAME; @@ -754,7 +754,7 @@ static Bitu INT67_Handler(void) { MEM_BlockWrite(SegPhys(es)+reg_di,emm_mappings,sizeof(emm_mappings)); MEM_BlockRead(SegPhys(ds)+reg_si,emm_mappings,sizeof(emm_mappings)); reg_ah=EMM_RestoreMappingTable(); - break; + break; case 0x03: /* Get Page Map Array Size */ reg_al=sizeof(emm_mappings); reg_ah=EMM_NO_ERROR; @@ -780,7 +780,7 @@ static Bitu INT67_Handler(void) { if (reg_ah!=EMM_NO_ERROR) break; }; } break; - case 0x01: // use segment address + case 0x01: // use segment address { PhysPt data = SegPhys(ds)+reg_si; for (int i=0; i>16; @@ -981,8 +981,8 @@ static Bitu INT67_Handler(void) { /* Load tables and initialize segment registers */ CPU_LGDT(new_gdt_limit, new_gdt_base); CPU_LIDT(new_idt_limit, new_idt_base); - if (CPU_LLDT(new_ldt)) LOG_MSG("VCPI:Could not load LDT with %x",new_ldt); - if (CPU_LTR(new_tr)) LOG_MSG("VCPI:Could not load TR with %x",new_tr); + if (CPU_LLDT(new_ldt)) LOG_MSG("VCPI: Could not load LDT with %x",new_ldt); + if (CPU_LTR(new_tr)) LOG_MSG("VCPI: Could not load TR with %x",new_tr); CPU_SetSegGeneral(ds,0); CPU_SetSegGeneral(es,0); @@ -1050,8 +1050,8 @@ static Bitu VCPI_PM_Handler() { /* Load descriptor table registers */ CPU_LGDT(0xff, vcpi.private_area+0x0000); CPU_LIDT(0x7ff, vcpi.private_area+0x2000); - if (CPU_LLDT(0x08)) LOG_MSG("VCPI:Could not load LDT"); - if (CPU_LTR(0x10)) LOG_MSG("VCPI:Could not load TR"); + if (CPU_LLDT(0x08)) LOG_MSG("VCPI: Could not load LDT"); + if (CPU_LTR(0x10)) LOG_MSG("VCPI: Could not load TR"); reg_flags&=(~FLAG_NT); reg_esp+=8; // skip interrupt return information @@ -1322,7 +1322,7 @@ public: vcpi.enabled=false; GEMMIS_seg=0; - + Section_prop * section=static_cast(configuration); ems_type=GetEMSType(section); if (ems_type<=0) return; @@ -1365,7 +1365,7 @@ public: emm_segmentmappings[i].handle=NULL_HANDLE; } - EMM_AllocateSystemHandle(8); // allocate OS-dedicated handle (ems handle zero, 128kb) + EMM_AllocateSystemHandle(24); // allocate OS-dedicated handle (ems handle zero, 384kb) if (ems_type==3) { DMA_SetWrapping(0xffffffff); // emm386-bug that disables dma wrapping @@ -1399,8 +1399,8 @@ public: CPU_SET_CRX(0, 1); CPU_LGDT(0xff, vcpi.private_area+0x0000); CPU_LIDT(0x7ff, vcpi.private_area+0x2000); - if (CPU_LLDT(0x08)) LOG_MSG("VCPI:Could not load LDT"); - if (CPU_LTR(0x10)) LOG_MSG("VCPI:Could not load TR"); + if (CPU_LLDT(0x08)) LOG_MSG("VCPI: Could not load LDT"); + if (CPU_LTR(0x10)) LOG_MSG("VCPI: Could not load TR"); CPU_Push32(SegValue(gs)); CPU_Push32(SegValue(fs)); @@ -1416,13 +1416,13 @@ public: } } } - + ~EMS() { if (ems_type<=0) return; /* Undo Biosclearing */ BIOS_ZeroExtendedSize(false); - + /* Remove ems device */ if (emm_device!=NULL) { DOS_DelDevice(emm_device); @@ -1455,11 +1455,11 @@ public: } } }; - + static EMS* test; void EMS_ShutDown(Section* /*sec*/) { - delete test; + delete test; } void EMS_Init(Section* sec) { diff --git a/src/ints/int10.cpp b/src/ints/int10.cpp index 0aa9670..a5cfa79 100644 --- a/src/ints/int10.cpp +++ b/src/ints/int10.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -23,6 +23,7 @@ #include "regs.h" #include "inout.h" #include "int10.h" +#include "mouse.h" #include "setup.h" Int10Data int10; @@ -47,10 +48,13 @@ static Bitu INT10_Handler(void) { break; } #endif + INT10_SetCurMode(); switch (reg_ah) { case 0x00: /* Set VideoMode */ + Mouse_BeforeNewVideoMode(true); INT10_SetVideoMode(reg_al); + Mouse_AfterNewVideoMode(true); break; case 0x01: /* Set TextMode Cursor Shape */ INT10_SetCursorShape(reg_ch,reg_cl); @@ -202,19 +206,20 @@ static Bitu INT10_Handler(void) { case 0x11: /* Character generator functions */ if (!IS_EGAVGA_ARCH) break; + if ((reg_al&0xf0)==0x10) Mouse_BeforeNewVideoMode(false); switch (reg_al) { /* Textmode calls */ case 0x00: /* Load user font */ case 0x10: - INT10_LoadFont(SegPhys(es)+reg_bp,reg_al==0x10,reg_cx,reg_dx,reg_bl,reg_bh); + INT10_LoadFont(SegPhys(es)+reg_bp,reg_al==0x10,reg_cx,reg_dx,reg_bl&0x7f,reg_bh); break; case 0x01: /* Load 8x14 font */ case 0x11: - INT10_LoadFont(Real2Phys(int10.rom.font_14),reg_al==0x11,256,0,reg_bl,14); + INT10_LoadFont(Real2Phys(int10.rom.font_14),reg_al==0x11,256,0,reg_bl&0x7f,14); break; case 0x02: /* Load 8x8 font */ case 0x12: - INT10_LoadFont(Real2Phys(int10.rom.font_8_first),reg_al==0x12,256,0,reg_bl,8); + INT10_LoadFont(Real2Phys(int10.rom.font_8_first),reg_al==0x12,256,0,reg_bl&0x7f,8); break; case 0x03: /* Set Block Specifier */ IO_Write(0x3c4,0x3);IO_Write(0x3c5,reg_bl); @@ -222,7 +227,7 @@ static Bitu INT10_Handler(void) { case 0x04: /* Load 8x16 font */ case 0x14: if (!IS_VGA_ARCH) break; - INT10_LoadFont(Real2Phys(int10.rom.font_16),reg_al==0x14,256,0,reg_bl,16); + INT10_LoadFont(Real2Phys(int10.rom.font_16),reg_al==0x14,256,0,reg_bl&0x7f,16); break; /* Graphics mode calls */ case 0x20: /* Set User 8x8 Graphics characters */ @@ -284,7 +289,6 @@ graphics_chars: reg_bp=RealOff(int10.rom.font_8_second); break; case 0x05: /* alpha alternate 9x14 */ - if (!IS_VGA_ARCH) break; SegSet16(es,RealSeg(int10.rom.font_14_alternate)); reg_bp=RealOff(int10.rom.font_14_alternate); break; @@ -311,6 +315,7 @@ graphics_chars: LOG(LOG_INT10,LOG_ERROR)("Function 11:Unsupported character generator call %2X",reg_al); break; } + if ((reg_al&0xf0)==0x10) Mouse_AfterNewVideoMode(false); break; case 0x12: /* alternate function select */ if (!IS_EGAVGA_ARCH) @@ -536,8 +541,10 @@ graphics_chars: reg_ah=VESA_GetSVGAModeInformation(reg_cx,SegValue(es),reg_di); break; case 0x02: /* Set videomode */ + Mouse_BeforeNewVideoMode(true); reg_al=0x4f; reg_ah=VESA_SetSVGAMode(reg_bx); + Mouse_AfterNewVideoMode(true); break; case 0x03: /* Get videomode */ reg_al=0x4f; @@ -585,10 +592,10 @@ graphics_chars: break; case 0x07: switch (reg_bl) { - case 0x80: /* Set Display Start during retrace ?? */ + case 0x80: /* Set Display Start during retrace */ case 0x00: /* Set display Start */ reg_al=0x4f; - reg_ah=VESA_SetDisplayStart(reg_cx,reg_dx); + reg_ah=VESA_SetDisplayStart(reg_cx,reg_dx,reg_bl==0x80); break; case 0x01: reg_al=0x4f; @@ -604,9 +611,8 @@ graphics_chars: case 0x09: switch (reg_bl) { case 0x80: /* Set Palette during retrace */ - //TODO case 0x00: /* Set Palette */ - reg_ah=VESA_SetPalette(SegPhys(es)+reg_di,reg_dx,reg_cx); + reg_ah=VESA_SetPalette(SegPhys(es)+reg_di,reg_dx,reg_cx,reg_bl==0x80); reg_al=0x4f; break; case 0x01: /* Get Palette */ @@ -626,27 +632,27 @@ graphics_chars: } switch (reg_bl) { case 0x00: - reg_edi=RealOff(int10.rom.pmode_interface); SegSet16(es,RealSeg(int10.rom.pmode_interface)); + reg_di=RealOff(int10.rom.pmode_interface); reg_cx=int10.rom.pmode_interface_size; reg_ax=0x004f; break; case 0x01: /* Get code for "set window" */ - reg_edi=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_window; SegSet16(es,RealSeg(int10.rom.pmode_interface)); - reg_cx=0x10; //0x10 should be enough for the callbacks + reg_di=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_window; + reg_cx=int10.rom.pmode_interface_start-int10.rom.pmode_interface_window; reg_ax=0x004f; break; case 0x02: /* Get code for "set display start" */ - reg_edi=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_start; SegSet16(es,RealSeg(int10.rom.pmode_interface)); - reg_cx=0x10; //0x10 should be enough for the callbacks + reg_di=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_start; + reg_cx=int10.rom.pmode_interface_palette-int10.rom.pmode_interface_start; reg_ax=0x004f; break; case 0x03: /* Get code for "set palette" */ - reg_edi=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_palette; SegSet16(es,RealSeg(int10.rom.pmode_interface)); - reg_cx=0x10; //0x10 should be enough for the callbacks + reg_di=RealOff(int10.rom.pmode_interface)+int10.rom.pmode_interface_palette; + reg_cx=int10.rom.pmode_interface_size-int10.rom.pmode_interface_palette; reg_ax=0x004f; break; default: @@ -708,15 +714,24 @@ static void INT10_Seg40Init(void) { real_writeb(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,0x51); // Set the default MSR real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x09); + // Set the pointer to video save pointer table + real_writed(BIOSMEM_SEG,BIOSMEM_VS_POINTER,int10.rom.video_save_pointers); } static void INT10_InitVGA(void) { -/* switch to color mode and enable CPU access 480 lines */ - IO_Write(0x3c2,0xc3); - /* More than 64k */ - IO_Write(0x3c4,0x04); - IO_Write(0x3c5,0x02); + if (IS_EGAVGA_ARCH) { + /* switch to color mode and enable CPU access 480 lines */ + IO_Write(0x3c2,0xc3); + /* More than 64k */ + IO_Write(0x3c4,0x04); + IO_Write(0x3c5,0x02); + if (IS_VGA_ARCH) { + /* Initialize DAC */ + IO_Write(0x3c8,0); + for (Bitu i=0;i<3*256;i++) IO_Write(0x3c9,0); + } + } } static void SetupTandyBios(void) { @@ -750,7 +765,5 @@ void INT10_Init(Section* /*sec*/) { //Init the 0x40 segment and init the datastructures in the the video rom area INT10_SetupRomMemory(); INT10_Seg40Init(); - INT10_SetupVESA(); - INT10_SetupRomMemoryChecksum();//SetupVesa modifies the rom as well. INT10_SetVideoMode(0x3); } diff --git a/src/ints/int10.h b/src/ints/int10.h index 41e4374..2ba402e 100644 --- a/src/ints/int10.h +++ b/src/ints/int10.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -99,6 +99,8 @@ extern Bit8u int10_font_08[256 * 8]; extern Bit8u int10_font_14[256 * 14]; extern Bit8u int10_font_16[256 * 16]; +extern Bit8u int10_font_14_alternate[20 * 15 + 1]; +extern Bit8u int10_font_16_alternate[19 * 17 + 1]; struct VideoModeBlock { Bit16u mode; @@ -131,6 +133,8 @@ typedef struct { RealPt video_dcc_table; RealPt oemstring; RealPt vesa_modes; + RealPt wait_retrace; + RealPt set_window; RealPt pmode_interface; Bit16u pmode_interface_size; Bit16u pmode_interface_start; @@ -145,15 +149,16 @@ typedef struct { extern Int10Data int10; -static Bit8u CURSOR_POS_COL(Bit8u page) { +static inline Bit8u CURSOR_POS_COL(Bit8u page) { return real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2); } -static Bit8u CURSOR_POS_ROW(Bit8u page) { +static inline Bit8u CURSOR_POS_ROW(Bit8u page) { return real_readb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2+1); } bool INT10_SetVideoMode(Bit16u mode); +void INT10_SetCurMode(void); void INT10_ScrollWindow(Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8s nlines,Bit8u attr,Bit8u page); @@ -205,9 +210,9 @@ Bit8u VESA_GetSVGAMode(Bit16u & mode); Bit8u VESA_SetCPUWindow(Bit8u window,Bit8u address); Bit8u VESA_GetCPUWindow(Bit8u window,Bit16u & address); Bit8u VESA_ScanLineLength(Bit8u subcall, Bit16u val, Bit16u & bytes,Bit16u & pixels,Bit16u & lines); -Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y); +Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y,bool wait); Bit8u VESA_GetDisplayStart(Bit16u & x,Bit16u & y); -Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count); +Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count,bool wait); Bit8u VESA_GetPalette(PhysPt data,Bitu index,Bitu count); /* Sub Groups */ diff --git a/src/ints/int10_char.cpp b/src/ints/int10_char.cpp index cf1d704..084f250 100644 --- a/src/ints/int10_char.cpp +++ b/src/ints/int10_char.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -24,6 +24,8 @@ #include "mem.h" #include "inout.h" #include "int10.h" +#include "pic.h" +#include "callback.h" static void CGA2_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) { Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); @@ -52,14 +54,12 @@ static void CGA4_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt b static void TANDY16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) { Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); - PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/4)+cleft)*4; - PhysPt src=base+((CurMode->twidth*rold)*(cheight/4)+cleft)*4; + Bit8u banks=CurMode->twidth/10; + PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/banks)+cleft)*4; + PhysPt src=base+((CurMode->twidth*rold)*(cheight/banks)+cleft)*4; Bitu copy=(cright-cleft)*4;Bitu nextline=CurMode->twidth*4; - for (Bitu i=0;i(cheight/banks);i++) { + for (Bitu b=0;btwidth*row)*(cheight/4)+cleft)*4; + Bit8u banks=CurMode->twidth/10; + PhysPt dest=base+((CurMode->twidth*row)*(cheight/banks)+cleft)*4; Bitu copy=(cright-cleft)*4;Bitu nextline=CurMode->twidth*4; attr=(attr & 0xf) | (attr & 0xf) << 4; - for (Bitu i=0;i(cheight/banks);i++) { for (Bitu x=0;xtwidth*row)*cheight+cleft; @@ -201,9 +201,24 @@ void INT10_ScrollWindow(Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8s nlines,Bit if(clr>=ncols) clr=(Bit8u)ncols-1; clr++; - /* Get the correct page */ - if(page==0xFF) page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); - PhysPt base=CurMode->pstart+page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE); + /* Get the correct page: current start address for current page (0xFF), + otherwise calculate from page number and page size */ + PhysPt base=CurMode->pstart; + if (page==0xff) base+=real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START); + else base+=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE); + + if (GCC_UNLIKELY(machine==MCH_PCJR)) { + if (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9) { + // PCJr cannot handle these modes at 0xb800 + // See INT10_PutPixel M_TANDY16 + Bitu cpupage = + (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7; + + base = cpupage << 14; + if (page!=0xff) + base += page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE); + } + } /* See how much lines need to be copied */ Bit8u start,end;Bits next; @@ -390,7 +405,7 @@ void INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) { void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result) { /* Externally used by the mouse routine */ PhysPt fontdata; - Bitu x,y; + Bit16u cols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); bool split_chr = false; switch (CurMode->type) { @@ -398,7 +413,7 @@ void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result) { { // Compute the address Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE); - address+=(row*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)+col)*2; + address+=(row*cols+col)*2; // read the char PhysPt where = CurMode->pstart+address; *result=mem_readw(where); @@ -408,45 +423,61 @@ void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result) { case M_CGA2: case M_TANDY16: split_chr = true; - /* Fallthrough */ - default: /* EGA/VGA don't have a split font-table */ - for(Bit16u chr=0;chr <= 255 ;chr++) { - if (!split_chr || (chr<128)) fontdata = Real2Phys(RealGetVec(0x43))+chr*cheight; - else fontdata = Real2Phys(RealGetVec(0x1F))+(chr-128)*cheight; - - x=8*col; - y=cheight*row; - bool error=false; - for (Bit8u h=0;h>=1; - } - y++; - if(bitline != vidline){ - /* It's not character 'chr', move on to the next */ - error = true; - break; - } - } - if(!error){ - /* We found it */ - *result = chr; - return; - } + switch (machine) { + case MCH_CGA: + case MCH_HERC: + fontdata=PhysMake(0xf000,0xfa6e); + break; + case TANDY_ARCH_CASE: + fontdata=Real2Phys(RealGetVec(0x44)); + break; + default: + fontdata=Real2Phys(RealGetVec(0x43)); + break; } - LOG(LOG_INT10,LOG_ERROR)("ReadChar didn't find character"); - *result = 0; + break; + default: + fontdata=Real2Phys(RealGetVec(0x43)); break; } + + Bitu x=col*8,y=row*cheight*(cols/CurMode->twidth); + + for (Bit16u chr=0;chr<256;chr++) { + + if (chr==128 && split_chr) fontdata=Real2Phys(RealGetVec(0x1f)); + + bool error=false; + Bit16u ty=(Bit16u)y; + for (Bit8u h=0;h>=1; + } + ty++; + if(bitline != vidline){ + /* It's not character 'chr', move on to the next */ + fontdata+=(cheight-h-1); + error = true; + break; + } + } + if(!error){ + /* We found it */ + *result = chr; + return; + } + } + LOG(LOG_INT10,LOG_ERROR)("ReadChar didn't find character"); + *result = 0; } void INT10_ReadCharAttr(Bit16u * result,Bit8u page) { if(page==0xFF) page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); @@ -456,39 +487,47 @@ void INT10_ReadCharAttr(Bit16u * result,Bit8u page) { } void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit8u chr,Bit8u attr,bool useattr) { /* Externally used by the mouse routine */ - RealPt fontdata; - Bitu x,y; - Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); + PhysPt fontdata; + Bit16u cols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); + Bit8u back,cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); switch (CurMode->type) { case M_TEXT: { // Compute the address Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE); - address+=(row*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)+col)*2; + address+=(row*cols+col)*2; // Write the char PhysPt where = CurMode->pstart+address; mem_writeb(where,chr); - if (useattr) { - mem_writeb(where+1,attr); - } + if (useattr) mem_writeb(where+1,attr); } return; case M_CGA4: case M_CGA2: case M_TANDY16: - if (chr<128) - fontdata=RealGetVec(0x43); - else { + if (chr>=128) { chr-=128; - fontdata=RealGetVec(0x1f); + fontdata=Real2Phys(RealGetVec(0x1f)); + break; + } + switch (machine) { + case MCH_CGA: + case MCH_HERC: + fontdata=PhysMake(0xf000,0xfa6e); + break; + case TANDY_ARCH_CASE: + fontdata=Real2Phys(RealGetVec(0x44)); + break; + default: + fontdata=Real2Phys(RealGetVec(0x43)); + break; } - fontdata=RealMake(RealSeg(fontdata), RealOff(fontdata) + chr*cheight); break; default: - fontdata=RealGetVec(0x43); - fontdata=RealMake(RealSeg(fontdata), RealOff(fontdata) + chr*cheight); + fontdata=Real2Phys(RealGetVec(0x43)); break; } + fontdata+=chr*cheight; if(GCC_UNLIKELY(!useattr)) { //Set attribute(color) to a sensible value static bool warned_use = false; @@ -511,50 +550,71 @@ void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit8u chr,Bit8u attr,bool useatt } } - //Some weird behavior of mode 6 (and 11) - if ((CurMode->mode == 0x6)/* || (CurMode->mode==0x11)*/) attr = (attr&0x80)|1; - //(same fix for 11 fixes vgatest2, but it's not entirely correct according to wd) + //Attribute behavior of mode 6; mode 11 does something similar but + //it is in INT 10h handler because it only applies to function 09h + if (CurMode->mode==0x06) attr=(attr&0x80)|1; - x=8*col; - y=cheight*row;Bit8u xor_mask=(CurMode->type == M_VGA) ? 0x0 : 0x80; - //TODO Check for out of bounds - if (CurMode->type==M_EGA) { + switch (CurMode->type) { + case M_VGA: + case M_LIN8: + // 256-color modes have background color instead of page + back=page; + page=0; + break; + case M_EGA: /* enable all planes for EGA modes (Ultima 1 colour bug) */ /* might be put into INT10_PutPixel but different vga bios implementations have different opinions about this */ IO_Write(0x3c4,0x2);IO_Write(0x3c5,0xf); + // fall-through + default: + back=attr&0x80; + break; } + + Bitu x=col*8,y=row*cheight*(cols/CurMode->twidth); + + Bit16u ty=(Bit16u)y; for (Bit8u h=0;h>=1; } - y++; + ty++; } } void INT10_WriteChar(Bit8u chr,Bit8u attr,Bit8u page,Bit16u count,bool showattr) { + Bit8u pospage=page; if (CurMode->type!=M_TEXT) { showattr=true; //Use attr in graphics mode always switch (machine) { case EGAVGA_ARCH_CASE: - page%=CurMode->ptotal; + switch (CurMode->type) { + case M_VGA: + case M_LIN8: + pospage=0; + break; + default: + page%=CurMode->ptotal; + pospage=page; + break; + } break; case MCH_CGA: case MCH_PCJR: page=0; + pospage=0; break; } } - Bit8u cur_row=CURSOR_POS_ROW(page); - Bit8u cur_col=CURSOR_POS_COL(page); + Bit8u cur_row=CURSOR_POS_ROW(pospage); + Bit8u cur_col=CURSOR_POS_COL(pospage); BIOS_NCOLS; while (count>0) { WriteChar(cur_col,cur_row,page,chr,attr,showattr); @@ -565,6 +625,11 @@ void INT10_WriteChar(Bit8u chr,Bit8u attr,Bit8u page,Bit16u count,bool showattr) cur_row++; } } + + if (CurMode->type==M_EGA) { + // Reset write ops for EGA graphics modes + IO_Write(0x3ce,0x3);IO_Write(0x3cf,0x0); + } } static void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u page) { @@ -572,9 +637,21 @@ static void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u pag Bit8u cur_row=CURSOR_POS_ROW(page); Bit8u cur_col=CURSOR_POS_COL(page); switch (chr) { - case 7: - //TODO BEEP - break; + case 7: /* Beep */ + // Prepare PIT counter 2 for ~900 Hz square wave + IO_Write(0x43,0xb6); + IO_Write(0x42,0x28); + IO_Write(0x42,0x05); + // Speaker on + IO_Write(0x61,IO_Read(0x61)|3); + // Idle for 1/3rd of a second + double start; + start=PIC_FullIndex(); + while ((PIC_FullIndex()-start)<333.0) CALLBACK_Idle(); + // Speaker off + IO_Write(0x61,IO_Read(0x61)&~3); + // No change in position + return; case 8: if(cur_col>0) cur_col--; break; @@ -585,13 +662,6 @@ static void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u pag // cur_col=0; //Seems to break an old chess game cur_row++; break; - case '\t': - do { - INT10_TeletypeOutputAttr(' ',attr,useattr,page); - cur_row=CURSOR_POS_ROW(page); - cur_col=CURSOR_POS_COL(page); - } while(cur_col%8); - break; default: /* Draw the actual Character */ WriteChar(cur_col,cur_row,page,chr,attr,useattr); @@ -603,8 +673,13 @@ static void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u pag } // Do we need to scroll ? if(cur_row==nrows) { - //Fill with black on non-text modes and with 0x7 on textmode - Bit8u fill = (CurMode->type == M_TEXT)?0x7:0; + //Fill with black on non-text modes and with attribute at cursor on textmode + Bit8u fill=0; + if (CurMode->type==M_TEXT) { + Bit16u chat; + INT10_ReadCharAttr(&chat,page); + fill=(Bit8u)(chat>>8); + } INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,fill,page); cur_row--; } diff --git a/src/ints/int10_memory.cpp b/src/ints/int10_memory.cpp index c70ab17..e3c40e2 100644 --- a/src/ints/int10_memory.cpp +++ b/src/ints/int10_memory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -47,55 +47,78 @@ static Bit16u map_offset[8]={ void INT10_LoadFont(PhysPt font,bool reload,Bitu count,Bitu offset,Bitu map,Bitu height) { PhysPt ftwhere=PhysMake(0xa000,map_offset[map & 0x7]+(Bit16u)(offset*32)); - IO_Write(0x3c4,0x2);IO_Write(0x3c5,0x4); //Enable plane 2 - IO_Write(0x3ce,0x6);Bitu old_6=IO_Read(0x3cf); - IO_Write(0x3cf,0x0); //Disable odd/even and a0000 addressing + Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); + bool mono=(base==VGAREG_MDA_CRTC_ADDRESS); + + //Put video adapter in planar mode + IO_Write(0x3c4,0x02);IO_Write(0x3c5,0x04); // select plane 2 for writing + IO_Write(0x3c4,0x04);IO_Write(0x3c5,0x07); // odd/even off in SEQ + IO_Write(0x3ce,0x04);IO_Write(0x3cf,0x02); // select plane 2 for reading + IO_Write(0x3ce,0x05);IO_Write(0x3cf,0x00); // write mode 0, odd/even off in GFX + IO_Write(0x3ce,0x06);IO_Write(0x3cf,0x04); // CPU memory window A0000-AFFFF + + //Load character patterns for (Bitu i=0;isheight/height; - if (machine==MCH_EGA) { - Bitu displayend = rows*height - 1; - IO_Write(base,0x12); - IO_Write(base+1,(Bit8u)(displayend & 0xff)); - IO_Write(base,0x7); - // Note: IBM EGA registers can't be read - Bitu v_overflow = IO_Read(base+1) & ~0x2; - if (displayend & 0x100) v_overflow |= 0x2; - IO_Write(base+1,(Bit8u)v_overflow); + //Vertical display end + Bitu rows=CurMode->sheight/height; + Bitu vdend=rows*height*((CurMode->sheight==200)?2:1)-1; + IO_Write(base,0x12); + IO_Write(base+1,(Bit8u)vdend); + //Underline location + if (CurMode->mode==7) { + IO_Write(base,0x14); + IO_Write(base+1,(IO_Read(base+1) & ~0x1f)|(height-1)); } //Rows setting in bios segment real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,rows-1); real_writeb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,(Bit8u)height); - //TODO Reprogram cursor size? + //Page size + Bitu pagesize=rows*real_readb(BIOSMEM_SEG,BIOSMEM_NB_COLS)*2; + pagesize+=0x100; // bios adds extra on reload + real_writew(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,pagesize); + //Cursor shape + if (height>=14) height--; // move up one line on 14+ line fonts + INT10_SetCursorShape(height-2,height-1); } } void INT10_ReloadFont(void) { + Bitu map=0; switch(CurMode->cheight) { case 8: - INT10_LoadFont(Real2Phys(int10.rom.font_8_first),true,256,0,0,8); + INT10_LoadFont(Real2Phys(int10.rom.font_8_first),false,256,0,map,8); break; case 14: - INT10_LoadFont(Real2Phys(int10.rom.font_14),true,256,0,0,14); + if (IS_VGA_ARCH && svgaCard==SVGA_None && CurMode->mode==7) map=0x80; + INT10_LoadFont(Real2Phys(int10.rom.font_14),false,256,0,map,14); break; case 16: default: - INT10_LoadFont(Real2Phys(int10.rom.font_16),true,256,0,0,16); + if (IS_VGA_ARCH && svgaCard==SVGA_None) map=0x80; + INT10_LoadFont(Real2Phys(int10.rom.font_16),false,256,0,map,16); break; } } @@ -110,14 +133,15 @@ void INT10_SetupRomMemory(void) { // set up the start of the ROM phys_writew(rom_base+0,0xaa55); phys_writeb(rom_base+2,0x40); // Size of ROM: 64 512-blocks = 32KB - if (IS_VGA_ARCH) { - phys_writeb(rom_base+0x1e,0x49); // IBM string - phys_writeb(rom_base+0x1f,0x42); - phys_writeb(rom_base+0x20,0x4d); - phys_writeb(rom_base+0x21,0x00); - } + phys_writeb(rom_base+0x1e,0x49); // IBM string + phys_writeb(rom_base+0x1f,0x42); + phys_writeb(rom_base+0x20,0x4d); + phys_writeb(rom_base+0x21,0x20); int10.rom.used=0x100; } + + if (IS_VGA_ARCH && svgaCard==SVGA_S3Trio) INT10_SetupVESA(); + int10.rom.font_8_first=RealMake(0xC000,int10.rom.used); for (i=0;i<128*8;i++) { phys_writeb(rom_base+int10.rom.used++,int10_font_08[i]); @@ -130,10 +154,18 @@ void INT10_SetupRomMemory(void) { for (i=0;i<256*14;i++) { phys_writeb(rom_base+int10.rom.used++,int10_font_14[i]); } + int10.rom.font_14_alternate=RealMake(0xC000,int10.rom.used); + for (i=0;i<20*15+1;i++) { + phys_writeb(rom_base+int10.rom.used++,int10_font_14_alternate[i]); + } int10.rom.font_16=RealMake(0xC000,int10.rom.used); for (i=0;i<256*16;i++) { phys_writeb(rom_base+int10.rom.used++,int10_font_16[i]); } + int10.rom.font_16_alternate=RealMake(0xC000,int10.rom.used); + for (i=0;i<19*17+1;i++) { + phys_writeb(rom_base+int10.rom.used++,int10_font_16_alternate[i]); + } int10.rom.static_state=RealMake(0xC000,int10.rom.used); for (i=0;i<0x10;i++) { phys_writeb(rom_base+int10.rom.used++,static_functionality[i]); @@ -142,9 +174,6 @@ void INT10_SetupRomMemory(void) { phys_writeb(PhysMake(0xf000,0xfa6e)+i,int10_font_08[i]); } RealSetVec(0x1F,int10.rom.font_8_second); - int10.rom.font_14_alternate=RealMake(0xC000,int10.rom.used); - int10.rom.font_16_alternate=RealMake(0xC000,int10.rom.used); - phys_writeb(rom_base+int10.rom.used++,0x00); // end of table (empty) if (IS_EGAVGA_ARCH) { int10.rom.video_parameter_table=RealMake(0xC000,int10.rom.used); @@ -208,9 +237,10 @@ void INT10_SetupRomMemory(void) { } INT10_SetupBasicVideoParameterTable(); + INT10_SetupRomMemoryChecksum(); if (IS_TANDY_ARCH) { - RealSetVec(0x44,int10.rom.font_8_first); + RealSetVec(0x44,RealMake(0xf000,0xfa6e)); } } @@ -220,11 +250,13 @@ void INT10_ReloadRomFonts(void) { for (Bitu i=0;i<256*16;i++) { phys_writeb(font16pt+i,int10_font_16[i]); } + phys_writeb(Real2Phys(int10.rom.font_16_alternate),0x1d); // 14x8 font PhysPt font14pt=Real2Phys(int10.rom.font_14); for (Bitu i=0;i<256*14;i++) { phys_writeb(font14pt+i,int10_font_14[i]); } + phys_writeb(Real2Phys(int10.rom.font_14_alternate),0x1d); // 8x8 fonts PhysPt font8pt=Real2Phys(int10.rom.font_8_first); for (Bitu i=0;i<128*8;i++) { @@ -234,6 +266,7 @@ void INT10_ReloadRomFonts(void) { for (Bitu i=0;i<128*8;i++) { phys_writeb(font8pt+i,int10_font_08[i+128*8]); } + INT10_SetupRomMemoryChecksum(); } void INT10_SetupRomMemoryChecksum(void) { @@ -1019,8 +1052,8 @@ Bit8u int10_font_16[256 * 16] = { 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, - 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, + 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, @@ -1057,8 +1090,8 @@ Bit8u int10_font_16[256 * 16] = { 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, - 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xd6, 0xd6, + 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, @@ -1115,8 +1148,8 @@ Bit8u int10_font_16[256 * 16] = { 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, - 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, @@ -1129,20 +1162,20 @@ Bit8u int10_font_16[256 * 16] = { 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, - 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, - 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, - 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, + 0xd6, 0xfe, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x38, + 0x7c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, - 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, + 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, @@ -1179,8 +1212,8 @@ Bit8u int10_font_16[256 * 16] = { 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, - 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, + 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, @@ -1197,12 +1230,12 @@ Bit8u int10_font_16[256 * 16] = { 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, - 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, - 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, - 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, + 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, + 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, @@ -1251,8 +1284,8 @@ Bit8u int10_font_16[256 * 16] = { 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, - 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x76, 0x36, + 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, @@ -1271,14 +1304,14 @@ Bit8u int10_font_16[256 * 16] = { 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, - 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x60, + 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, - 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, - 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, + 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, @@ -1304,9 +1337,9 @@ Bit8u int10_font_16[256 * 16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, - 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, - 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, @@ -1474,3 +1507,128 @@ Bit8u int10_font_16[256 * 16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +Bit8u int10_font_14_alternate[20 * 15 + 1] = { + 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, + 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, + 0x00, 0x63, 0x63, 0x63, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x2d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4d, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, + 0x54, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x56, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x57, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, + 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x58, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, + 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, + 0x59, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x5a, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, + 0x30, 0x61, 0xc3, 0xff, 0x00, 0x00, 0x00, + 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, + 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, + 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, + 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, + 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, + 0x91, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, + 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, + 0x9b, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, + 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x9d, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, + 0x18, 0xff, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x9e, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, + 0x6f, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, + 0xf1, + 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0x18, + 0x18, 0x18, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xf6, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00 +}; + +Bit8u int10_font_16_alternate[19 * 17 + 1] = { + 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, + 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, + 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x4d, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x54, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x56, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x57, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, + 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x58, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, + 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x59, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x5a, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, + 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, + 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, + 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, + 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x91, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, + 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x9b, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, + 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x9d, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, + 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x9e, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, + 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0xab, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, + 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0xac, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, + 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00 +}; diff --git a/src/ints/int10_misc.cpp b/src/ints/int10_misc.cpp index 9522a6a..cc4673d 100644 --- a/src/ints/int10_misc.cpp +++ b/src/ints/int10_misc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -154,7 +154,7 @@ RealPt INT10_EGA_RIL_GetVersionPt(void) { return RealMake(0xc000,0x30); } -static void EGA_RIL(Bit16u dx, Bitu& port, Bitu& regs) { +static void EGA_RIL(Bit16u dx, Bit16u& port, Bit16u& regs) { port = 0; regs = 0; //if nul is returned it's a single register port switch(dx) { @@ -193,8 +193,8 @@ static void EGA_RIL(Bit16u dx, Bitu& port, Bitu& regs) { } void INT10_EGA_RIL_ReadRegister(Bit8u & bl, Bit16u dx) { - Bitu port = 0; - Bitu regs = 0; + Bit16u port = 0; + Bit16u regs = 0; EGA_RIL(dx,port,regs); if(regs == 0) { if(port) bl = IO_Read(port); @@ -208,8 +208,8 @@ void INT10_EGA_RIL_ReadRegister(Bit8u & bl, Bit16u dx) { } void INT10_EGA_RIL_WriteRegister(Bit8u & bl, Bit8u bh, Bit16u dx) { - Bitu port = 0; - Bitu regs = 0; + Bit16u port = 0; + Bit16u regs = 0; EGA_RIL(dx,port,regs); if(regs == 0) { if(port) IO_Write(port,bl); @@ -228,8 +228,8 @@ void INT10_EGA_RIL_WriteRegister(Bit8u & bl, Bit8u bh, Bit16u dx) { } void INT10_EGA_RIL_ReadRegisterRange(Bit8u ch, Bit8u cl, Bit16u dx, PhysPt dst) { - Bitu port = 0; - Bitu regs = 0; + Bit16u port = 0; + Bit16u regs = 0; EGA_RIL(dx,port,regs); if(regs == 0) { LOG(LOG_INT10,LOG_ERROR)("EGA RIL range read with port %x called",port); @@ -247,8 +247,8 @@ void INT10_EGA_RIL_ReadRegisterRange(Bit8u ch, Bit8u cl, Bit16u dx, PhysPt dst) } void INT10_EGA_RIL_WriteRegisterRange(Bit8u ch, Bit8u cl, Bit16u dx, PhysPt src) { - Bitu port = 0; - Bitu regs = 0; + Bit16u port = 0; + Bit16u regs = 0; EGA_RIL(dx,port,regs); if(regs == 0) { LOG(LOG_INT10,LOG_ERROR)("EGA RIL range write called with port %x",port); @@ -278,7 +278,7 @@ void INT10_EGA_RIL_WriteRegisterRange(Bit8u ch, Bit8u cl, Bit16u dx, PhysPt src) */ void INT10_EGA_RIL_ReadRegisterSet(Bit16u cx, PhysPt tbl) { /* read cx register sets */ - for (Bitu i=0; imode <= 3) { + CurMode = &ModeList_VGA_Text_200lines[CurMode->mode]; + } else if (CurMode->mode == 7) { + CurMode = &ModeList_VGA_Text_350lines[4]; + } + break; + case 0x00: // 350 lines emulation + if (CurMode->mode <= 3) { + CurMode = &ModeList_VGA_Text_350lines[CurMode->mode]; + } else if (CurMode->mode == 7) { + CurMode = &ModeList_VGA_Text_350lines[4]; + } + break; + } +} + +void INT10_SetCurMode(void) { + Bit16u bios_mode=(Bit16u)real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); + if (GCC_UNLIKELY(CurMode->mode!=bios_mode)) { + bool mode_changed=false; + switch (machine) { + case MCH_CGA: + if (bios_mode<7) mode_changed=SetCurMode(ModeList_OTHER,bios_mode); + break; + case TANDY_ARCH_CASE: + if (bios_mode!=7 && bios_mode<=0xa) mode_changed=SetCurMode(ModeList_OTHER,bios_mode); + break; + case MCH_HERC: + if (bios_mode<7) mode_changed=SetCurMode(ModeList_OTHER,bios_mode); + else if (bios_mode==7) {mode_changed=true;CurMode=&Hercules_Mode;} + break; + case MCH_EGA: + mode_changed=SetCurMode(ModeList_EGA,bios_mode); + break; + case VGA_ARCH_CASE: + switch (svgaCard) { + case SVGA_TsengET4K: + case SVGA_TsengET3K: + mode_changed=SetCurMode(ModeList_VGA_Tseng,bios_mode); + break; + case SVGA_ParadisePVGA1A: + mode_changed=SetCurMode(ModeList_VGA_Paradise,bios_mode); + break; + case SVGA_S3Trio: + if (bios_mode>=0x68 && CurMode->mode==(bios_mode+0x98)) break; + // fall-through + default: + mode_changed=SetCurMode(ModeList_VGA,bios_mode); + break; + } + if (mode_changed && CurMode->type==M_TEXT) SetTextLines(); + break; + } + if (mode_changed) LOG(LOG_INT10,LOG_WARN)("BIOS video mode changed to %X",bios_mode); + } +} static void FinishSetMode(bool clearmem) { /* Clear video memory if needs be */ if (clearmem) { switch (CurMode->type) { - case M_CGA4: - case M_CGA2: case M_TANDY16: + case M_CGA4: + if ((machine==MCH_PCJR) && (CurMode->mode >= 9)) { + // PCJR cannot access the full 32k at 0xb800 + for (Bit16u ct=0;ct<16*1024;ct++) { + // 0x1800 is the last 32k block in 128k, as set in the CRTCPU_PAGE register + real_writew(0x1800,ct*2,0x0000); + } + break; + } + // fall-through + case M_CGA2: for (Bit16u ct=0;ct<16*1024;ct++) { real_writew( 0xb800,ct*2,0x0000); } @@ -423,7 +491,6 @@ static void FinishSetMode(bool clearmem) { // this is an index into the dcc table: if (IS_VGA_ARCH) real_writeb(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,0x0b); - real_writed(BIOSMEM_SEG,BIOSMEM_VS_POINTER,int10.rom.video_save_pointers); // Set cursor shape if (CurMode->type==M_TEXT) { @@ -434,13 +501,15 @@ static void FinishSetMode(bool clearmem) { // Set active page 0 INT10_SetActivePage(0); /* Set some interrupt vectors */ - switch (CurMode->cheight) { - case 8:RealSetVec(0x43,int10.rom.font_8_first);break; - case 14:RealSetVec(0x43,int10.rom.font_14);break; - case 16:RealSetVec(0x43,int10.rom.font_16);break; + if (CurMode->mode<=3 || CurMode->mode==7) + RealSetVec(0x43,int10.rom.font_8_first); + else { + switch (CurMode->cheight) { + case 8:RealSetVec(0x43,int10.rom.font_8_first);break; + case 14:RealSetVec(0x43,int10.rom.font_14);break; + case 16:RealSetVec(0x43,int10.rom.font_16);break; + } } - /* Tell mouse resolution change */ - Mouse_NewVideoMode(); } bool INT10_SetVideoMode_OTHER(Bit16u mode,bool clearmem) { @@ -456,8 +525,12 @@ bool INT10_SetVideoMode_OTHER(Bit16u mode,bool clearmem) { } break; case MCH_HERC: - // Only init the adapter if the equipment word is set to monochrome (Testdrive) - if ((real_readw(BIOSMEM_SEG,BIOSMEM_INITIAL_MODE)&0x30)!=0x30) return false; + // Allow standard color modes if equipment word is not set to mono (Victory Road) + if ((real_readw(BIOSMEM_SEG,BIOSMEM_INITIAL_MODE)&0x30)!=0x30 && mode<7) { + SetCurMode(ModeList_OTHER,mode); + FinishSetMode(clearmem); + return true; + } CurMode=&Hercules_Mode; mode=7; // in case the video parameter table is modified break; @@ -672,18 +745,7 @@ bool INT10_SetVideoMode(Bit16u mode) { return false; } } - // check for scanline backwards compatibility (VESA text modes??) - if (CurMode->type==M_TEXT) { - if ((modeset_ctl&0x90)==0x80) { // 200 lines emulation - if (CurMode->mode <= 3) { - CurMode = &ModeList_VGA_Text_200lines[CurMode->mode]; - } - } else if ((modeset_ctl&0x90)==0x00) { // 350 lines emulation - if (CurMode->mode <= 3) { - CurMode = &ModeList_VGA_Text_350lines[CurMode->mode]; - } - } - } + if (CurMode->type==M_TEXT) SetTextLines(); } else { if (!SetCurMode(ModeList_EGA,mode)){ LOG(LOG_INT10,LOG_ERROR)("EGA:Trying to set illegal mode %X",mode); @@ -707,9 +769,12 @@ bool INT10_SetVideoMode(Bit16u mode) { /* Setup MISC Output Register */ Bit8u misc_output=0x2 | (mono_mode ? 0x0 : 0x1); - if ((CurMode->type==M_TEXT) && (CurMode->cwidth==9)) { - // 28MHz (16MHz EGA) clock for 9-pixel wide chars - misc_output|=0x4; + if (machine==MCH_EGA) { + // 16MHz clock for 350-line EGA modes except mode F + if ((CurMode->vdispend==350) && (mode!=0xf)) misc_output|=0x4; + } else { + // 28MHz clock for 9-pixel wide chars + if ((CurMode->type==M_TEXT) && (CurMode->cwidth==9)) misc_output|=0x4; } switch (CurMode->vdispend) { @@ -892,7 +957,7 @@ bool INT10_SetVideoMode(Bit16u mode) { switch (CurMode->type) { case M_TEXT: max_scanline|=CurMode->cheight-1; - underline=mono_mode ? 0x0f : 0x1f; // mode 7 uses a diff underline position + underline=mono_mode ? CurMode->cheight-1 : 0x1f; // mode 7 uses underline position break; case M_VGA: underline=0x40; @@ -908,8 +973,10 @@ bool INT10_SetVideoMode(Bit16u mode) { case M_CGA4: max_scanline|=1; break; + default: + if (CurMode->vdispend==350) underline=0x0f; + break; } - if (CurMode->vdispend==350) underline=0x0f; IO_Write(crtc_base,0x09);IO_Write(crtc_base+1,max_scanline); IO_Write(crtc_base,0x14);IO_Write(crtc_base+1,underline); @@ -998,7 +1065,9 @@ bool INT10_SetVideoMode(Bit16u mode) { if (svgaCard == SVGA_S3Trio) { /* Setup the correct clock */ if (CurMode->mode>=0x100) { - misc_output|=0xef; //Select clock 3 + if (CurMode->vdispend>480) + misc_output|=0xc0; //480-line sync + misc_output|=0x0c; //Select clock 3 Bitu clock=CurMode->vtotal*8*CurMode->htotal*70; VGA_SetClock(3,clock/1000); } @@ -1046,6 +1115,8 @@ bool INT10_SetVideoMode(Bit16u mode) { break; case M_LIN4: case M_EGA: + if (CurMode->mode == 0x0f) + gfx_data[0x7]=0x05; // only planes 0 and 2 are used gfx_data[0x6]|=0x05; //graphics mode at 0xa000-affff break; case M_CGA4: @@ -1075,12 +1146,14 @@ bool INT10_SetVideoMode(Bit16u mode) { att_data[0x10]=0x01; //Color Graphics switch (CurMode->mode) { case 0x0f: - att_data[0x10]|=0x0a; //Monochrome - att_data[0x01]=0x08; - att_data[0x04]=0x18; - att_data[0x05]=0x18; - att_data[0x09]=0x08; - att_data[0x0d]=0x18; + att_data[0x12]=0x05; // planes 0 and 2 enabled + att_data[0x10]|=0x0a; // monochrome and blinking + + att_data[0x01]=0x08; // low-intensity + att_data[0x04]=0x18; // blink-on case + att_data[0x05]=0x18; // high-intensity + att_data[0x09]=0x08; // low-intensity in blink-off case + att_data[0x0d]=0x18; // high-intensity in blink-off break; case 0x11: for (i=1;i<16;i++) att_data[i]=0x3f; @@ -1124,7 +1197,7 @@ att_text16: att_data[ct]=ct; att_data[ct+8]=ct+0x38; } - if (IS_VGA_ARCH) att_data[0x06]=0x14; //Odd Color 6 yellow/brown. + att_data[0x06]=0x14; //Odd Color 6 yellow/brown. } break; case M_CGA2: @@ -1225,7 +1298,10 @@ dac_text16: case M_LIN15: case M_LIN16: case M_LIN32: - for (i=0;i<256;i++) { + // IBM and clones use 248 default colors in the palette for 256-color mode. + // The last 8 colors of the palette are only initialized to 0 at BIOS init. + // Palette index is left at 0xf8 as on most clones, IBM leaves it at 0x10. + for (i=0;i<248;i++) { IO_Write(0x3c9,vga_palette[i][0]); IO_Write(0x3c9,vga_palette[i][1]); IO_Write(0x3c9,vga_palette[i][2]); @@ -1247,24 +1323,26 @@ dac_text16: vga.config.pel_panning = 0; IO_Write(0x3c0,0x20); //Disable palette access } + /* Write palette register data to dynamic save area if pointer is non-zero */ + RealPt vsavept=real_readd(BIOSMEM_SEG,BIOSMEM_VS_POINTER); + RealPt dsapt=real_readd(RealSeg(vsavept),RealOff(vsavept)+4); + if (dsapt) { + for (Bit8u ct=0;ct<0x10;ct++) { + real_writeb(RealSeg(dsapt),RealOff(dsapt)+ct,att_data[ct]); + } + real_writeb(RealSeg(dsapt),RealOff(dsapt)+0x10,0); // overscan + } /* Setup some special stuff for different modes */ - Bit8u feature=real_readb(BIOSMEM_SEG,BIOSMEM_INITIAL_MODE); switch (CurMode->type) { case M_CGA2: - feature=(feature&~0x30)|0x20; real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x1e); break; case M_CGA4: - feature=(feature&~0x30)|0x20; if (CurMode->mode==4) real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2a); else if (CurMode->mode==5) real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2e); else real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2); break; - case M_TANDY16: - feature=(feature&~0x30)|0x20; - break; case M_TEXT: - feature=(feature&~0x30)|0x20; switch (CurMode->mode) { case 0:real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x2c);break; case 1:real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x28);break; @@ -1273,14 +1351,7 @@ dac_text16: case 7:real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x29);break; } break; - case M_LIN4: - case M_EGA: - case M_VGA: - feature=(feature&~0x30); - break; } - // disabled, has to be set in bios.cpp exclusively -// real_writeb(BIOSMEM_SEG,BIOSMEM_INITIAL_MODE,feature); if (svgaCard == SVGA_S3Trio) { /* Setup the CPU Window */ @@ -1373,6 +1444,7 @@ dac_text16: /* Set vga attrib register into defined state */ IO_Read(mono_mode ? 0x3ba : 0x3da); IO_Write(0x3c0,0x20); + IO_Read(mono_mode ? 0x3ba : 0x3da); // Kukoo2 demo /* Load text mode font */ if (CurMode->type==M_TEXT) { diff --git a/src/ints/int10_pal.cpp b/src/ints/int10_pal.cpp index 716296c..42eb1c1 100644 --- a/src/ints/int10_pal.cpp +++ b/src/ints/int10_pal.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dosbox.h" @@ -92,6 +92,7 @@ void INT10_SetOverscanBorderColor(Bit8u val) { case TANDY_ARCH_CASE: IO_Read(VGAREG_TDY_RESET); WriteTandyACTL(0x02,val); + IO_Write(VGAREG_TDY_ADDRESS, 0); // enable the screen break; case EGAVGA_ARCH_CASE: ResetACTL(); @@ -293,6 +294,7 @@ void INT10_GetDACPage(Bit8u* mode,Bit8u* page) { *page&=0xc; *page>>=2; } + IO_Write(VGAREG_ACTL_ADDRESS,32); //Enable output and protect palette } void INT10_SetPelMask(Bit8u mask) { @@ -308,9 +310,37 @@ void INT10_SetBackgroundBorder(Bit8u val) { color_select=(color_select & 0xe0) | (val & 0x1f); real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,color_select); - if (machine == MCH_CGA || machine == MCH_TANDY) + switch (machine) { + case MCH_CGA: + // only write the color select register IO_Write(0x3d9,color_select); - else if (machine == MCH_PCJR) { + break; + case MCH_TANDY: + // TODO handle val == 0x1x, wait for retrace + switch(CurMode->mode) { + default: // modes 0-5: write to color select and border + INT10_SetOverscanBorderColor(val); + IO_Write(0x3d9, color_select); + break; + case 0x06: // 2-color: only write the color select register + IO_Write(0x3d9, color_select); + break; + case 0x07: // Tandy monochrome not implemented + break; + case 0x08: + case 0x09: // 16-color: write to color select, border and pal. index 0 + INT10_SetOverscanBorderColor(val); + INT10_SetSinglePaletteRegister(0, val); + IO_Write(0x3d9, color_select); + break; + case 0x0a: // 4-color highres: + // write zero to color select, write palette to indexes 1-3 + // TODO palette + IO_Write(0x3d9, 0); + break; + } + break; + case MCH_PCJR: IO_Read(VGAREG_TDY_RESET); // reset the flipflop if (vga.mode!=M_TANDY_TEXT) { IO_Write(VGAREG_TDY_ADDRESS, 0x10); @@ -318,8 +348,8 @@ void INT10_SetBackgroundBorder(Bit8u val) { } IO_Write(VGAREG_TDY_ADDRESS, 0x2); // border color IO_Write(VGAREG_PCJR_DATA, color_select&0xf); - } - else if (IS_EGAVGA_ARCH) { + break; + case EGAVGA_ARCH_CASE: val = ((val << 1) & 0x10) | (val & 0x7); /* Always set the overscan color */ INT10_SetSinglePaletteRegister( 0x11, val ); @@ -333,6 +363,7 @@ void INT10_SetBackgroundBorder(Bit8u val) { INT10_SetSinglePaletteRegister( 2, val ); val+=2; INT10_SetSinglePaletteRegister( 3, val ); + break; } } @@ -343,6 +374,7 @@ void INT10_SetColorSelect(Bit8u val) { if (machine == MCH_CGA || machine==MCH_TANDY) IO_Write(0x3d9,temp); else if (machine == MCH_PCJR) { + IO_Read(VGAREG_TDY_RESET); // reset the flipflop switch(vga.mode) { case M_TANDY2: IO_Write(VGAREG_TDY_ADDRESS, 0x11); diff --git a/src/ints/int10_put_pixel.cpp b/src/ints/int10_put_pixel.cpp index 8f759e6..1e052cd 100644 --- a/src/ints/int10_put_pixel.cpp +++ b/src/ints/int10_put_pixel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -30,34 +30,44 @@ void INT10_PutPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u color) { switch (CurMode->type) { case M_CGA4: - { - if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)<=5) { - Bit16u off=(y>>1)*80+(x>>2); - if (y&1) off+=8*1024; + { + if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)<=5) { + // this is a 16k mode + Bit16u off=(y>>1)*80+(x>>2); + if (y&1) off+=8*1024; - Bit8u old=real_readb(0xb800,off); - if (color & 0x80) { - color&=3; - old^=color << (2*(3-(x&3))); - } else { - old=(old&cga_masks[x&3])|((color&3) << (2*(3-(x&3)))); - } - real_writeb(0xb800,off,old); - } else { - Bit16u off=(y>>2)*160+((x>>2)&(~1)); - off+=(8*1024) * (y & 3); + Bit8u old=real_readb(0xb800,off); + if (color & 0x80) { + color&=3; + old^=color << (2*(3-(x&3))); + } else { + old=(old&cga_masks[x&3])|((color&3) << (2*(3-(x&3)))); + } + real_writeb(0xb800,off,old); + } else { + // a 32k mode: PCJr special case (see M_TANDY16) + Bit16u seg; + if (machine==MCH_PCJR) { + Bitu cpupage = + (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7; + seg = cpupage << 10; // A14-16 to addr bits 14-16 + } else + seg = 0xb800; - Bit16u old=real_readw(0xb800,off); - if (color & 0x80) { - old^=(color&1) << (7-(x&7)); - old^=((color&2)>>1) << ((7-(x&7))+8); - } else { - old=(old&(~(0x101<<(7-(x&7))))) | ((color&1) << (7-(x&7))) | (((color&2)>>1) << ((7-(x&7))+8)); - } - real_writew(0xb800,off,old); - } + Bit16u off=(y>>2)*160+((x>>2)&(~1)); + off+=(8*1024) * (y & 3); + + Bit16u old=real_readw(seg,off); + if (color & 0x80) { + old^=(color&1) << (7-(x&7)); + old^=((color&2)>>1) << ((7-(x&7))+8); + } else { + old=(old&(~(0x101<<(7-(x&7))))) | ((color&1) << (7-(x&7))) | (((color&2)>>1) << ((7-(x&7))+8)); + } + real_writew(seg,off,old); } - break; + } + break; case M_CGA2: { Bit16u off=(y>>1)*80+(x>>3); @@ -73,26 +83,50 @@ void INT10_PutPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u color) { } break; case M_TANDY16: - { - IO_Write(0x3d4,0x09); - Bit8u scanlines_m1=IO_Read(0x3d5); - Bit16u off=(y>>((scanlines_m1==1)?1:2))*(CurMode->swidth>>1)+(x>>1); - off+=(8*1024) * (y & scanlines_m1); - Bit8u old=real_readb(0xb800,off); - Bit8u p[2]; - p[1] = (old >> 4) & 0xf; - p[0] = old & 0xf; - Bitu ind = 1-(x & 0x1); + { + // find out if we are in a 32k mode (0x9 or 0xa) + // This requires special handling on the PCJR + // because only 16k are mapped at 0xB800 + bool is_32k = (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9)? + true:false; - if (color & 0x80) { - p[ind]^=(color & 0x7f); - } else { - p[ind]=color; - } - old = (p[1] << 4) | p[0]; - real_writeb(0xb800,off,old); + Bit16u segment, offset; + if (is_32k) { + if (machine==MCH_PCJR) { + Bitu cpupage = + (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7; + segment = cpupage << 10; // A14-16 to addr bits 14-16 + } else + segment = 0xb800; + // bits 1 and 0 of y select the bank + // two pixels per byte (thus x>>1) + offset = (y >> 2) * (CurMode->swidth >> 1) + (x>>1); + // select the scanline bank + offset += (8*1024) * (y & 3); + } else { + segment = 0xb800; + // bit 0 of y selects the bank + offset = (y >> 1) * (CurMode->swidth >> 1) + (x>>1); + offset += (8*1024) * (y & 1); } - break; + + // update the pixel + Bit8u old=real_readb(segment, offset); + Bit8u p[2]; + p[1] = (old >> 4) & 0xf; + p[0] = old & 0xf; + Bitu ind = 1-(x & 0x1); + + if (color & 0x80) { + // color is to be XORed + p[ind]^=(color & 0x7f); + } else { + p[ind]=color; + } + old = (p[1] << 4) | p[0]; + real_writeb(segment,offset, old); + } + break; case M_LIN4: if ((machine!=MCH_VGA) || (svgaCard!=SVGA_TsengET4K) || (CurMode->swidth>800)) { @@ -166,6 +200,26 @@ void INT10_GetPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u * color) { *color=(val>>(((7-(x&7))))) & 1 ; } break; + case M_TANDY16: + { + bool is_32k = (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9)?true:false; + Bit16u segment, offset; + if (is_32k) { + if (machine==MCH_PCJR) { + Bitu cpupage = (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7; + segment = cpupage << 10; + } else segment = 0xb800; + offset = (y >> 2) * (CurMode->swidth >> 1) + (x>>1); + offset += (8*1024) * (y & 3); + } else { + segment = 0xb800; + offset = (y >> 1) * (CurMode->swidth >> 1) + (x>>1); + offset += (8*1024) * (y & 1); + } + Bit8u val=real_readb(segment,offset); + *color=(val>>((x&1)?0:4)) & 0xf; + } + break; case M_EGA: { /* Calculate where the pixel is in video memory */ diff --git a/src/ints/int10_vesa.cpp b/src/ints/int10_vesa.cpp index baeb0c4..6e3801b 100644 --- a/src/ints/int10_vesa.cpp +++ b/src/ints/int10_vesa.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -36,7 +36,7 @@ #define VESA_UNIMPLEMENTED 0xFF static struct { - Bitu setwindow; + Bitu rmWindow; Bitu pmStart; Bitu pmWindow; Bitu pmPalette; @@ -45,7 +45,7 @@ static struct { static char string_oem[]="S3 Incorporated. Trio64"; static char string_vendorname[]="DOSBox Development Team"; static char string_productname[]="DOSBox - The DOS Emulator"; -static char string_productrev[]="DOSBox "VERSION; +static char string_productrev[]="DOSBox " VERSION; #ifdef _MSC_VER #pragma pack (1) @@ -254,7 +254,7 @@ foundit: var_write(&minfo.XResolution,mblock->swidth); var_write(&minfo.YResolution,mblock->sheight); } - var_write(&minfo.WinFuncPtr,CALLBACK_RealPointer(callback.setwindow)); + var_write(&minfo.WinFuncPtr,int10.rom.set_window); var_write(&minfo.NumberOfBanks,0x1); var_write(&minfo.Reserved_page,0x1); var_write(&minfo.XCharSize,mblock->cwidth); @@ -297,11 +297,15 @@ Bit8u VESA_GetCPUWindow(Bit8u window,Bit16u & address) { } -Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count) { +Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count,bool wait) { //Structure is (vesa 3.0 doc): blue,green,red,alignment Bit8u r,g,b; if (index>255) return VESA_FAIL; if (index+count>256) return VESA_FAIL; + + // Wait for retrace if requested + if (wait) CALLBACK_RunRealFar(RealSeg(int10.rom.wait_retrace),RealOff(int10.rom.wait_retrace)); + IO_Write(0x3c8,(Bit8u)index); while (count) { b = mem_readb(data++); @@ -422,8 +426,7 @@ Bit8u VESA_ScanLineLength(Bit8u subcall,Bit16u val, Bit16u & bytes,Bit16u & pixe return VESA_SUCCESS; } -Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y) { - // TODO wait for retrace in case bl==0x80 +Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y,bool wait) { Bitu pixels_per_offset; Bitu panning_factor = 1; @@ -467,6 +470,9 @@ Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y) { IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on IO_Write(0x3c0,new_panning); + // Wait for retrace if requested + if (wait) CALLBACK_RunRealFar(RealSeg(int10.rom.wait_retrace),RealOff(int10.rom.wait_retrace)); + return VESA_SUCCESS; } @@ -514,25 +520,30 @@ static Bitu VESA_SetWindow(void) { if (reg_bh) reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx); else reg_ah=VESA_SetCPUWindow(reg_bl,(Bit8u)reg_dx); reg_al=0x4f; - return 0; + return CBRET_NONE; } static Bitu VESA_PMSetWindow(void) { - VESA_SetCPUWindow(0,(Bit8u)reg_dx); - return 0; + IO_Write(0x3d4,0x6a); + IO_Write(0x3d5,reg_dl); + return CBRET_NONE; } static Bitu VESA_PMSetPalette(void) { - VESA_SetPalette(SegPhys(es) + reg_edi, reg_dx, reg_cx ); - return 0; + PhysPt data=SegPhys(es)+reg_edi; + Bit32u count=reg_cx; + IO_Write(0x3c8,reg_dl); + do { + IO_Write(0x3c9,mem_readb(data+2)); + IO_Write(0x3c9,mem_readb(data+1)); + IO_Write(0x3c9,mem_readb(data)); + data+=4; + } while (--count); + return CBRET_NONE; } static Bitu VESA_PMSetStart(void) { - // This function is from VBE2 and directly sets the VGA - // display start address. - - // TODO wait for retrace in case bl==0x80 Bit32u start = (reg_dx << 16) | reg_cx; vga.config.display_start = start; - return 0; + return CBRET_NONE; } @@ -569,10 +580,12 @@ void INT10_SetupVESA(void) { case SVGA_S3Trio: break; } - callback.setwindow=CALLBACK_Allocate(); - callback.pmPalette=CALLBACK_Allocate(); - callback.pmStart=CALLBACK_Allocate(); - CALLBACK_Setup(callback.setwindow,VESA_SetWindow,CB_RETF, "VESA Real Set Window"); + /* Prepare the real mode interface */ + int10.rom.wait_retrace=RealMake(0xc000,int10.rom.used); + int10.rom.used += (Bit16u)CALLBACK_Setup(0, NULL, CB_VESA_WAIT, PhysMake(0xc000,int10.rom.used), ""); + callback.rmWindow=CALLBACK_Allocate(); + int10.rom.set_window=RealMake(0xc000,int10.rom.used); + int10.rom.used += (Bit16u)CALLBACK_Setup(callback.rmWindow, VESA_SetWindow, CB_RETF, PhysMake(0xc000,int10.rom.used), "VESA Real Set Window"); /* Prepare the pmode interface */ int10.rom.pmode_interface=RealMake(0xc000,int10.rom.used); int10.rom.used += 8; //Skip the byte later used for offsets @@ -585,11 +598,12 @@ void INT10_SetupVESA(void) { int10.rom.pmode_interface_start = int10.rom.used - RealOff( int10.rom.pmode_interface ); phys_writew( Real2Phys(int10.rom.pmode_interface) + 2, int10.rom.pmode_interface_start); callback.pmStart=CALLBACK_Allocate(); - int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmStart, VESA_PMSetStart, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Start"); + int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmStart, VESA_PMSetStart, CB_VESA_PM, PhysMake(0xc000,int10.rom.used), "VESA PM Set Start"); /* PM Set Palette call */ int10.rom.pmode_interface_palette = int10.rom.used - RealOff( int10.rom.pmode_interface ); phys_writew( Real2Phys(int10.rom.pmode_interface) + 4, int10.rom.pmode_interface_palette); callback.pmPalette=CALLBACK_Allocate(); + int10.rom.used += (Bit16u)CALLBACK_Setup(0, NULL, CB_VESA_PM, PhysMake(0xc000,int10.rom.used), ""); int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmPalette, VESA_PMSetPalette, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Palette"); /* Finalize the size and clear the required ports pointer */ phys_writew( Real2Phys(int10.rom.pmode_interface) + 6, 0); diff --git a/src/ints/int10_video_state.cpp b/src/ints/int10_video_state.cpp index c33e822..0dbc34e 100644 --- a/src/ints/int10_video_state.cpp +++ b/src/ints/int10_video_state.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/ints/int10_vptable.cpp b/src/ints/int10_vptable.cpp index 7d79fbd..3ec451d 100644 --- a/src/ints/int10_vptable.cpp +++ b/src/ints/int10_vptable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/ints/mouse.cpp b/src/ints/mouse.cpp index 5f8c7ba..b463a60 100644 --- a/src/ints/mouse.cpp +++ b/src/ints/mouse.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -36,8 +36,8 @@ static Bitu call_int33,call_int74,int74_ret_callback,call_mouse_bd; static Bit16u ps2cbseg,ps2cbofs; static bool useps2callback,ps2callbackinit; -static Bitu call_ps2; -static RealPt ps2_callback; +static Bitu call_ps2,call_uir; +static RealPt ps2_callback,uir_callback; static Bit16s oldmouseX, oldmouseY; // forward void WriteMouseIntVector(void); @@ -113,8 +113,8 @@ static struct { Bit16u dspeed_val; float senv_x; float senv_y; - Bit16u updateRegion_x[2]; - Bit16u updateRegion_y[2]; + Bit16s updateRegion_x[2]; + Bit16s updateRegion_y[2]; Bit16u doubleSpeedThreshold; Bit16u language; Bit16u cursorType; @@ -251,6 +251,11 @@ void DrawCursorText() { // Restore Background RestoreCursorBackgroundText(); + // Check if cursor in update region + if ((POS_Y <= mouse.updateRegion_y[1]) && (POS_Y >= mouse.updateRegion_y[0]) && + (POS_X <= mouse.updateRegion_x[1]) && (POS_X >= mouse.updateRegion_x[0])) { + return; + } // Save Background mouse.backposx = POS_X>>3; @@ -259,15 +264,26 @@ void DrawCursorText() { //use current page (CV program) Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); - Bit16u result; - - ReadCharAttr(mouse.backposx,mouse.backposy,page,&result); - mouse.backData[0] = (Bit8u)(result & 0xFF); - mouse.backData[1] = (Bit8u)(result>>8); - mouse.background = true; - // Write Cursor - result = (result & mouse.textAndMask) ^ mouse.textXorMask; - WriteChar(mouse.backposx,mouse.backposy,page,(Bit8u)(result&0xFF),(Bit8u)(result>>8),true); + + if (mouse.cursorType == 0) { + Bit16u result; + ReadCharAttr(mouse.backposx,mouse.backposy,page,&result); + mouse.backData[0] = (Bit8u)(result & 0xFF); + mouse.backData[1] = (Bit8u)(result>>8); + mouse.background = true; + // Write Cursor + result = (result & mouse.textAndMask) ^ mouse.textXorMask; + WriteChar(mouse.backposx,mouse.backposy,page,(Bit8u)(result&0xFF),(Bit8u)(result>>8),true); + } else { + Bit16u address=page * real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE); + address += (mouse.backposy * real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS) + mouse.backposx) * 2; + address /= 2; + Bit16u cr = real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); + IO_Write(cr , 0xe); + IO_Write(cr + 1, (address>>8) & 0xff); + IO_Write(cr , 0xf); + IO_Write(cr + 1, address & 0xff); + } } // *************************************************************************** @@ -364,6 +380,7 @@ void RestoreCursorBackground() { void DrawCursor() { if (mouse.hidden || mouse.inhibit_draw) return; + INT10_SetCurMode(); // In Textmode ? if (CurMode->type==M_TEXT) { DrawCursorText(); @@ -465,8 +482,8 @@ void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate) { mouse.y += dy; } else { if (CurMode->type == M_TEXT) { - mouse.x = x*CurMode->swidth; - mouse.y = y*CurMode->sheight * 8 / CurMode->cheight; + mouse.x = x*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8; + mouse.y = y*(real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1)*8; } else if ((mouse.max_x < 2048) || (mouse.max_y < 2048) || (mouse.max_x != mouse.max_y)) { if ((mouse.max_x > 0) && (mouse.max_y > 0)) { mouse.x = x*mouse.max_x; @@ -508,18 +525,21 @@ void Mouse_ButtonPressed(Bit8u button) { switch (button) { #if (MOUSE_BUTTONS >= 1) case 0: + if (mouse.buttons&1) return; mouse.buttons|=1; Mouse_AddEvent(MOUSE_LEFT_PRESSED); break; #endif #if (MOUSE_BUTTONS >= 2) case 1: + if (mouse.buttons&2) return; mouse.buttons|=2; Mouse_AddEvent(MOUSE_RIGHT_PRESSED); break; #endif #if (MOUSE_BUTTONS >= 3) case 2: + if (mouse.buttons&4) return; mouse.buttons|=4; Mouse_AddEvent(MOUSE_MIDDLE_PRESSED); break; @@ -536,18 +556,21 @@ void Mouse_ButtonReleased(Bit8u button) { switch (button) { #if (MOUSE_BUTTONS >= 1) case 0: + if (!(mouse.buttons&1)) return; mouse.buttons&=~1; Mouse_AddEvent(MOUSE_LEFT_RELEASED); break; #endif #if (MOUSE_BUTTONS >= 2) case 1: + if (!(mouse.buttons&2)) return; mouse.buttons&=~2; Mouse_AddEvent(MOUSE_RIGHT_RELEASED); break; #endif #if (MOUSE_BUTTONS >= 3) case 2: + if (!(mouse.buttons&4)) return; mouse.buttons&=~4; Mouse_AddEvent(MOUSE_MIDDLE_RELEASED); break; @@ -590,12 +613,20 @@ static void Mouse_ResetHardware(void){ PIC_SetIRQMask(MOUSE_IRQ,false); } +void Mouse_BeforeNewVideoMode(bool setmode) { + if (CurMode->type!=M_TEXT) RestoreCursorBackground(); + else RestoreCursorBackgroundText(); + mouse.hidden = 1; + mouse.oldhidden = 1; + mouse.background = false; +} + //Does way to much. Many things should be moved to mouse reset one day -void Mouse_NewVideoMode(void) { +void Mouse_AfterNewVideoMode(bool setmode) { mouse.inhibit_draw = false; /* Get the correct resolution from the current video mode */ Bit8u mode = mem_readb(BIOS_VIDEO_MODE); - if(mode == mouse.mode) {LOG(LOG_MOUSE,LOG_NORMAL)("New video is the same as the old"); /*return;*/} + if (setmode && mode == mouse.mode) LOG(LOG_MOUSE,LOG_NORMAL)("New video mode is the same as the old"); mouse.gran_x = (Bit16s)0xffff; mouse.gran_y = (Bit16s)0xffff; switch (mode) { @@ -637,7 +668,6 @@ void Mouse_NewVideoMode(void) { return; } mouse.mode = mode; - mouse.hidden = 1; mouse.max_x = 639; mouse.min_x = 0; mouse.min_y = 0; @@ -648,7 +678,6 @@ void Mouse_NewVideoMode(void) { mouse.hotx = 0; mouse.hoty = 0; - mouse.background = false; mouse.screenMask = defaultScreenMask; mouse.cursorMask = defaultCursorMask; mouse.textAndMask= defaultTextAndMask; @@ -656,13 +685,9 @@ void Mouse_NewVideoMode(void) { mouse.language = 0; mouse.page = 0; mouse.doubleSpeedThreshold = 64; - mouse.updateRegion_x[0] = 1; - mouse.updateRegion_y[0] = 1; - mouse.updateRegion_x[1] = 1; - mouse.updateRegion_y[1] = 1; - mouse.cursorType = 0; + mouse.updateRegion_y[1] = -1; //offscreen + mouse.cursorType = 0; //Test mouse.enabled=true; - mouse.oldhidden=1; oldmouseX = static_cast(mouse.x); oldmouseY = static_cast(mouse.y); @@ -672,17 +697,24 @@ void Mouse_NewVideoMode(void) { //Much too empty, Mouse_NewVideoMode contains stuff that should be in here static void Mouse_Reset(void) { - /* Remove drawn mouse Legends of Valor */ - if (CurMode->type!=M_TEXT) RestoreCursorBackground(); - else RestoreCursorBackgroundText(); - mouse.hidden = 1; - - Mouse_NewVideoMode(); + Mouse_BeforeNewVideoMode(false); + Mouse_AfterNewVideoMode(false); Mouse_SetMickeyPixelRate(8,16); mouse.mickey_x = 0; mouse.mickey_y = 0; + mouse.buttons = 0; + + for (Bit16u but=0; but((mouse.max_x + 1)/ 2); mouse.y = static_cast((mouse.max_y + 1)/ 2); @@ -703,6 +735,7 @@ static Bitu INT33_Handler(void) { break; case 0x01: /* Show Mouse */ if(mouse.hidden) mouse.hidden--; + mouse.updateRegion_y[1] = -1; //offscreen Mouse_AutoLock(true); DrawCursor(); break; @@ -801,9 +834,14 @@ static Bitu INT33_Handler(void) { } break; case 0x0a: /* Define Text Cursor */ - mouse.cursorType = reg_bx; + mouse.cursorType = (reg_bx?1:0); mouse.textAndMask = reg_cx; mouse.textXorMask = reg_dx; + if (reg_bx) { + INT10_SetCursorShape(reg_cl,reg_dl); + LOG(LOG_MOUSE,LOG_NORMAL)("Hardware Text cursor selected"); + } + DrawCursor(); break; case 0x0b: /* Read Motion Data */ reg_cx=static_cast(mouse.mickey_x); @@ -820,11 +858,12 @@ static Bitu INT33_Handler(void) { case 0x0f: /* Define mickey/pixel rate */ Mouse_SetMickeyPixelRate(reg_cx,reg_dx); break; - case 0x10: /* Define screen region for updating */ - mouse.updateRegion_x[0]=reg_cx; - mouse.updateRegion_y[0]=reg_dx; - mouse.updateRegion_x[1]=reg_si; - mouse.updateRegion_y[1]=reg_di; + case 0x10: /* Define screen region for updating */ + mouse.updateRegion_x[0]=(Bit16s)reg_cx; + mouse.updateRegion_y[0]=(Bit16s)reg_dx; + mouse.updateRegion_x[1]=(Bit16s)reg_si; + mouse.updateRegion_y[1]=(Bit16s)reg_di; + DrawCursor(); break; case 0x11: /* Get number of buttons */ reg_ax=0xffff; @@ -929,6 +968,12 @@ static Bitu INT33_Handler(void) { reg_cx=(Bit16u)mouse.max_x; reg_dx=(Bit16u)mouse.max_y; break; + case 0x2a: /* Get cursor hot spot */ + reg_al=(Bit8u)-mouse.hidden; // Microsoft uses a negative byte counter for cursor visibility + reg_bx=(Bit16u)mouse.hotx; + reg_cx=(Bit16u)mouse.hoty; + reg_dx=0x04; // PS/2 mouse type + break; case 0x31: /* Get Current Minimum/Maximum virtual coordinates */ reg_ax=(Bit16u)mouse.min_x; reg_bx=(Bit16u)mouse.min_y; @@ -1002,7 +1047,7 @@ static Bitu MOUSE_BD_Handler(void) { } static Bitu INT74_Handler(void) { - if (mouse.events>0) { + if (mouse.events>0 && !mouse.in_UIR) { mouse.events--; /* Check for an active Interrupt Handler that will get called */ if (mouse.sub_mask & mouse.event_queue[mouse.events].type) { @@ -1013,11 +1058,13 @@ static Bitu INT74_Handler(void) { reg_si=static_cast(mouse.mickey_x); reg_di=static_cast(mouse.mickey_y); CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback))); - CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback))); - SegSet16(cs, mouse.sub_seg); - reg_ip = mouse.sub_ofs; - if(mouse.in_UIR) LOG(LOG_MOUSE,LOG_ERROR)("Already in UIR!"); + CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback))+7); + CPU_Push16(RealSeg(uir_callback)); + CPU_Push16(RealOff(uir_callback)); + CPU_Push16(mouse.sub_seg); + CPU_Push16(mouse.sub_ofs); mouse.in_UIR = true; + //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 %X",mouse.event_queue[mouse.events].type ); } else if (useps2callback) { CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback))); CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback))); @@ -1025,16 +1072,17 @@ static Bitu INT74_Handler(void) { } else { SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback))); reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback)); + //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 not interested"); } } else { SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback))); reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback)); + //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 no events"); } return CBRET_NONE; } -Bitu MOUSE_UserInt_CB_Handler(void) { - mouse.in_UIR = false; +Bitu INT74_Ret_Handler(void) { if (mouse.events) { if (!mouse.timer_in_progress) { mouse.timer_in_progress = true; @@ -1044,6 +1092,11 @@ Bitu MOUSE_UserInt_CB_Handler(void) { return CBRET_NONE; } +Bitu UIR_Handler(void) { + mouse.in_UIR = false; + return CBRET_NONE; +} + void MOUSE_Init(Section* /*sec*/) { // Callback for mouse interrupt 0x33 call_int33=CALLBACK_Allocate(); @@ -1069,22 +1122,29 @@ void MOUSE_Init(Section* /*sec*/) { call_int74=CALLBACK_Allocate(); CALLBACK_Setup(call_int74,&INT74_Handler,CB_IRQ12,"int 74"); // pseudocode for CB_IRQ12: + // sti // push ds // push es // pushad - // sti // callback INT74_Handler - // doesn't return here, but rather to CB_IRQ12_RET - // (ps2 callback/user callback inbetween if requested) + // ps2 or user callback if requested + // otherwise jumps to CB_IRQ12_RET + // push ax + // mov al, 0x20 + // out 0xa0, al + // out 0x20, al + // pop ax + // cld + // retf int74_ret_callback=CALLBACK_Allocate(); - CALLBACK_Setup(int74_ret_callback,&MOUSE_UserInt_CB_Handler,CB_IRQ12_RET,"int 74 ret"); + CALLBACK_Setup(int74_ret_callback,&INT74_Ret_Handler,CB_IRQ12_RET,"int 74 ret"); // pseudocode for CB_IRQ12_RET: - // callback MOUSE_UserInt_CB_Handler // cli // mov al, 0x20 // out 0xa0, al // out 0x20, al + // callback INT74_Ret_Handler // popad // pop es // pop ds @@ -1099,6 +1159,11 @@ void MOUSE_Init(Section* /*sec*/) { CALLBACK_Setup(call_ps2,&PS2_Handler,CB_RETF,"ps2 bios callback"); ps2_callback=CALLBACK_RealPointer(call_ps2); + // Callback for mouse user routine return + call_uir=CALLBACK_Allocate(); + CALLBACK_Setup(call_uir,&UIR_Handler,CB_RETF_CLI,"mouse uir ret"); + uir_callback=CALLBACK_RealPointer(call_uir); + memset(&mouse,0,sizeof(mouse)); mouse.hidden = 1; //Hide mouse on startup mouse.timer_in_progress = false; diff --git a/src/ints/xms.cpp b/src/ints/xms.cpp index 1d9d2af..1e0bf89 100644 --- a/src/ints/xms.cpp +++ b/src/ints/xms.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/src/ints/xms.h b/src/ints/xms.h index 75891dc..d397c49 100644 --- a/src/ints/xms.h +++ b/src/ints/xms.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __XMS_H__ diff --git a/src/libs/zmbv/drvproc.cpp b/src/libs/zmbv/drvproc.cpp index 736d7d7..299e5e6 100644 --- a/src/libs/zmbv/drvproc.cpp +++ b/src/libs/zmbv/drvproc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // // Zipped Motion Block Video @@ -68,7 +68,7 @@ * Defined separately for each message. * ***************************************************************************/ -LRESULT PASCAL DriverProc(DWORD dwDriverID, HDRVR hDriver, UINT uiMessage, LPARAM lParam1, LPARAM lParam2) { +extern "C" LRESULT PASCAL __declspec(dllexport) DriverProc(DWORD dwDriverID, HDRVR hDriver, UINT uiMessage, LPARAM lParam1, LPARAM lParam2) { CodecInst* pi = (CodecInst*)dwDriverID; switch (uiMessage) { diff --git a/src/libs/zmbv/makedll.mk b/src/libs/zmbv/makedll.mk new file mode 100644 index 0000000..c2b0b02 --- /dev/null +++ b/src/libs/zmbv/makedll.mk @@ -0,0 +1,17 @@ +CPP=g++ +CPPFLAGS=-O3 +LIBS=-static -lz -lwinmm -static-libgcc -static-libstdc++ + +OBJ = resource.o zmbv.o drvproc.o zmbv_vfw.o + +default: zmbv.dll + + +resource.o: resource.rc + windres -i resource.rc -o resource.o + +%.o: %.cpp + $(CPP) -c -o $@ $< $(CPPFLAGS) + +zmbv.dll: $(OBJ) + $(CPP) -shared -o $@ $^ $(CPPFLAGS) $(LIBS) zmbv_mingw.def \ No newline at end of file diff --git a/src/libs/zmbv/resource.h b/src/libs/zmbv/resource.h index f4830af..0d6f72a 100644 --- a/src/libs/zmbv/resource.h +++ b/src/libs/zmbv/resource.h @@ -1,21 +1,8 @@ //{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by zmbv_vfw.rc -// + #define IDD_ABOUT 101 #define IDD_CONFIGURE 102 #define IDC_HOMEPAGE 1000 #define IDC_EMAIL 1001 #define IDC_SLIDER1 1008 -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 103 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1009 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/src/libs/zmbv/resource.rc b/src/libs/zmbv/resource.rc new file mode 100644 index 0000000..5e4e3a4 --- /dev/null +++ b/src/libs/zmbv/resource.rc @@ -0,0 +1,40 @@ +#include "resource.h" +#include "windows.h" + +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT DIALOGEX 0, 0, 167, 55 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "DOSBox Video Codec v0.1a" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,131,34,29,14 + CTEXT "Zipped Motion Block Video v 0.1\nCopyright 2009-2019 DOSBox Team", + IDC_STATIC,7,7,153,25,SS_NOPREFIX + PUSHBUTTON "Email author",IDC_EMAIL,7,34,50,14 + PUSHBUTTON "Visit home page",IDC_HOMEPAGE,59,34,58,14 +END + +IDD_CONFIGURE DIALOGEX 0, 0, 213, 146 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "ZMBV configuration dialog" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + PUSHBUTTON "Email author",IDC_EMAIL,44,86,50,14 + PUSHBUTTON "Visit home page",IDC_HOMEPAGE,109,87,58,14 + DEFPUSHBUTTON "OK",IDOK,44,125,50,14 + PUSHBUTTON "Cancel",IDCANCEL,117,125,50,14 + CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_BOTH | + TBS_NOTICKS | WS_TABSTOP,57,30,92,18 +END + + diff --git a/src/libs/zmbv/zmbv.cpp b/src/libs/zmbv/zmbv.cpp index f52176f..dc2c047 100644 --- a/src/libs/zmbv/zmbv.cpp +++ b/src/libs/zmbv/zmbv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include diff --git a/src/libs/zmbv/zmbv.h b/src/libs/zmbv/zmbv.h index f16d5ed..7a6705c 100644 --- a/src/libs/zmbv/zmbv.h +++ b/src/libs/zmbv/zmbv.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_DOSBOX_H diff --git a/src/libs/zmbv/zmbv_mingw.def b/src/libs/zmbv/zmbv_mingw.def new file mode 100644 index 0000000..0414274 --- /dev/null +++ b/src/libs/zmbv/zmbv_mingw.def @@ -0,0 +1,2 @@ +EXPORTS + DriverProc = DriverProc@20 @1 diff --git a/src/libs/zmbv/zmbv_vfw.cpp b/src/libs/zmbv/zmbv_vfw.cpp index b1d7747..3be8459 100644 --- a/src/libs/zmbv/zmbv_vfw.cpp +++ b/src/libs/zmbv/zmbv_vfw.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // // Zipped Motion Block Video @@ -30,10 +30,10 @@ #include #include -TCHAR szDescription[] = TEXT("Zipped Motion Block Video v0.1"); +TCHAR szDescription[] = TEXT("Zipped Motion Block Video v0.1a"); TCHAR szName[] = TEXT(CODEC_4CC); -#define VERSION 0x00000001 // 0.1 +#define VERSION 0x00000002 // newer version /******************************************************************** ********************************************************************/ @@ -50,6 +50,7 @@ void Msg(const char fmt[], ...) { va_start(val, fmt); wvsprintf(buf, fmt, val); + va_end(val); const COORD _80x50 = {80,50}; static BOOL startup = (AllocConsole(), SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), _80x50)); @@ -89,7 +90,7 @@ DWORD Close(CodecInst* pinst) { BOOL CodecInst::QueryAbout() { return TRUE; } -static BOOL CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { +static INT_PTR CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_COMMAND) { switch (LOWORD(wParam)) { case IDOK: @@ -110,7 +111,7 @@ DWORD CodecInst::About(HWND hwnd) { return ICERR_OK; } -static BOOL CALLBACK ConfigureDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { +static INT_PTR CALLBACK ConfigureDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_INITDIALOG) { @@ -174,6 +175,7 @@ DWORD CodecInst::GetInfo(ICINFO* icinfo, DWORD dwSize) { ****************************************************************/ static int GetInputBitDepth(const BITMAPINFOHEADER *lpbiIn) { + //Msg( "Get input depth compression %d bitcount %d\n", lpbiIn->biCompression, lpbiIn->biBitCount ); if (lpbiIn->biCompression == BI_RGB) { if (lpbiIn->biPlanes != 1) return -1; @@ -213,11 +215,14 @@ static bool CanCompress(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut, b if (lpbiIn) { if (GetInputBitDepth(lpbiIn) < 0) return false; - } else return false; + } else + return false; if (lpbiOut) { + //Needs to match our 4cc format if (memcmp(&lpbiOut->biCompression,CODEC_4CC, 4)) return false; - } else return !requireOutput; + } else + return !requireOutput; return true; } @@ -225,7 +230,8 @@ static bool CanCompress(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut, b ****************************************************************/ DWORD CodecInst::CompressQuery(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { - if (CanCompress(lpbiIn,lpbiOut,false)) return ICERR_OK; + if (CanCompress(lpbiIn,lpbiOut,false)) + return ICERR_OK; return ICERR_BADFORMAT; } @@ -300,7 +306,29 @@ DWORD CodecInst::Compress(ICCOMPRESS* icinfo, DWORD dwSize) { if (icinfo->dwFlags & ICCOMPRESS_KEYFRAME) flags |= 1; - codec->PrepareCompressFrame( flags, format, 0, icinfo->lpOutput, 99999999); + char palette[256 * 4]; + char *pal = NULL; + + if (lpbiIn->biBitCount == 8) { + const int entries = icinfo->lpbiInput->biClrUsed ? icinfo->lpbiInput->biClrUsed : 256; + const char* srcpal = (char *)icinfo->lpbiInput + icinfo->lpbiInput->biSize; + char* dstpal = palette; + + memset(palette, 0, sizeof palette); + + for(int i=0; iPrepareCompressFrame( flags, format, pal, icinfo->lpOutput, 99999999); char *readPt = (char *)icinfo->lpInput + pitch*(lpbiIn->biHeight - 1); for(i = 0;ibiHeight;i++) { codec->CompressLines(1, (void **)&readPt ); @@ -350,7 +378,8 @@ DWORD CodecInst::DecompressQuery(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER l DWORD CodecInst::DecompressGetFormat(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { if (memcmp(&lpbiIn->biCompression,CODEC_4CC,4)) return ICERR_BADFORMAT; - if (!lpbiOut) return sizeof(BITMAPINFOHEADER); + if (!lpbiOut) + return sizeof(BITMAPINFOHEADER); *lpbiOut = *lpbiIn; lpbiOut->biPlanes = 1; lpbiOut->biSize = sizeof(BITMAPINFOHEADER); diff --git a/src/libs/zmbv/zmbv_vfw.rc b/src/libs/zmbv/zmbv_vfw.rc index 87fbe8a..643418a 100644 --- a/src/libs/zmbv/zmbv_vfw.rc +++ b/src/libs/zmbv/zmbv_vfw.rc @@ -59,7 +59,7 @@ CAPTION "DOSBox Video Codec v0.1" FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN DEFPUSHBUTTON "OK",IDOK,131,34,29,14 - CTEXT "Zipped Motion Block Video v 0.1\nCopyright 2009-2011 DOSBox Team", + CTEXT "Zipped Motion Block Video v 0.1\nCopyright 2009-2019 DOSBox Team", IDC_STATIC,7,7,153,25,SS_NOPREFIX PUSHBUTTON "Email author",IDC_EMAIL,7,34,50,14 PUSHBUTTON "Visit home page",IDC_HOMEPAGE,59,34,58,14 diff --git a/src/misc/cross.cpp b/src/misc/cross.cpp index 25260b2..23ff71b 100644 --- a/src/misc/cross.cpp +++ b/src/misc/cross.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -21,6 +21,7 @@ #include "cross.h" #include "support.h" #include +#include #include #ifdef HW_RVL @@ -93,7 +94,7 @@ void Cross::CreatePlatformConfigDir(std::string& in) { in += "\\DOSBox"; mkdir(in.c_str()); #elif defined(MACOSX) - in = "~/Library/Preferences/"; + in = "~/Library/Preferences"; ResolveHomedir(in); //Don't create it. Assume it exists #elif defined(HW_RVL) @@ -178,6 +179,7 @@ dir_information* open_directory(const char* dirname) { } bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory) { + if (!dirp) return false; dirp->handle = FindFirstFile(dirp->base_path, &dirp->search_data); if (INVALID_HANDLE_VALUE == dirp->handle) { return false; @@ -192,6 +194,7 @@ bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_dire } bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory) { + if (!dirp) return false; int result = FindNextFile(dirp->handle, &dirp->search_data); if (result==0) return false; @@ -204,7 +207,7 @@ bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_direc } void close_directory(dir_information* dirp) { - if (dirp->handle != INVALID_HANDLE_VALUE) { + if (dirp && dirp->handle != INVALID_HANDLE_VALUE) { FindClose(dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; } @@ -220,37 +223,12 @@ dir_information* open_directory(const char* dirname) { } bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory) { - struct dirent* dentry = readdir(dirp->dir); - if (dentry==NULL) { - return false; - } - -// safe_strncpy(entry_name,dentry->d_name,(FILENAME_MAXd_name,CROSS_LEN); - -#ifdef DIRENT_HAS_D_TYPE - if(dentry->d_type == DT_DIR) { - is_directory = true; - return true; - } else if(dentry->d_type == DT_REG) { - is_directory = false; - return true; - } -#endif - - // probably use d_type here instead of a full stat() - static char buffer[2*CROSS_LEN] = { 0 }; - buffer[0] = 0; - strcpy(buffer,dirp->base_path); - strcat(buffer,entry_name); - struct stat status; - if (stat(buffer,&status)==0) is_directory = (S_ISDIR(status.st_mode)>0); - else is_directory = false; - - return true; + if (!dirp) return false; + return read_directory_next(dirp,entry_name,is_directory); } bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory) { + if (!dirp) return false; struct dirent* dentry = readdir(dirp->dir); if (dentry==NULL) { return false; @@ -269,21 +247,81 @@ bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_direc } #endif - // probably use d_type here instead of a full stat() - static char buffer[2*CROSS_LEN] = { 0 }; + //Maybe only for DT_UNKNOWN if DIRENT_HAD_D_TYPE.. + static char buffer[2 * CROSS_LEN + 1] = { 0 }; + static char split[2] = { CROSS_FILESPLIT , 0 }; buffer[0] = 0; strcpy(buffer,dirp->base_path); + size_t buflen = strlen(buffer); + if (buflen && buffer[buflen - 1] != CROSS_FILESPLIT ) strcat(buffer, split); strcat(buffer,entry_name); struct stat status; - if (stat(buffer,&status)==0) is_directory = (S_ISDIR(status.st_mode)>0); + if (stat(buffer,&status) == 0) is_directory = (S_ISDIR(status.st_mode)>0); else is_directory = false; return true; } void close_directory(dir_information* dirp) { - closedir(dirp->dir); + if (dirp) closedir(dirp->dir); } #endif + +FILE *fopen_wrap(const char *path, const char *mode) { +#if defined(WIN32) || defined(OS2) + ; +#elif defined (MACOSX) + ; +#else +#if defined (HAVE_REALPATH) + char work[CROSS_LEN] = {0}; + strncpy(work,path,CROSS_LEN-1); + char* last = strrchr(work,'/'); + + if (last) { + if (last != work) { + *last = 0; + //If this compare fails, then we are dealing with files in / + //Which is outside the scope, but test anyway. + //However as realpath only works for exising files. The testing is + //in that case not done against new files. + } + char* check = realpath(work,NULL); + if (check) { + if ( ( strlen(check) == 5 && strcmp(check,"/proc") == 0) || strncmp(check,"/proc/",6) == 0) { +// LOG_MSG("lst hit %s blocking!",path); + free(check); + return NULL; + } + free(check); + } + } + +#if 0 +//Lightweight version, but then existing files can still be read, which is not ideal + if (strpbrk(mode,"aw+") != NULL) { + LOG_MSG("pbrk ok"); + char* check = realpath(path,NULL); + //Will be null if file doesn't exist.... ENOENT + //TODO What about unlink /proc/self/mem and then create it ? + //Should be safe for what we want.. + if (check) { + if (strncmp(check,"/proc/",6) == 0) { + free(check); + return NULL; + } + free(check); + } + } +*/ +#endif //0 + +#endif //HAVE_REALPATH +#endif + + return fopen(path,mode); +} + + diff --git a/src/misc/messages.cpp b/src/misc/messages.cpp index b266ace..b0e6594 100644 --- a/src/misc/messages.cpp +++ b/src/misc/messages.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -89,7 +89,7 @@ static void LoadMessageFile(const char * fname) { if (*parser!=10 && *parser!=13) { *writer++=*parser; } - *parser++; + parser++; } *writer=0; /* New string name */ diff --git a/src/misc/programs.cpp b/src/misc/programs.cpp index 6ba02c2..ea33b0c 100644 --- a/src/misc/programs.cpp +++ b/src/misc/programs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -78,7 +78,7 @@ static Bitu PROGRAMS_Handler(void) { HostPt writer=(HostPt)&index; for (;size>0;size--) *writer++=mem_readb(reader++); Program * new_program; - if (index > internal_progs.size()) E_Exit("something is messing with the memory"); + if (index >= internal_progs.size()) E_Exit("something is messing with the memory"); PROGRAMS_Main * handler = internal_progs[index]; (*handler)(&new_program); new_program->Run(); @@ -138,6 +138,7 @@ void Program::WriteOut(const char * format,...) { va_end(msg); Bit16u size = (Bit16u)strlen(buf); + dos.internal_output=true; for(Bit16u i = 0; i < size;i++) { Bit8u out;Bit16u s=1; if (buf[i] == 0xA && last_written_character != 0xD) { @@ -146,6 +147,7 @@ void Program::WriteOut(const char * format,...) { last_written_character = out = buf[i]; DOS_WriteFile(STDOUT,&out,&s); } + dos.internal_output=false; // DOS_WriteFile(STDOUT,(Bit8u *)buf,&size); } @@ -153,6 +155,7 @@ void Program::WriteOut(const char * format,...) { void Program::WriteOut_NoParsing(const char * format) { Bit16u size = (Bit16u)strlen(format); char const* buf = format; + dos.internal_output=true; for(Bit16u i = 0; i < size;i++) { Bit8u out;Bit16u s=1; if (buf[i] == 0xA && last_written_character != 0xD) { @@ -161,6 +164,7 @@ void Program::WriteOut_NoParsing(const char * format) { last_written_character = out = buf[i]; DOS_WriteFile(STDOUT,&out,&s); } + dos.internal_output=false; // DOS_WriteFile(STDOUT,(Bit8u *)format,&size); } @@ -216,9 +220,16 @@ Bitu Program::GetEnvCount(void) { } bool Program::SetEnv(const char * entry,const char * new_string) { - PhysPt env_read=PhysMake(psp->GetEnvironment(),0); - PhysPt env_write=env_read; - char env_string[1024+1]; + PhysPt env_read = PhysMake(psp->GetEnvironment(),0); + + //Get size of environment. + DOS_MCB mcb(psp->GetEnvironment()-1); + Bit16u envsize = mcb.GetSize()*16; + + + PhysPt env_write = env_read; + PhysPt env_write_start = env_read; + char env_string[1024+1] = { 0 }; do { MEM_StrCopy(env_read,env_string,1024); if (!env_string[0]) break; @@ -231,16 +242,19 @@ bool Program::SetEnv(const char * entry,const char * new_string) { } while (1); /* TODO Maybe save the program name sometime. not really needed though */ /* Save the new entry */ + + //ensure room + if (envsize <= (env_write-env_write_start) + strlen(entry) + 1 + strlen(new_string) + 2) return false; + if (new_string[0]) { std::string bigentry(entry); for (std::string::iterator it = bigentry.begin(); it != bigentry.end(); ++it) *it = toupper(*it); - sprintf(env_string,"%s=%s",bigentry.c_str(),new_string); -// sprintf(env_string,"%s=%s",entry,new_string); //oldcode + snprintf(env_string,1024+1,"%s=%s",bigentry.c_str(),new_string); MEM_BlockWrite(env_write,env_string,(Bitu)(strlen(env_string)+1)); env_write += (PhysPt)(strlen(env_string)+1); } /* Clear out the final piece of the environment */ - mem_writed(env_write,0); + mem_writeb(env_write,0); return true; } @@ -538,7 +552,7 @@ void CONFIG::Run(void) { std::string::size_type spcpos = pvars[0].find_first_of(' '); // split on the ' ' if (spcpos != std::string::npos) { - pvars.insert(++pvars.begin(),pvars[0].substr(spcpos+1)); + pvars.insert(pvars.begin()+1,pvars[0].substr(spcpos+1)); pvars[0].erase(spcpos); } switch(pvars.size()) { @@ -575,6 +589,7 @@ void CONFIG::Run(void) { // it's a property name std::string val = sec->GetPropValue(pvars[0].c_str()); WriteOut("%s",val.c_str()); + first_shell->SetEnv("CONFIG",val.c_str()); } break; } @@ -592,6 +607,7 @@ void CONFIG::Run(void) { return; } WriteOut("%s",val.c_str()); + first_shell->SetEnv("CONFIG",val.c_str()); break; } default: @@ -630,7 +646,7 @@ void CONFIG::Run(void) { if ((equpos != std::string::npos) && ((spcpos == std::string::npos) || (equpos < spcpos))) { // If we have a '=' possibly before a ' ' split on the = - pvars.insert(++pvars.begin(),pvars[0].substr(equpos+1)); + pvars.insert(pvars.begin()+1,pvars[0].substr(equpos+1)); pvars[0].erase(equpos); // As we had a = the first thing must be a property now Section* sec=control->GetSectionFromProperty(pvars[0].c_str()); @@ -644,7 +660,7 @@ void CONFIG::Run(void) { if ((spcpos != std::string::npos) && ((equpos == std::string::npos) || (spcpos < equpos))) { // ' ' before a possible '=', split on the ' ' - pvars.insert(++pvars.begin(),pvars[0].substr(spcpos+1)); + pvars.insert(pvars.begin()+1,pvars[0].substr(spcpos+1)); pvars[0].erase(spcpos); } // check if the first parameter is a section or property @@ -703,9 +719,14 @@ void CONFIG::Run(void) { // Input has been parsed (pvar[0]=section, [1]=property, [2]=value) // now execute Section* tsec = control->GetSection(pvars[0]); - std::string value; - value += pvars[2]; + std::string value(pvars[2]); + //Due to parsing there can be a = at the start of value. + while (value.size() && (value.at(0) ==' ' ||value.at(0) =='=') ) value.erase(0,1); for(Bitu i = 3; i < pvars.size(); i++) value += (std::string(" ") + pvars[i]); + if (value.empty() ) { + WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX")); + return; + } std::string inputline = pvars[1] + "=" + value; tsec->ExecuteDestroy(false); diff --git a/src/misc/setup.cpp b/src/misc/setup.cpp index 25bd5a4..4794628 100644 --- a/src/misc/setup.cpp +++ b/src/misc/setup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -37,7 +37,7 @@ void Value::destroy() throw(){ if (type == V_STRING) delete _string; } -Value& Value::copy(Value const& in) throw(WrongType) { +Value& Value::copy(Value const& in) { if (this != &in) { //Selfassigment! if(type != V_NONE && type != in.type) throw WrongType(); destroy(); @@ -55,27 +55,27 @@ void Value::plaincopy(Value const& in) throw(){ if(type == V_STRING) _string = new string(*in._string); } -Value::operator bool () const throw(WrongType) { +Value::operator bool () const { if(type != V_BOOL) throw WrongType(); return _bool; } -Value::operator Hex () const throw(WrongType) { +Value::operator Hex () const { if(type != V_HEX) throw WrongType(); return _hex; } -Value::operator int () const throw(WrongType) { +Value::operator int () const { if(type != V_INT) throw WrongType(); return _int; } -Value::operator double () const throw(WrongType) { +Value::operator double () const { if(type != V_DOUBLE) throw WrongType(); return _double; } -Value::operator char const* () const throw(WrongType) { +Value::operator char const* () const { if(type != V_STRING) throw WrongType(); return _string->c_str(); } @@ -105,7 +105,7 @@ bool Value::operator==(Value const& other) { } return false; } -bool Value::SetValue(string const& in,Etype _type) throw(WrongType) { +bool Value::SetValue(string const& in,Etype _type) { /* Throw exception if the current type isn't the wanted type * Unless the wanted type is current. */ @@ -176,13 +176,13 @@ bool Value::set_bool(string const &in) { lowcase(result); _bool = true; // TODO if(!result.size()) return false; - + if(result=="0" || result=="disabled" || result=="false" || result=="off") { _bool = false; } else if(result=="1" || result=="enabled" || result=="true" || result=="on") { _bool = true; } else return false; - + return true; } @@ -227,7 +227,7 @@ bool Property::CheckValue(Value const& in, bool warn){ return true; } } - if(warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); + if (warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); return false; } @@ -243,108 +243,141 @@ char const* Property::Get_help() { return MSG_Get(result.c_str()); } +bool Prop_int::SetVal(Value const& in, bool forced, bool warn) { + if (forced) { + value = in; + return true; + } else if (!suggested_values.empty()){ + if ( CheckValue(in,warn) ) { + value = in; + return true; + } else { + value = default_value; + return false; + } + } else { + //Handle ranges if specified + int mi = min; + int ma = max; + int va = static_cast(Value(in)); + //No ranges + if (mi == -1 && ma == -1) { value = in; return true;} + + //Inside range + if (va >= mi && va <= ma) { value = in; return true;} + + //Outside range, set it to the closest boundary + if (va > ma ) va = ma; else va = mi; + + if (warn) LOG_MSG("%s is outside the allowed range %s-%s for variable: %s.\nIt has been set to the closest boundary: %d.",in.ToString().c_str(),min.ToString().c_str(),max.ToString().c_str(),propname.c_str(),va); + + value = va; + return true; + } +} bool Prop_int::CheckValue(Value const& in, bool warn) { - if(suggested_values.empty() && Property::CheckValue(in,warn)) return true; +// if(!suggested_values.empty() && Property::CheckValue(in,warn)) return true; + if(!suggested_values.empty()) return Property::CheckValue(in,warn); + LOG_MSG("still used ?"); //No >= and <= in Value type and == is ambigious int mi = min; int ma = max; int va = static_cast(Value(in)); - if(mi == -1 && ma == -1) return true; + if (mi == -1 && ma == -1) return true; if (va >= mi && va <= ma) return true; - if(warn) LOG_MSG("%s lies outside the range %s-%s for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),min.ToString().c_str(),max.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); + + if (warn) LOG_MSG("%s lies outside the range %s-%s for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),min.ToString().c_str(),max.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); return false; } -bool Prop_double::SetValue(std::string const& input){ +bool Prop_double::SetValue(std::string const& input) { Value val; if(!val.SetValue(input,Value::V_DOUBLE)) return false; return SetVal(val,false,true); } -//void Property::SetValue(char* input){ +//void Property::SetValue(char* input){ // value.SetValue(input, Value::V_CURRENT); //} -bool Prop_int::SetValue(std::string const& input){; +bool Prop_int::SetValue(std::string const& input) { Value val; - if(!val.SetValue(input,Value::V_INT)) return false; + if (!val.SetValue(input,Value::V_INT)) return false; bool retval = SetVal(val,false,true); return retval; } -bool Prop_string::SetValue(std::string const& input){ +bool Prop_string::SetValue(std::string const& input) { //Special version for lowcase stuff std::string temp(input); - //suggested values always case insensitive. + //suggested values always case insensitive. //If there are none then it can be paths and such which are case sensitive - if(!suggested_values.empty()) lowcase(temp); + if (!suggested_values.empty()) lowcase(temp); Value val(temp,Value::V_STRING); return SetVal(val,false,true); } -bool Prop_string::CheckValue(Value const& in, bool warn){ - if(suggested_values.empty()) return true; +bool Prop_string::CheckValue(Value const& in, bool warn) { + if (suggested_values.empty()) return true; for(iter it = suggested_values.begin();it != suggested_values.end();it++) { if ( (*it) == in) { //Match! return true; } - if((*it).ToString() == "%u") { - Bitu value; + if ((*it).ToString() == "%u") { + Bit32u value; if(sscanf(in.ToString().c_str(),"%u",&value) == 1) { return true; } } } - if(warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset it to default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); + if (warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); return false; } -bool Prop_path::SetValue(std::string const& input){ +bool Prop_path::SetValue(std::string const& input) { //Special version to merge realpath with it Value val(input,Value::V_STRING); bool retval = SetVal(val,false,true); - if(input.empty()) { + if (input.empty()) { realpath = ""; return false; } std::string workcopy(input); Cross::ResolveHomedir(workcopy); //Parse ~ and friends //Prepend config directory in it exists. Check for absolute paths later - if( current_config_dir.empty()) realpath = workcopy; + if ( current_config_dir.empty()) realpath = workcopy; else realpath = current_config_dir + CROSS_FILESPLIT + workcopy; //Absolute paths if (Cross::IsPathAbsolute(workcopy)) realpath = workcopy; return retval; } - -bool Prop_bool::SetValue(std::string const& input){ + +bool Prop_bool::SetValue(std::string const& input) { return value.SetValue(input,Value::V_BOOL); } -bool Prop_hex::SetValue(std::string const& input){ +bool Prop_hex::SetValue(std::string const& input) { Value val; val.SetValue(input,Value::V_HEX); return SetVal(val,false,true); } -void Prop_multival::make_default_value(){ +void Prop_multival::make_default_value() { Bitu i = 1; Property *p = section->Get_prop(0); - if(!p) return; + if (!p) return; std::string result = p->Get_Default_Value().ToString(); while( (p = section->Get_prop(i++)) ) { std::string props = p->Get_Default_Value().ToString(); - if(props == "") continue; - result += seperator; result += props; + if (props == "") continue; + result += separator; result += props; } Value val(result,Value::V_STRING); SetVal(val,false,true); } - - //TODO checkvalue stuff bool Prop_multival_remain::SetValue(std::string const& input) { Value val(input,Value::V_STRING); @@ -354,30 +387,30 @@ bool Prop_multival_remain::SetValue(std::string const& input) { int i = 0,number_of_properties = 0; Property *p = section->Get_prop(0); //No properties in this section. do nothing - if(!p) return false; - + if (!p) return false; + while( (section->Get_prop(number_of_properties)) ) number_of_properties++; - + string::size_type loc = string::npos; while( (p = section->Get_prop(i++)) ) { - //trim leading seperators - loc = local.find_first_not_of(seperator); - if(loc != string::npos) local.erase(0,loc); - loc = local.find_first_of(seperator); + //trim leading separators + loc = local.find_first_not_of(separator); + if (loc != string::npos) local.erase(0,loc); + loc = local.find_first_of(separator); string in = "";//default value - /* when i == number_of_properties add the total line. (makes more then + /* when i == number_of_properties add the total line. (makes more then * one string argument possible for parameters of cpu) */ - if(loc != string::npos && i < number_of_properties) { //seperator found + if (loc != string::npos && i < number_of_properties) { //separator found in = local.substr(0,loc); local.erase(0,loc+1); - } else if(local.size()) { //last argument or last property + } else if (local.size()) { //last argument or last property in = local; local = ""; } //Test Value. If it fails set default Value valtest (in,p->Get_type()); - if(!p->CheckValue(valtest,true)) { + if (!p->CheckValue(valtest,true)) { make_default_value(); return false; } @@ -395,28 +428,50 @@ bool Prop_multival::SetValue(std::string const& input) { int i = 0; Property *p = section->Get_prop(0); //No properties in this section. do nothing - if(!p) return false; + if (!p) return false; + Value::Etype prevtype = Value::V_NONE; + string prevargument = ""; + string::size_type loc = string::npos; while( (p = section->Get_prop(i++)) ) { - //trim leading seperators - loc = local.find_first_not_of(seperator); - if(loc != string::npos) local.erase(0,loc); - loc = local.find_first_of(seperator); + //trim leading separators + loc = local.find_first_not_of(separator); + if (loc != string::npos) local.erase(0,loc); + loc = local.find_first_of(separator); string in = "";//default value - if(loc != string::npos) { //seperator found + if (loc != string::npos) { //separator found in = local.substr(0,loc); local.erase(0,loc+1); - } else if(local.size()) { //last argument + } else if (local.size()) { //last argument in = local; local = ""; } - //Test Value. If it fails set default - Value valtest (in,p->Get_type()); - if(!p->CheckValue(valtest,true)) { - make_default_value(); - return false; + + if (p->Get_type() == Value::V_STRING) { + //Strings are only checked against the suggested values list. + //Test Value. If it fails set default + Value valtest (in,p->Get_type()); + if (!p->CheckValue(valtest,true)) { + make_default_value(); + return false; + } + p->SetValue(in); + } else { + //Non-strings can have more things, conversion alone is not enough (as invalid values as converted to 0) + bool r = p->SetValue(in); + if (!r) { + if (in.empty() && p->Get_type() == prevtype ) { + //Nothing there, but same type of variable, so repeat it (sensitivity) + in = prevargument; + p->SetValue(in); + } else { + //Something was there to be parsed or not the same type. Invalidate entire property. + make_default_value(); + } + } } - p->SetValue(in); + prevtype = p->Get_type(); + prevargument = in; } return retval; @@ -425,11 +480,10 @@ bool Prop_multival::SetValue(std::string const& input) { const std::vector& Property::GetValues() const { return suggested_values; } -const std::vector& Prop_multival::GetValues() const -{ +const std::vector& Prop_multival::GetValues() const { Property *p = section->Get_prop(0); //No properties in this section. do nothing - if(!p) return suggested_values; + if (!p) return suggested_values; int i =0; while( (p = section->Get_prop(i++)) ) { std::vector v = p->GetValues(); @@ -477,16 +531,19 @@ Prop_bool* Section_prop::Add_bool(string const& _propname, Property::Changeable: properties.push_back(test); return test; } + Prop_hex* Section_prop::Add_hex(string const& _propname, Property::Changeable::Value when, Hex _value) { Prop_hex* test=new Prop_hex(_propname,when,_value); properties.push_back(test); return test; } + Prop_multival* Section_prop::Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) { Prop_multival* test = new Prop_multival(_propname,when,sep); properties.push_back(test); return test; } + Prop_multival_remain* Section_prop::Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) { Prop_multival_remain* test = new Prop_multival_remain(_propname,when,sep); properties.push_back(test); @@ -495,7 +552,7 @@ Prop_multival_remain* Section_prop::Add_multiremain(std::string const& _propname int Section_prop::Get_int(string const&_propname) const { for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ + if ((*tel)->propname==_propname){ return ((*tel)->GetValue()); } } @@ -504,15 +561,16 @@ int Section_prop::Get_int(string const&_propname) const { bool Section_prop::Get_bool(string const& _propname) const { for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ + if ((*tel)->propname==_propname){ return ((*tel)->GetValue()); } } return false; } + double Section_prop::Get_double(string const& _propname) const { for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ + if ((*tel)->propname==_propname){ return ((*tel)->GetValue()); } } @@ -521,9 +579,9 @@ double Section_prop::Get_double(string const& _propname) const { Prop_path* Section_prop::Get_path(string const& _propname) const { for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ + if ((*tel)->propname==_propname){ Prop_path* val = dynamic_cast((*tel)); - if(val) return val; else return NULL; + if (val) return val; else return NULL; } } return NULL; @@ -541,16 +599,16 @@ Prop_multival* Section_prop::Get_multival(string const& _propname) const { Prop_multival_remain* Section_prop::Get_multivalremain(string const& _propname) const { for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ + if ((*tel)->propname==_propname){ Prop_multival_remain* val = dynamic_cast((*tel)); - if(val) return val; else return NULL; + if (val) return val; else return NULL; } } return NULL; } Property* Section_prop::Get_prop(int index){ for(it tel=properties.begin();tel!=properties.end();tel++){ - if(!index--) return (*tel); + if (!index--) return (*tel); } return NULL; } @@ -572,23 +630,23 @@ Hex Section_prop::Get_hex(string const& _propname) const { return 0; } -void trim(string& in) { - string::size_type loc = in.find_first_not_of(" \r\t\f\n"); - if(loc != string::npos) in.erase(0,loc); - loc = in.find_last_not_of(" \r\t\f\n"); - if(loc != string::npos) in.erase(loc+1); -} - -//TODO double c_str bool Section_prop::HandleInputline(string const& gegevens){ string str1 = gegevens; string::size_type loc = str1.find('='); - if(loc == string::npos) return false; + if (loc == string::npos) return false; string name = str1.substr(0,loc); string val = str1.substr(loc + 1); + + /* Remove quotes around value */ + trim(val); + string::size_type length = val.length(); + if (length > 1 && + ((val[0] == '\"' && val[length - 1] == '\"' ) || + (val[0] == '\'' && val[length - 1] == '\'')) + ) val = val.substr(1,length - 2); /* trim the results incase there were spaces somewhere */ trim(name);trim(val); - for(it tel=properties.begin();tel!=properties.end();tel++){ + for(it tel = properties.begin();tel != properties.end();tel++){ if(!strcasecmp((*tel)->propname.c_str(),name.c_str())){ return (*tel)->SetValue(val); } @@ -598,24 +656,30 @@ bool Section_prop::HandleInputline(string const& gegevens){ void Section_prop::PrintData(FILE* outfile) const { /* Now print out the individual section entries */ - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - fprintf(outfile,"%s=%s\n",(*tel)->propname.c_str(),(*tel)->GetValue().ToString().c_str()); + size_t len = 0; + // Determine maximum length of the props in this section + for(const_it tel = properties.begin();tel != properties.end();tel++) { + if ((*tel)->propname.length() > len) + len = (*tel)->propname.length(); + } + + for(const_it tel = properties.begin();tel != properties.end();tel++) { + fprintf(outfile,"%-*s = %s\n", len, (*tel)->propname.c_str(), (*tel)->GetValue().ToString().c_str()); } } -//TODO geen noodzaak voor 2 keer c_str -string Section_prop::GetPropValue(string const& _property) const{ +string Section_prop::GetPropValue(string const& _property) const { for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if(!strcasecmp((*tel)->propname.c_str(),_property.c_str())){ + if (!strcasecmp((*tel)->propname.c_str(),_property.c_str())){ return (*tel)->GetValue().ToString(); } } return NO_SUCH_PROPERTY; } -bool Section_line::HandleInputline(string const& line){ - data+=line; - data+="\n"; +bool Section_line::HandleInputline(string const& line) { + if (!data.empty()) data += "\n"; //Add return to previous line in buffer + data += line; return true; } @@ -632,16 +696,16 @@ bool Config::PrintConfig(char const * const configfilename) const { FILE* outfile=fopen(configfilename,"w+t"); if(outfile==NULL) return false; - /* Print start of configfile and add an return to improve readibility. */ + /* Print start of configfile and add a return to improve readibility. */ fprintf(outfile,MSG_Get("CONFIGFILE_INTRO"),VERSION); fprintf(outfile,"\n"); for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ /* Print out the Section header */ - Section_prop *sec = dynamic_cast(*tel); strcpy(temp,(*tel)->GetName()); lowcase(temp); fprintf(outfile,"[%s]\n",temp); + Section_prop *sec = dynamic_cast(*tel); if (sec) { Property *p; size_t i = 0, maxwidth = 0; @@ -651,15 +715,15 @@ bool Config::PrintConfig(char const * const configfilename) const { } i=0; char prefix[80]; - snprintf(prefix,80, "\n# %*s ", maxwidth, ""); - while ((p = sec->Get_prop(i++))) { + snprintf(prefix,80, "\n# %*s ", (int)maxwidth, ""); + while ((p = sec->Get_prop(i++))) { std::string help = p->Get_help(); std::string::size_type pos = std::string::npos; while ((pos = help.find("\n", pos+1)) != std::string::npos) { help.replace(pos, 1, prefix); } - - fprintf(outfile, "# %*s: %s", maxwidth, p->propname.c_str(), help.c_str()); + + fprintf(outfile, "# %*s: %s", (int)maxwidth, p->propname.c_str(), help.c_str()); std::vector values = p->GetValues(); if (!values.empty()) { @@ -691,7 +755,7 @@ bool Config::PrintConfig(char const * const configfilename) const { helpstr++; } } - + fprintf(outfile,"\n"); (*tel)->PrintData(outfile); fprintf(outfile,"\n"); /* Always an empty line between sections */ @@ -699,9 +763,9 @@ bool Config::PrintConfig(char const * const configfilename) const { fclose(outfile); return true; } - -Section_prop* Config::AddSection_prop(char const * const _name,void (*_initfunction)(Section*),bool canchange){ + +Section_prop* Config::AddSection_prop(char const * const _name,void (*_initfunction)(Section*),bool canchange) { Section_prop* blah = new Section_prop(_name); blah->AddInitFunction(_initfunction,canchange); sectionlist.push_back(blah); @@ -709,7 +773,7 @@ Section_prop* Config::AddSection_prop(char const * const _name,void (*_initfunct } Section_prop::~Section_prop() { -//ExecuteDestroy should be here else the destroy functions use destroyed properties + //ExecuteDestroy should be here else the destroy functions use destroyed properties ExecuteDestroy(true); /* Delete properties themself (properties stores the pointer of a prop */ for(it prop = properties.begin(); prop != properties.end(); prop++) @@ -717,7 +781,7 @@ Section_prop::~Section_prop() { } -Section_line* Config::AddSection_line(char const * const _name,void (*_initfunction)(Section*)){ +Section_line* Config::AddSection_line(char const * const _name,void (*_initfunction)(Section*)) { Section_line* blah = new Section_line(_name); blah->AddInitFunction(_initfunction); sectionlist.push_back(blah); @@ -726,7 +790,7 @@ Section_line* Config::AddSection_line(char const * const _name,void (*_initfunct void Config::Init() { - for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ + for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++) { (*tel)->ExecuteInit(); } } @@ -765,37 +829,36 @@ Config::~Config() { } } -Section* Config::GetSection(int index){ +Section* Config::GetSection(int index) { for (it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ if (!index--) return (*tel); } return NULL; } -//c_str() 2x -Section* Config::GetSection(string const& _sectionname) const{ + +Section* Config::GetSection(string const& _sectionname) const { for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ if (!strcasecmp((*tel)->GetName(),_sectionname.c_str())) return (*tel); } return NULL; } -Section* Config::GetSectionFromProperty(char const * const prop) const{ +Section* Config::GetSectionFromProperty(char const * const prop) const { for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ if ((*tel)->GetPropValue(prop) != NO_SUCH_PROPERTY) return (*tel); } return NULL; } - -bool Config::ParseConfigFile(char const * const configfilename){ +bool Config::ParseConfigFile(char const * const configfilename) { //static bool first_configfile = true; ifstream in(configfilename); if (!in) return false; const char * settings_type; settings_type = (configfiles.size() == 0)? "primary":"additional"; configfiles.push_back(configfilename); - - LOG_MSG("CONFIG:Loading %s settings from config file %s", settings_type,configfilename); + + LOG_MSG("CONFIG: Loading %s settings from config file %s", settings_type,configfilename); //Get directory from configfilename, used with relative paths. current_config_dir=configfilename; @@ -807,7 +870,7 @@ bool Config::ParseConfigFile(char const * const configfilename){ Section* currentsection = NULL; Section* testsec = NULL; while (getline(in,gegevens)) { - + /* strip leading/trailing whitespace */ trim(gegevens); if(!gegevens.size()) continue; @@ -868,7 +931,7 @@ void Config::ParseEnv(char ** envp) { } } -void Config::SetStartUp(void (*_function)(void)) { +void Config::SetStartUp(void (*_function)(void)) { _start_function=_function; } @@ -955,7 +1018,7 @@ bool CommandLine::FindStringRemain(char const * const name,std::string & value) return true; } -/* Only used for parsing command.com /C +/* Only used for parsing command.com /C * Allowing /C dir and /Cdir * Restoring quotes back into the commands so command /C mount d "/tmp/a b" works as intended */ @@ -989,8 +1052,8 @@ bool CommandLine::FindStringRemainBegin(char const * const name,std::string & va } bool CommandLine::GetStringRemain(std::string & value) { - if(!cmds.size()) return false; - + if (!cmds.size()) return false; + cmd_it it=cmds.begin();value=(*it++); for(;it != cmds.end();it++) { value+=" "; @@ -998,7 +1061,7 @@ bool CommandLine::GetStringRemain(std::string & value) { } return true; } - + unsigned int CommandLine::GetCount(void) { return (unsigned int)cmds.size(); @@ -1042,7 +1105,7 @@ int CommandLine::GetParameterFromList(const char* const params[], std::vector #include "dosbox.h" +#include "cross.h" #include "debug.h" #include "support.h" #include "video.h" @@ -43,7 +44,13 @@ void lowcase(std::string &str) { int (*tf)(int) = std::tolower; std::transform(str.begin(), str.end(), str.begin(), tf); } - + +void trim(std::string &str) { + std::string::size_type loc = str.find_first_not_of(" \r\t\f\n"); + if (loc != std::string::npos) str.erase(0,loc); + loc = str.find_last_not_of(" \r\t\f\n"); + if (loc != std::string::npos) str.erase(loc+1); +} /* Ripped some source from freedos for this one. @@ -176,9 +183,11 @@ void E_Exit(const char * format,...) { #endif va_list msg; va_start(msg,format); - vsprintf(buf,format,msg); + vsnprintf(buf,sizeof(buf),format,msg); va_end(msg); - strcat(buf,"\n"); + + buf[sizeof(buf) - 1] = '\0'; + //strcat(buf,"\n"); catcher should handle the end of line.. throw(buf); } diff --git a/src/platform/visualc/config.h b/src/platform/visualc/config.h index fffd256..948a4be 100644 --- a/src/platform/visualc/config.h +++ b/src/platform/visualc/config.h @@ -3,6 +3,9 @@ /* Define to 1 to enable internal debugger, requires libcurses */ #define C_DEBUG 0 +/* Define to 1 to enable output=ddraw */ +#define C_DDRAW 1 + /* Define to 1 to enable screenshots, requires libpng */ #define C_SSHOT 1 @@ -19,7 +22,11 @@ #define C_HEAVY_DEBUG 0 /* The type of cpu this host has */ +#ifdef _M_X64 +#define C_TARGETCPU X86_64 +#else // _M_IX86 #define C_TARGETCPU X86 +#endif //#define C_TARGETCPU X86_64 /* Define to 1 to use x86 dynamic cpu core */ @@ -29,13 +36,19 @@ #define C_DYNREC 0 /* Enable memory function inlining in */ -#define C_CORE_INLINE 0 +#define C_CORE_INLINE 1 /* Enable the FPU module, still only for beta testing */ #define C_FPU 1 /* Define to 1 to use a x86 assembly fpu core */ +#ifdef _M_X64 +//No support for inline asm with visual studio in x64 bit mode. +//This means that non-dynamic cores can't use the better fpu emulation. +#define C_FPU_X86 0 +#else // _M_IX86 #define C_FPU_X86 1 +#endif /* Define to 1 to use a unaligned memory access */ #define C_UNALIGNED_MEMORY 1 @@ -46,10 +59,7 @@ /* environ can be linked */ #define ENVIRON_LINKED 1 -/* Define to 1 if you have the header file. */ -#define HAVE_DDRAW_H 1 - -/* Define to 1 if you want serial passthrough support (Win32 only). */ +/* Define to 1 if you want serial passthrough support. */ #define C_DIRECTSERIAL 1 #define GCC_ATTRIBUTE(x) /* attribute not supported */ @@ -63,16 +73,26 @@ #pragma warning(disable : 4996) #endif -typedef double Real64; +typedef double Real64; /* The internal types */ -typedef unsigned char Bit8u; -typedef signed char Bit8s; -typedef unsigned short Bit16u; -typedef signed short Bit16s; -typedef unsigned long Bit32u; -typedef signed long Bit32s; -typedef unsigned __int64 Bit64u; -typedef signed __int64 Bit64s; -typedef unsigned int Bitu; -typedef signed int Bits; - +typedef unsigned char Bit8u; +typedef signed char Bit8s; +typedef unsigned short Bit16u; +typedef signed short Bit16s; +typedef unsigned int Bit32u; +typedef signed int Bit32s; +typedef unsigned __int64 Bit64u; +typedef signed __int64 Bit64s; +#define sBit32t +#define sBit64t "I64" +#define sBit32fs(a) sBit32t #a +#define sBit64fs(a) sBit64t #a +#ifdef _M_X64 +typedef Bit64u Bitu; +typedef Bit64s Bits; +#define sBitfs sBit64fs +#else // _M_IX86 +typedef Bit32u Bitu; +typedef Bit32s Bits; +#define sBitfs sBit32fs +#endif diff --git a/src/platform/wii/config.h b/src/platform/wii/config.h index 81bfa54..e221b39 100644 --- a/src/platform/wii/config.h +++ b/src/platform/wii/config.h @@ -1,8 +1,11 @@ -#define VERSION "0.74" +#define VERSION "SVN-r4301" /* Define to 1 to enable internal debugger, requires libcurses */ #define C_DEBUG 0 +/* Define to 1 to enable output=ddraw */ +#define C_DDRAW 0 + /* Define to 1 to enable screenshots, requires libpng */ #define C_SSHOT 0 @@ -68,6 +71,11 @@ typedef unsigned long Bit32u; typedef int Bit32s; typedef unsigned long long Bit64u; typedef signed long long Bit64s; +#define sBit32t +#define sBit64t "I64" +#define sBit32fs(a) sBit32t #a +#define sBit64fs(a) sBit64t #a typedef unsigned int Bitu; typedef signed int Bits; +#define sBitfs sBit32fs diff --git a/src/shell/shell.cpp b/src/shell/shell.cpp index 147abe7..a0b8421 100644 --- a/src/shell/shell.cpp +++ b/src/shell/shell.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -34,7 +34,7 @@ Bitu call_shellstop; /* Larger scope so shell_del autoexec can use it to * remove things from the environment */ -Program * first_shell = 0; +DOS_Shell * first_shell = 0; static Bitu shellstop_handler(void) { return CBRET_STOP; @@ -43,6 +43,11 @@ static Bitu shellstop_handler(void) { static void SHELL_ProgramStart(Program * * make) { *make = new DOS_Shell; } +//Repeat it with the correct type, could do it in the function below, but this way it should be +//clear that if the above function is changed, this function might need a change as well. +static void SHELL_ProgramStart_First_shell(DOS_Shell * * make) { + *make = new DOS_Shell; +} #define AUTOEXEC_SIZE 4096 static char autoexec_data[AUTOEXEC_SIZE] = { 0 }; @@ -94,14 +99,28 @@ void AutoexecObject::CreateAutoexec(void) { //Create a new autoexec.bat autoexec_data[0] = 0; size_t auto_len; - for(auto_it it= autoexec_strings.begin(); it != autoexec_strings.end(); it++) { + for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); it++) { + + std::string linecopy = (*it); + std::string::size_type offset = 0; + //Lets have \r\n as line ends in autoexec.bat. + while(offset < linecopy.length()) { + std::string::size_type n = linecopy.find("\n",offset); + if ( n == std::string::npos ) break; + std::string::size_type rn = linecopy.find("\r\n",offset); + if ( rn != std::string::npos && rn + 1 == n) {offset = n + 1; continue;} + // \n found without matching \r + linecopy.replace(n,1,"\r\n"); + offset = n + 2; + } + auto_len = strlen(autoexec_data); - if ((auto_len+(*it).length()+3)>AUTOEXEC_SIZE) { + if ((auto_len+linecopy.length() + 3) > AUTOEXEC_SIZE) { E_Exit("SYSTEM:Autoexec.bat file overflow"); } - sprintf((autoexec_data+auto_len),"%s\r\n",(*it).c_str()); + sprintf((autoexec_data + auto_len),"%s\r\n",linecopy.c_str()); } - if(first_shell) VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); + if (first_shell) VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); } AutoexecObject::~AutoexecObject(){ @@ -109,21 +128,32 @@ AutoexecObject::~AutoexecObject(){ // Remove the line from the autoexecbuffer and update environment for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); ) { - if((*it) == buf) { - it = autoexec_strings.erase(it); + if ((*it) == buf) { std::string::size_type n = buf.size(); char* buf2 = new char[n + 1]; safe_strncpy(buf2, buf.c_str(), n + 1); + bool stringset = false; // If it's a environment variable remove it from there as well - if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ + if ((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ char* after_set = buf2 + 4;//move to variable that is being set char* test = strpbrk(after_set,"="); - if(!test) continue; + if (!test) { + delete [] buf2; + continue; + } *test = 0; + stringset = true; //If the shell is running/exists update the environment - if(first_shell) first_shell->SetEnv(after_set,""); + if (first_shell) first_shell->SetEnv(after_set,""); } delete [] buf2; + if (stringset && first_shell && first_shell->bf && first_shell->bf->filename.find("AUTOEXEC.BAT") != std::string::npos) { + //Replace entry with spaces if it is a set and from autoexec.bat, as else the location counter will be off. + *it = buf.assign(buf.size(),' '); + it++; + } else { + it = autoexec_strings.erase(it); + } } else it++; } this->CreateAutoexec(); @@ -166,9 +196,9 @@ Bitu DOS_Shell::GetRedirection(char *s, char **ifn, char **ofn,bool * append) { while (*lr && *lr!=' ' && *lr!='<' && *lr!='|') lr++; //if it ends on a : => remove it. if((*ofn != lr) && (lr[-1] == ':')) lr[-1] = 0; -// if(*lr && *(lr+1)) -// *lr++=0; -// else +// if(*lr && *(lr+1)) +// *lr++=0; +// else // *lr=0; t = (char*)malloc(lr-*ofn+1); safe_strncpy(t,*ofn,lr-*ofn+1); @@ -180,9 +210,9 @@ Bitu DOS_Shell::GetRedirection(char *s, char **ifn, char **ofn,bool * append) { *ifn=lr; while (*lr && *lr!=' ' && *lr!='>' && *lr != '|') lr++; if((*ifn != lr) && (lr[-1] == ':')) lr[-1] = 0; -// if(*lr && *(lr+1)) -// *lr++=0; -// else +// if(*lr && *(lr+1)) +// *lr++=0; +// else // *lr=0; t = (char*)malloc(lr-*ifn+1); safe_strncpy(t,*ifn,lr-*ifn+1); @@ -196,7 +226,7 @@ Bitu DOS_Shell::GetRedirection(char *s, char **ifn, char **ofn,bool * append) { } *lw=0; return num; -} +} void DOS_Shell::ParseLine(char * line) { LOG(LOG_EXEC,LOG_ERROR)("Parsing command line: %s",line); @@ -205,7 +235,7 @@ void DOS_Shell::ParseLine(char * line) { line = trim(line); /* Do redirection and pipe checks */ - + char * in = 0; char * out = 0; @@ -215,23 +245,23 @@ void DOS_Shell::ParseLine(char * line) { bool append; bool normalstdin = false; /* wether stdin/out are open on start. */ bool normalstdout = false; /* Bug: Assumed is they are "con" */ - + num = GetRedirection(line,&in, &out,&append); - if (num>1) LOG_MSG("SHELL:Multiple command on 1 line not supported"); + if (num>1) LOG_MSG("SHELL: Multiple command on 1 line not supported"); if (in || out) { - normalstdin = (psp->GetFileHandle(0) != 0xff); - normalstdout = (psp->GetFileHandle(1) != 0xff); + normalstdin = (psp->GetFileHandle(0) != 0xff); + normalstdout = (psp->GetFileHandle(1) != 0xff); } if (in) { if(DOS_OpenFile(in,OPEN_READ,&dummy)) { //Test if file exists DOS_CloseFile(dummy); - LOG_MSG("SHELL:Redirect input from %s",in); + LOG_MSG("SHELL: Redirect input from %s",in); if(normalstdin) DOS_CloseFile(0); //Close stdin DOS_OpenFile(in,OPEN_READ,&dummy); //Open new stdin } } if (out){ - LOG_MSG("SHELL:Redirect output to %s",out); + LOG_MSG("SHELL: Redirect output to %s",out); if(normalstdout) DOS_CloseFile(1); if(!normalstdin && !in) DOS_OpenFile("con",OPEN_READWRITE,&dummy); bool status = true; @@ -245,7 +275,7 @@ void DOS_Shell::ParseLine(char * line) { } else { status = DOS_OpenFileExtended(out,OPEN_READWRITE,DOS_ATTR_ARCHIVE,0x12,&dummy,&dummy2); } - + if(!status && normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); //Read only file, open con again if(!normalstdin && !in) DOS_CloseFile(0); } @@ -268,21 +298,21 @@ void DOS_Shell::ParseLine(char * line) { -void DOS_Shell::RunInternal(void) -{ +void DOS_Shell::RunInternal(void) { char input_line[CMD_MAXLINE] = {0}; - while(bf && bf->ReadLine(input_line)) - { - if (echo) { + while (bf) { + if (bf->ReadLine(input_line)) { + if (echo) { if (input_line[0] != '@') { ShowPrompt(); WriteOut_NoParsing(input_line); WriteOut_NoParsing("\n"); - }; - }; - ParseLine(input_line); + } + } + ParseLine(input_line); + if (echo) WriteOut_NoParsing("\n"); + } } - return; } void DOS_Shell::Run(void) { @@ -299,18 +329,20 @@ void DOS_Shell::Run(void) { return; } /* Start a normal shell and check for a first command init */ - WriteOut(MSG_Get("SHELL_STARTUP_BEGIN"),VERSION); -#if C_DEBUG - WriteOut(MSG_Get("SHELL_STARTUP_DEBUG")); -#endif - if (machine == MCH_CGA) WriteOut(MSG_Get("SHELL_STARTUP_CGA")); - if (machine == MCH_HERC) WriteOut(MSG_Get("SHELL_STARTUP_HERC")); - WriteOut(MSG_Get("SHELL_STARTUP_END")); - if (cmd->FindString("/INIT",line,true)) { + WriteOut(MSG_Get("SHELL_STARTUP_BEGIN"),VERSION); +#if C_DEBUG + WriteOut(MSG_Get("SHELL_STARTUP_DEBUG")); +#endif + if (machine == MCH_CGA) WriteOut(MSG_Get("SHELL_STARTUP_CGA")); + if (machine == MCH_HERC) WriteOut(MSG_Get("SHELL_STARTUP_HERC")); + WriteOut(MSG_Get("SHELL_STARTUP_END")); + strcpy(input_line,line.c_str()); line.erase(); ParseLine(input_line); + } else { + WriteOut(MSG_Get("SHELL_STARTUP_SUB"),VERSION); } do { if (bf){ @@ -355,14 +387,25 @@ public: char * extra = const_cast(section->data.c_str()); if (extra && !secure && !control->cmdline->FindExist("-noautoexec",true)) { /* detect if "echo off" is the first line */ + size_t firstline_length = strcspn(extra,"\r\n"); bool echo_off = !strncasecmp(extra,"echo off",8); - if (!echo_off) echo_off = !strncasecmp(extra,"@echo off",9); + if (echo_off && firstline_length == 8) extra += 8; + else { + echo_off = !strncasecmp(extra,"@echo off",9); + if (echo_off && firstline_length == 9) extra += 9; + else echo_off = false; + } - /* if "echo off" add it to the front of autoexec.bat */ - if(echo_off) autoexec_echo.InstallBefore("@echo off"); + /* if "echo off" move it to the front of autoexec.bat */ + if (echo_off) { + autoexec_echo.InstallBefore("@echo off"); + if (*extra == '\r') extra++; //It can point to \0 + if (*extra == '\n') extra++; //same + } - /* Install the stuff from the configfile */ - autoexec[0].Install(section->data); + /* Install the stuff from the configfile if anything left after moving echo off */ + + if (*extra) autoexec[0].Install(std::string(extra)); } /* Check to see for extra command line options to be added (before the command specified on commandline) */ @@ -380,38 +423,42 @@ public: bool addexit = control->cmdline->FindExist("-exit",true); /* Check for first command being a directory or file */ - char buffer[CROSS_LEN]; - char orig[CROSS_LEN]; + char buffer[CROSS_LEN+1]; + char orig[CROSS_LEN+1]; char cross_filesplit[2] = {CROSS_FILESPLIT , 0}; - /* Combining -securemode and no parameter leaves you with a lovely Z:\. */ - if ( !control->cmdline->FindCommand(1,line) ) { - if ( secure ) autoexec[12].Install("z:\\config.com -securemode"); - } else { + + Bitu dummy = 1; + bool command_found = false; + while (control->cmdline->FindCommand(dummy++,line) && !command_found) { struct stat test; + if (line.length() > CROSS_LEN) continue; strcpy(buffer,line.c_str()); - if (stat(buffer,&test)){ - getcwd(buffer,CROSS_LEN); + if (stat(buffer,&test)) { + if (getcwd(buffer,CROSS_LEN) == NULL) continue; + if (strlen(buffer) + line.length() + 1 > CROSS_LEN) continue; strcat(buffer,cross_filesplit); strcat(buffer,line.c_str()); - if (stat(buffer,&test)) goto nomount; + if (stat(buffer,&test)) continue; } - if (test.st_mode & S_IFDIR) { + if (test.st_mode & S_IFDIR) { autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\""); autoexec[13].Install("C:"); if(secure) autoexec[14].Install("z:\\config.com -securemode"); + command_found = true; } else { char* name = strrchr(buffer,CROSS_FILESPLIT); - if (!name) { //Only a filename + if (!name) { //Only a filename line = buffer; - getcwd(buffer,CROSS_LEN); + if (getcwd(buffer,CROSS_LEN) == NULL) continue; + if (strlen(buffer) + line.length() + 1 > CROSS_LEN) continue; strcat(buffer,cross_filesplit); strcat(buffer,line.c_str()); - if(stat(buffer,&test)) goto nomount; + if(stat(buffer,&test)) continue; name = strrchr(buffer,CROSS_FILESPLIT); - if(!name) goto nomount; + if(!name) continue; } *name++ = 0; - if (access(buffer,F_OK)) goto nomount; + if (access(buffer,F_OK)) continue; autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\""); autoexec[13].Install("C:"); /* Save the non-modified filename (so boot and imgmount can use it (long filenames, case sensivitive)) */ @@ -438,9 +485,14 @@ public: autoexec[15].Install(name); if(addexit) autoexec[16].Install("exit"); } + command_found = true; } } -nomount: + + /* Combining -securemode, noautoexec and no parameters leaves you with a lovely Z:\. */ + if ( !command_found ) { + if ( secure ) autoexec[12].Install("z:\\config.com -securemode"); + } VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); } }; @@ -451,6 +503,41 @@ void AUTOEXEC_Init(Section * sec) { test = new AUTOEXEC(sec); } +static Bitu INT2E_Handler(void) { + /* Save return address and current process */ + RealPt save_ret=real_readd(SegValue(ss),reg_sp); + Bit16u save_psp=dos.psp(); + + /* Set first shell as process and copy command */ + dos.psp(DOS_FIRST_SHELL); + DOS_PSP psp(DOS_FIRST_SHELL); + psp.SetCommandTail(RealMakeSeg(ds,reg_si)); + SegSet16(ss,RealSeg(psp.GetStack())); + reg_sp=2046; + + /* Read and fix up command string */ + CommandTail tail; + MEM_BlockRead(PhysMake(dos.psp(),128),&tail,128); + if (tail.count<127) tail.buffer[tail.count]=0; + else tail.buffer[126]=0; + char* crlf=strpbrk(tail.buffer,"\r\n"); + if (crlf) *crlf=0; + + /* Execute command */ + if (strlen(tail.buffer)) { + DOS_Shell temp; + temp.ParseLine(tail.buffer); + temp.RunInternal(); + } + + /* Restore process and "return" to caller */ + dos.psp(save_psp); + SegSet16(cs,RealSeg(save_ret)); + reg_ip=RealOff(save_ret); + reg_ax=0; + return CBRET_NONE; +} + static char const * const path_string="PATH=Z:\\"; static char const * const comspec_string="COMSPEC=Z:\\COMMAND.COM"; static char const * const full_name="Z:\\COMMAND.COM"; @@ -465,7 +552,7 @@ void SHELL_Init() { MSG_Add("SHELL_ILLEGAL_SWITCH","Illegal switch: %s.\n"); MSG_Add("SHELL_MISSING_PARAMETER","Required parameter missing.\n"); MSG_Add("SHELL_CMD_CHDIR_ERROR","Unable to change to: %s.\n"); - MSG_Add("SHELL_CMD_CHDIR_HINT","To change to different drive type \033[31m%c:\033[0m\n"); + MSG_Add("SHELL_CMD_CHDIR_HINT","Hint: To change to different drive type \033[31m%c:\033[0m\n"); MSG_Add("SHELL_CMD_CHDIR_HINT_2","directoryname is longer than 8 characters and/or contains spaces.\nTry \033[31mcd %s\033[0m\n"); MSG_Add("SHELL_CMD_CHDIR_HINT_3","You are still on drive Z:, change to a mounted drive with \033[31mC:\033[0m.\n"); MSG_Add("SHELL_CMD_DATE_HELP","Displays or changes the internal date.\n"); @@ -562,7 +649,8 @@ void SHELL_Init() { "\xBA \xBA\n" ); MSG_Add("SHELL_STARTUP_CGA","\xBA DOSBox supports Composite CGA mode. \xBA\n" - "\xBA Use \033[31m(alt-)F11\033[37m to change the colours when in this mode. \xBA\n" + "\xBA Use \033[31mF12\033[37m to set composite output ON, OFF, or AUTO (default). \xBA\n" + "\xBA \033[31m(Alt-)F11\033[37m changes hue; \033[31mctrl-alt-F11\033[37m selects early/late CGA model. \xBA\n" "\xBA \xBA\n" ); MSG_Add("SHELL_STARTUP_HERC","\xBA Use \033[31mF11\033[37m to cycle through white, amber, and green monochrome color. \xBA\n" @@ -581,6 +669,7 @@ void SHELL_Init() { //"\n" //Breaks the startup message if you type a mount and a drive change. ); #endif + MSG_Add("SHELL_STARTUP_SUB","\n\n\033[32;1mDOSBox %s Command Shell\033[0m\n\n"); MSG_Add("SHELL_CMD_CHDIR_HELP","Displays/changes the current directory.\n"); MSG_Add("SHELL_CMD_CHDIR_HELP_LONG","CHDIR [drive:][path]\n" "CHDIR [..]\n" @@ -654,6 +743,12 @@ void SHELL_Init() { /* Set up int 23 to "int 20" in the psp. Fixes what.exe */ real_writed(0,0x23*4,((Bit32u)psp_seg<<16)); + /* Set up int 2e handler */ + Bitu call_int2e=CALLBACK_Allocate(); + RealPt addr_int2e=RealMake(psp_seg+16+1,8); + CALLBACK_Setup(call_int2e,&INT2E_Handler,CB_IRET_STI,Real2Phys(addr_int2e),"Shell Int 2e"); + RealSetVec(0x2e,addr_int2e); + /* Setup MCBs */ DOS_MCB pspmcb((Bit16u)(psp_seg-1)); pspmcb.SetPSPSeg(psp_seg); // MCB of the command shell psp @@ -663,7 +758,7 @@ void SHELL_Init() { envmcb.SetPSPSeg(psp_seg); // MCB of the command shell environment envmcb.SetSize(DOS_MEM_START-env_seg); envmcb.SetType(0x4d); - + /* Setup environment */ PhysPt env_write=PhysMake(env_seg,0); MEM_BlockWrite(env_write,path_string,(Bitu)(strlen(path_string)+1)); @@ -678,7 +773,7 @@ void SHELL_Init() { DOS_PSP psp(psp_seg); psp.MakeNew(0); dos.psp(psp_seg); - + /* The start of the filetable in the psp must look like this: * 01 01 01 00 02 * In order to achieve this: First open 2 files. Close the first and @@ -690,7 +785,7 @@ void SHELL_Init() { DOS_ForceDuplicateEntry(1,0); /* "new" STDIN */ DOS_ForceDuplicateEntry(1,2); /* STDERR */ DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDAUX */ - DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDPRN */ + DOS_OpenFile("PRN",OPEN_READWRITE,&dummy); /* STDPRN */ psp.SetParent(psp_seg); /* Set the environment */ @@ -698,15 +793,16 @@ void SHELL_Init() { /* Set the command line for the shell start up */ CommandTail tail; tail.count=(Bit8u)strlen(init_line); + memset(&tail.buffer,0,127); strcpy(tail.buffer,init_line); MEM_BlockWrite(PhysMake(psp_seg,128),&tail,128); - + /* Setup internal DOS Variables */ dos.dta(RealMake(psp_seg,0x80)); dos.psp(psp_seg); - - SHELL_ProgramStart(&first_shell); + + SHELL_ProgramStart_First_shell(&first_shell); first_shell->Run(); delete first_shell; first_shell = 0;//Make clear that it shouldn't be used anymore diff --git a/src/shell/shell_batch.cpp b/src/shell/shell_batch.cpp index 1c86ecc..033def6 100644 --- a/src/shell/shell_batch.cpp +++ b/src/shell/shell_batch.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -34,7 +34,7 @@ BatchFile::BatchFile(DOS_Shell * host,char const * const resolved_name,char cons filename = totalname; //Test if file is openable - if (!DOS_OpenFile(totalname,128,&file_handle)) { + if (!DOS_OpenFile(totalname,(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) { //TODO Come up with something better E_Exit("SHELL:Can't open BatchFile %s",totalname); } @@ -49,7 +49,7 @@ BatchFile::~BatchFile() { bool BatchFile::ReadLine(char * line) { //Open the batchfile and seek to stored postion - if (!DOS_OpenFile(filename.c_str(),128,&file_handle)) { + if (!DOS_OpenFile(filename.c_str(),(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) { LOG(LOG_MISC,LOG_ERROR)("ReadLine Can't open BatchFile %s",filename.c_str()); delete this; return false; @@ -68,8 +68,12 @@ emptyline: * Exclusion list: tab for batch files * escape for ansi * backspace for alien odyssey */ - if (c>31 || c==0x1b || c=='\t' || c==8) - *cmd_write++=c; + if (c>31 || c==0x1b || c=='\t' || c==8) { + //Only add it if room for it (and trailing zero) in the buffer, but do the check here instead at the end + //So we continue reading till EOL/EOF + if (((cmd_write - temp) + 1) < (CMD_MAXLINE - 1)) + *cmd_write++ = c; + } } } while (c!='\n' && n); *cmd_write=0; @@ -85,55 +89,64 @@ emptyline: /* Now parse the line read from the bat file for % stuff */ cmd_write=line; char * cmd_read=temp; - char env_name[256];char * env_write; while (*cmd_read) { - env_write=env_name; - if (*cmd_read=='%') { + if (*cmd_read == '%') { cmd_read++; if (cmd_read[0] == '%') { cmd_read++; - *cmd_write++='%'; + if (((cmd_write - line) + 1) < (CMD_MAXLINE - 1)) + *cmd_write++ = '%'; continue; } if (cmd_read[0] == '0') { /* Handle %0 */ const char *file_name = cmd->GetFileName(); cmd_read++; - strcpy(cmd_write,file_name); - cmd_write+=strlen(file_name); + size_t name_len = strlen(file_name); + if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) { + strcpy(cmd_write,file_name); + cmd_write += name_len; + } continue; } char next = cmd_read[0]; - if(next > '0' && next <= '9') { + if(next > '0' && next <= '9') { /* Handle %1 %2 .. %9 */ cmd_read++; //Progress reader next -= '0'; if (cmd->GetCount()<(unsigned int)next) continue; std::string word; if (!cmd->FindCommand(next,word)) continue; - strcpy(cmd_write,word.c_str()); - cmd_write+=strlen(word.c_str()); + size_t name_len = strlen(word.c_str()); + if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) { + strcpy(cmd_write,word.c_str()); + cmd_write += name_len; + } continue; } else { /* Not a command line number has to be an environment */ - char * first=strchr(cmd_read,'%'); - /* No env afterall.Somewhat of a hack though as %% and % aren't handled consistent in dosbox. Maybe echo needs to parse % and %% as well. */ - if (!first) {*cmd_write++ = '%';continue;} + char * first = strchr(cmd_read,'%'); + /* No env afterall. Ignore a single % */ + if (!first) {/* *cmd_write++ = '%';*/continue;} *first++ = 0; std::string env; if (shell->GetEnvStr(cmd_read,env)) { - const char * equals=strchr(env.c_str(),'='); + const char* equals = strchr(env.c_str(),'='); if (!equals) continue; equals++; - strcpy(cmd_write,equals); - cmd_write+=strlen(equals); + size_t name_len = strlen(equals); + if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) { + strcpy(cmd_write,equals); + cmd_write += name_len; + } } - cmd_read=first; + cmd_read = first; } } else { - *cmd_write++=*cmd_read++; + if (((cmd_write - line) + 1) < (CMD_MAXLINE - 1)) + *cmd_write++ = *cmd_read++; } } - *cmd_write=0; + *cmd_write = 0; //Store current location and close bat file this->location = 0; DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR); @@ -143,7 +156,7 @@ emptyline: bool BatchFile::Goto(char * where) { //Open bat file and search for the where string - if (!DOS_OpenFile(filename.c_str(),128,&file_handle)) { + if (!DOS_OpenFile(filename.c_str(),(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) { LOG(LOG_MISC,LOG_ERROR)("SHELL:Goto Can't open BatchFile %s",filename.c_str()); delete this; return false; @@ -160,8 +173,10 @@ again: n=1; DOS_ReadFile(file_handle,&c,&n); if (n>0) { - if (c>31) - *cmd_write++=c; + if (c>31) { + if (((cmd_write - cmd_buffer) + 1) < (CMD_MAXLINE - 1)) + *cmd_write++ = c; + } } } while (c!='\n' && n); *cmd_write++ = 0; diff --git a/src/shell/shell_cmds.cpp b/src/shell/shell_cmds.cpp index 1e42d10..88cb072 100644 --- a/src/shell/shell_cmds.cpp +++ b/src/shell/shell_cmds.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -21,9 +21,11 @@ #include "shell.h" #include "callback.h" #include "regs.h" +#include "bios.h" #include "../dos/drives.h" #include "support.h" #include "control.h" +#include #include #include #include @@ -82,21 +84,21 @@ static void StripSpaces(char*&args,char also) { args++; } -static char* ExpandDot(char*args, char* buffer) { +static char* ExpandDot(char*args, char* buffer , size_t bufsize) { if(*args == '.') { if(*(args+1) == 0){ - strcpy(buffer,"*.*"); + safe_strncpy(buffer, "*.*", bufsize); return buffer; } if( (*(args+1) != '.') && (*(args+1) != '\\') ) { buffer[0] = '*'; buffer[1] = 0; - strcat(buffer,args); + if (bufsize > 2) strncat(buffer,args,bufsize - 1 /*used buffer portion*/ - 1 /*trailing zero*/ ); return buffer; } else - strcpy (buffer, args); + safe_strncpy (buffer, args, bufsize); } - else strcpy(buffer,args); + else safe_strncpy(buffer,args, bufsize); return buffer; } @@ -189,7 +191,7 @@ void DOS_Shell::CMD_DELETE(char * args) { char full[DOS_PATHLENGTH]; char buffer[CROSS_LEN]; - args = ExpandDot(args,buffer); + args = ExpandDot(args,buffer, CROSS_LEN); StripSpaces(args); if (!DOS_Canonicalize(args,full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));return; } //TODO Maybe support confirmation for *.* like dos does. @@ -232,34 +234,38 @@ void DOS_Shell::CMD_HELP(char * args){ void DOS_Shell::CMD_RENAME(char * args){ HELP("RENAME"); StripSpaces(args); - if(!*args) {SyntaxError();return;} - if((strchr(args,'*')!=NULL) || (strchr(args,'?')!=NULL) ) { WriteOut(MSG_Get("SHELL_CMD_NO_WILD"));return;} + if (!*args) {SyntaxError();return;} + if ((strchr(args,'*')!=NULL) || (strchr(args,'?')!=NULL) ) { WriteOut(MSG_Get("SHELL_CMD_NO_WILD"));return;} char * arg1=StripWord(args); + StripSpaces(args); + if (!*args) {SyntaxError();return;} char* slash = strrchr(arg1,'\\'); - if(slash) { - slash++; + if (slash) { /* If directory specified (crystal caves installer) * rename from c:\X : rename c:\abc.exe abc.shr. - * File must appear in C:\ */ + * File must appear in C:\ + * Ren X:\A\B C => ren X:\A\B X:\A\C */ - char dir_source[DOS_PATHLENGTH]={0}; + char dir_source[DOS_PATHLENGTH + 4] = {0}; //not sure if drive portion is included in pathlength //Copy first and then modify, makes GCC happy - strcpy(dir_source,arg1); + safe_strncpy(dir_source,arg1,DOS_PATHLENGTH + 4); char* dummy = strrchr(dir_source,'\\'); - *dummy=0; - - if((strlen(dir_source) == 2) && (dir_source[1] == ':')) - strcat(dir_source,"\\"); //X: add slash - - char dir_current[DOS_PATHLENGTH + 1]; - dir_current[0] = '\\'; //Absolute addressing so we can return properly - DOS_GetCurrentDir(0,dir_current + 1); - if(!DOS_ChangeDir(dir_source)) { + if (!dummy) { //Possible due to length WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); return; } - DOS_Rename(slash,args); - DOS_ChangeDir(dir_current); + dummy++; + *dummy = 0; + + //Maybe check args for directory, as I think that isn't allowed + + //dir_source and target are introduced for when we support multiple files being renamed. + char target[DOS_PATHLENGTH+CROSS_LEN + 5] = {0}; + strcpy(target,dir_source); + strncat(target,args,CROSS_LEN); + + DOS_Rename(arg1,target); + } else { DOS_Rename(arg1,args); } @@ -302,13 +308,25 @@ void DOS_Shell::CMD_EXIT(char * args) { void DOS_Shell::CMD_CHDIR(char * args) { HELP("CHDIR"); StripSpaces(args); + Bit8u drive = DOS_GetDefaultDrive()+'A'; + char dir[DOS_PATHLENGTH]; if (!*args) { - Bit8u drive=DOS_GetDefaultDrive()+'A'; - char dir[DOS_PATHLENGTH]; DOS_GetCurrentDir(0,dir); WriteOut("%c:\\%s\n",drive,dir); } else if(strlen(args) == 2 && args[1]==':') { - WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(*reinterpret_cast(&args[0]))); + Bit8u targetdrive = (args[0] | 0x20)-'a' + 1; + unsigned char targetdisplay = *reinterpret_cast(&args[0]); + if(!DOS_GetCurrentDir(targetdrive,dir)) { + if(drive == 'Z') { + WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(targetdisplay)); + } else { + WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); + } + return; + } + WriteOut("%c:\\%s\n",toupper(targetdisplay),dir); + if(drive == 'Z') + WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(targetdisplay)); } else if (!DOS_ChangeDir(args)) { /* Changedir failed. Check if the filename is longer then 8 and/or contains spaces */ @@ -333,8 +351,7 @@ void DOS_Shell::CMD_CHDIR(char * args) { temps += "~1"; WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str()); } else { - Bit8u drive=DOS_GetDefaultDrive()+'A'; - if (drive=='Z') { + if (drive == 'Z') { WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_3")); } else { WriteOut(MSG_Get("SHELL_CMD_CHDIR_ERROR"),args); @@ -369,8 +386,8 @@ void DOS_Shell::CMD_RMDIR(char * args) { } } -static void FormatNumber(Bitu num,char * buf) { - Bitu numm,numk,numb,numg; +static void FormatNumber(Bit32u num,char * buf) { + Bit32u numm,numk,numb,numg; numb=num % 1000; num/=1000; numk=num % 1000; @@ -391,7 +408,31 @@ static void FormatNumber(Bitu num,char * buf) { return; }; sprintf(buf,"%d",numb); -} +} + +struct DtaResult { + char name[DOS_NAMELENGTH_ASCII]; + Bit32u size; + Bit16u date; + Bit16u time; + Bit8u attr; + + static bool compareName(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.name, rhs.name) < 0; } + static bool compareExt(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.getExtension(), rhs.getExtension()) < 0; } + static bool compareSize(const DtaResult &lhs, const DtaResult &rhs) { return lhs.size < rhs.size; } + static bool compareDate(const DtaResult &lhs, const DtaResult &rhs) { return lhs.date < rhs.date || (lhs.date == rhs.date && lhs.time < rhs.time); } + + const char * getExtension() const { + const char * ext = empty_string; + if (name[0] != '.') { + ext = strrchr(name, '.'); + if (!ext) ext = empty_string; + } + return ext; + } + +}; + void DOS_Shell::CMD_DIR(char * args) { HELP("DIR"); @@ -405,7 +446,7 @@ void DOS_Shell::CMD_DIR(char * args) { line = std::string(args) + " " + value; args=const_cast(line.c_str()); } - + bool optW=ScanCMDBool(args,"W"); ScanCMDBool(args,"S"); bool optP=ScanCMDBool(args,"P"); @@ -414,6 +455,29 @@ void DOS_Shell::CMD_DIR(char * args) { } bool optB=ScanCMDBool(args,"B"); bool optAD=ScanCMDBool(args,"AD"); + bool optAminusD=ScanCMDBool(args,"A-D"); + // Sorting flags + bool reverseSort = false; + bool optON=ScanCMDBool(args,"ON"); + if (ScanCMDBool(args,"O-N")) { + optON = true; + reverseSort = true; + } + bool optOD=ScanCMDBool(args,"OD"); + if (ScanCMDBool(args,"O-D")) { + optOD = true; + reverseSort = true; + } + bool optOE=ScanCMDBool(args,"OE"); + if (ScanCMDBool(args,"O-E")) { + optOE = true; + reverseSort = true; + } + bool optOS=ScanCMDBool(args,"OS"); + if (ScanCMDBool(args,"O-S")) { + optOS = true; + reverseSort = true; + } char * rem=ScanCMDRemain(args); if (rem) { WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); @@ -441,7 +505,7 @@ void DOS_Shell::CMD_DIR(char * args) { break; } } - args = ExpandDot(args,buffer); + args = ExpandDot(args,buffer,CROSS_LEN); if (!strrchr(args,'*') && !strrchr(args,'?')) { Bit16u attribute=0; @@ -472,13 +536,45 @@ void DOS_Shell::CMD_DIR(char * args) { return; } - do { /* File name and extension */ - char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr; - dta.GetResult(name,size,date,time,attr); + std::vector results; + + do { /* File name and extension */ + DtaResult result; + dta.GetResult(result.name,result.size,result.date,result.time,result.attr); + + /* Skip non-directories if option AD is present, or skip dirs in case of A-D */ + if(optAD && !(result.attr&DOS_ATTR_DIRECTORY) ) continue; + else if(optAminusD && (result.attr&DOS_ATTR_DIRECTORY) ) continue; + + results.push_back(result); + + } while ( (ret=DOS_FindNext()) ); + + if (optON) { + // Sort by name + std::sort(results.begin(), results.end(), DtaResult::compareName); + } else if (optOE) { + // Sort by extension + std::sort(results.begin(), results.end(), DtaResult::compareExt); + } else if (optOD) { + // Sort by date + std::sort(results.begin(), results.end(), DtaResult::compareDate); + } else if (optOS) { + // Sort by size + std::sort(results.begin(), results.end(), DtaResult::compareSize); + } + if (reverseSort) { + std::reverse(results.begin(), results.end()); + } + + for (std::vector::iterator iter = results.begin(); iter != results.end(); iter++) { + + char * name = iter->name; + Bit32u size = iter->size; + Bit16u date = iter->date; + Bit16u time = iter->time; + Bit8u attr = iter->attr; - /* Skip non-directories if option AD is present */ - if(optAD && !(attr&DOS_ATTR_DIRECTORY) ) continue; - /* output the file */ if (optB) { // this overrides pretty much everything @@ -526,7 +622,9 @@ void DOS_Shell::CMD_DIR(char * args) { if (optP && !(++p_count%(22*w_size))) { CMD_PAUSE(empty_string); } - } while ( (ret=DOS_FindNext()) ); + } + + if (optW) { if (w_count%5) WriteOut("\n"); } @@ -582,8 +680,8 @@ void DOS_Shell::CMD_COPY(char * args) { dos.dta(save_dta); return; } - // Gather all sources (extension to copy more then 1 file specified at commandline) - // Concatating files go as follows: All parts except for the last bear the concat flag. + // Gather all sources (extension to copy more then 1 file specified at command line) + // Concatenating files go as follows: All parts except for the last bear the concat flag. // This construction allows them to be counted (only the non concat set) char* source_p = NULL; char source_x[DOS_PATHLENGTH+CROSS_LEN]; @@ -669,14 +767,17 @@ void DOS_Shell::CMD_COPY(char * args) { char* temp = strstr(pathTarget,"*.*"); if(temp) *temp = 0;//strip off *.* from target - // add '\\' if target is a directoy + // add '\\' if target is a directory + bool target_is_file = true; if (pathTarget[strlen(pathTarget)-1]!='\\') { if (DOS_FindFirst(pathTarget,0xffff & ~DOS_ATTR_VOLUME)) { dta.GetResult(name,size,date,time,attr); - if (attr & DOS_ATTR_DIRECTORY) + if (attr & DOS_ATTR_DIRECTORY) { strcat(pathTarget,"\\"); + target_is_file = false; + } } - }; + } else target_is_file = false; //Find first sourcefile bool ret = DOS_FindFirst(const_cast(source.filename.c_str()),0xffff & ~DOS_ATTR_VOLUME); @@ -689,7 +790,8 @@ void DOS_Shell::CMD_COPY(char * args) { Bit16u sourceHandle,targetHandle; char nameTarget[DOS_PATHLENGTH]; char nameSource[DOS_PATHLENGTH]; - + + bool second_file_of_current_source = false; while (ret) { dta.GetResult(name,size,date,time,attr); @@ -702,7 +804,11 @@ void DOS_Shell::CMD_COPY(char * args) { strcpy(nameTarget,pathTarget); if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,name); - //Don't create a newfile when in concat mode + //Special variable to ensure that copy * a_file, where a_file is not a directory concats. + bool special = second_file_of_current_source && target_is_file; + second_file_of_current_source = true; + if (special) oldsource.concat = true; + //Don't create a new file when in concat mode if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) { Bit32u dummy=0; //In concat mode. Open the target and seek to the eof @@ -719,7 +825,7 @@ void DOS_Shell::CMD_COPY(char * args) { failed |= DOS_CloseFile(sourceHandle); failed |= DOS_CloseFile(targetHandle); WriteOut(" %s\n",name); - if(!source.concat) count++; //Only count concat files once + if(!source.concat && !special) count++; //Only count concat files once } else { DOS_CloseFile(sourceHandle); WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast(target.filename.c_str())); @@ -730,8 +836,9 @@ void DOS_Shell::CMD_COPY(char * args) { } } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast(source.filename.c_str())); }; - //On the next file - ret = DOS_FindNext(); + //On to the next file if the previous one wasn't a device + if ((attr&DOS_ATTR_DEVICE) == 0) ret = DOS_FindNext(); + else ret = false; }; } @@ -751,6 +858,11 @@ void DOS_Shell::CMD_SET(char * args) { } return; } + //There are args: + char * pcheck = args; + while ( *pcheck && (*pcheck == ' ' || *pcheck == '\t')) pcheck++; + if (*pcheck && strlen(pcheck) >3 && (strncasecmp(pcheck,"/p ",3) == 0)) E_Exit("Set /P is not supported. Use Choice!"); + char * p=strpbrk(args, "="); if (!p) { if (!GetEnvStr(args,line)) WriteOut(MSG_Get("SHELL_CMD_SET_NOT_SET"),args); @@ -766,7 +878,8 @@ void DOS_Shell::CMD_SET(char * args) { *p_parsed++ = '%'; p += 2; //%% => % } else { char * second = strchr(++p,'%'); - if(!second) continue; *second++ = 0; + if (!second) continue; + *second++ = 0; std::string temp; if (GetEnvStr(p,temp)) { std::string::size_type equals = temp.find('='); @@ -918,6 +1031,7 @@ nextfile: do { n=1; DOS_ReadFile(handle,&c,&n); + if (c==0x1a) break; // stop at EOF DOS_WriteFile(STDOUT,&c,&n); } while (n); DOS_CloseFile(handle); @@ -932,7 +1046,8 @@ void DOS_Shell::CMD_PAUSE(char * args){ HELP("PAUSE"); WriteOut(MSG_Get("SHELL_CMD_PAUSE")); Bit8u c;Bit16u n=1; - DOS_ReadFile (STDIN,&c,&n); + DOS_ReadFile(STDIN,&c,&n); + if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key } void DOS_Shell::CMD_CALL(char * args){ @@ -944,7 +1059,7 @@ void DOS_Shell::CMD_CALL(char * args){ void DOS_Shell::CMD_DATE(char * args) { HELP("DATE"); - if(ScanCMDBool(args,"h")) { + if(ScanCMDBool(args,"H")) { // synchronize date with host parameter time_t curtime; struct tm *loctime; @@ -960,11 +1075,11 @@ void DOS_Shell::CMD_DATE(char * args) { return; } // check if a date was passed in command line - Bitu newday,newmonth,newyear; + Bit32u newday,newmonth,newyear; if(sscanf(args,"%u-%u-%u",&newmonth,&newday,&newyear)==3) { - reg_cx = newyear; - reg_dh = newmonth; - reg_dl = newday; + reg_cx = static_cast(newyear); + reg_dh = static_cast(newmonth); + reg_dl = static_cast(newday); reg_ah=0x2b; // set system date CALLBACK_RunRealInt(0x21); @@ -976,13 +1091,13 @@ void DOS_Shell::CMD_DATE(char * args) { CALLBACK_RunRealInt(0x21); const char* datestring = MSG_Get("SHELL_CMD_DATE_DAYS"); - Bit8u length; + Bit32u length; char day[6] = {0}; if(sscanf(datestring,"%u",&length) && (length<5) && (strlen(datestring)==(length*7+1))) { // date string appears valid - for(int i = 0; i < length; i++) day[i] = datestring[reg_al*length+1+i]; + for(Bit32u i = 0; i < length; i++) day[i] = datestring[reg_al*length+1+i]; } - bool dateonly = ScanCMDBool(args,"t"); + bool dateonly = ScanCMDBool(args,"T"); if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_NOW")); const char* formatstring = MSG_Get("SHELL_CMD_DATE_FORMAT"); @@ -994,9 +1109,9 @@ void DOS_Shell::CMD_DATE(char * args) { buffer[bufferptr] = formatstring[i]; bufferptr++; } else { - if(formatstring[i]=='M') bufferptr += sprintf(buffer+bufferptr,"%02u",(Bitu)reg_dh); - if(formatstring[i]=='D') bufferptr += sprintf(buffer+bufferptr,"%02u",(Bitu)reg_dl); - if(formatstring[i]=='Y') bufferptr += sprintf(buffer+bufferptr,"%04u",(Bitu)reg_cx); + if(formatstring[i]=='M') bufferptr += sprintf(buffer+bufferptr,"%02u",(Bit8u) reg_dh); + if(formatstring[i]=='D') bufferptr += sprintf(buffer+bufferptr,"%02u",(Bit8u) reg_dl); + if(formatstring[i]=='Y') bufferptr += sprintf(buffer+bufferptr,"%04u",(Bit16u) reg_cx); } } WriteOut("%s %s\n",day, buffer); @@ -1005,7 +1120,7 @@ void DOS_Shell::CMD_DATE(char * args) { void DOS_Shell::CMD_TIME(char * args) { HELP("TIME"); - if(ScanCMDBool(args,"h")) { + if(ScanCMDBool(args,"H")) { // synchronize time with host parameter time_t curtime; struct tm *loctime; @@ -1025,7 +1140,7 @@ void DOS_Shell::CMD_TIME(char * args) { mem_writed(BIOS_TIMER,ticks); return; } - bool timeonly = ScanCMDBool(args,"t"); + bool timeonly = ScanCMDBool(args,"T"); reg_ah=0x2c; // get system time CALLBACK_RunRealInt(0x21); @@ -1203,7 +1318,16 @@ void DOS_Shell::CMD_VER(char *args) { char* word = StripWord(args); if(strcasecmp(word,"set")) return; word = StripWord(args); - dos.version.major = (Bit8u)(atoi(word)); - dos.version.minor = (Bit8u)(atoi(args)); + if (!*args && !*word) { //Reset + dos.version.major = 5; + dos.version.minor = 0; + } else if (*args == 0 && *word && (strchr(word,'.') != 0)) { //Allow: ver set 5.1 + const char * p = strchr(word,'.'); + dos.version.major = (Bit8u)(atoi(word)); + dos.version.minor = (Bit8u)(atoi(p+1)); + } else { //Official syntax: ver set 5 2 + dos.version.major = (Bit8u)(atoi(word)); + dos.version.minor = (Bit8u)(atoi(args)); + } } else WriteOut(MSG_Get("SHELL_CMD_VER_VER"),VERSION,dos.version.major,dos.version.minor); } diff --git a/src/shell/shell_misc.cpp b/src/shell/shell_misc.cpp index a4e528d..e028289 100644 --- a/src/shell/shell_misc.cpp +++ b/src/shell/shell_misc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,9 +11,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -223,6 +223,7 @@ void DOS_Shell::InputCommand(char * line) { /* Don't care */ break; case 0x0d: /* Return */ + outc('\r'); outc('\n'); size=0; //Kill the while loop break; @@ -252,17 +253,21 @@ void DOS_Shell::InputCommand(char * line) { if ((path = strrchr(line+completion_index,'/'))) completion_index = (Bit16u)(path-line+1); // build the completion list - char mask[DOS_PATHLENGTH]; + char mask[DOS_PATHLENGTH] = {0}; + if (strlen(p_completion_start) + 3 >= DOS_PATHLENGTH) { + //Beep; + break; + } if (p_completion_start) { - strcpy(mask, p_completion_start); + safe_strncpy(mask, p_completion_start,DOS_PATHLENGTH); char* dot_pos=strrchr(mask,'.'); char* bs_pos=strrchr(mask,'\\'); char* fs_pos=strrchr(mask,'/'); char* cl_pos=strrchr(mask,':'); // not perfect when line already contains wildcards, but works if ((dot_pos-bs_pos>0) && (dot_pos-fs_pos>0) && (dot_pos-cl_pos>0)) - strcat(mask, "*"); - else strcat(mask, "*.*"); + strncat(mask, "*",DOS_PATHLENGTH - 1); + else strncat(mask, "*.*",DOS_PATHLENGTH - 1); } else { strcpy(mask, "*.*"); } @@ -322,6 +327,7 @@ void DOS_Shell::InputCommand(char * line) { case 0x1b: /* ESC */ //write a backslash and return to the next line outc('\\'); + outc('\r'); outc('\n'); *line = 0; // reset the line. if (l_completion.size()) l_completion.clear(); //reset the completion list. @@ -362,7 +368,7 @@ void DOS_Shell::InputCommand(char * line) { // remove current command from history if it's there if (current_hist) { - current_hist=false; + // current_hist=false; l_history.pop_front(); } @@ -473,17 +479,51 @@ bool DOS_Shell::Execute(char * name,char * args) { /* Fill the command line */ CommandTail cmdtail; cmdtail.count = 0; - memset(&cmdtail.buffer,0,126); //Else some part of the string is unitialized (valgrind) + memset(&cmdtail.buffer,0,127); //Else some part of the string is unitialized (valgrind) if (strlen(line)>126) line[126]=0; cmdtail.count=(Bit8u)strlen(line); memcpy(cmdtail.buffer,line,strlen(line)); cmdtail.buffer[strlen(line)]=0xd; /* Copy command line in stack block too */ MEM_BlockWrite(SegPhys(ss)+reg_sp+0x100,&cmdtail,128); + + + /* Split input line up into parameters, using a few special rules, most notable the one for /AAA => A\0AA + * Qbix: It is extremly messy, but this was the only way I could get things like /:aa and :/aa to work correctly */ + + //Prepare string first + char parseline[258] = { 0 }; + for(char *pl = line,*q = parseline; *pl ;pl++,q++) { + if (*pl == '=' || *pl == ';' || *pl ==',' || *pl == '\t' || *pl == ' ') *q = 0; else *q = *pl; //Replace command seperators with 0. + } //No end of string \0 needed as parseline is larger than line + + for(char* p = parseline; (p-parseline) < 250 ;p++) { //Stay relaxed within boundaries as we have plenty of room + if (*p == '/') { //Transform /Hello into H\0ello + *p = 0; + p++; + while ( *p == 0 && (p-parseline) < 250) p++; //Skip empty fields + if ((p-parseline) < 250) { //Found something. Lets get the first letter and break it up + p++; + memmove(static_cast(p + 1),static_cast(p),(250-(p-parseline))); + if ((p-parseline) < 250) *p = 0; + } + } + } + parseline[255] = parseline[256] = parseline[257] = 0; //Just to be safe. + /* Parse FCB (first two parameters) and put them into the current DOS_PSP */ Bit8u add; - FCB_Parsename(dos.psp(),0x5C,0x00,cmdtail.buffer,&add); - FCB_Parsename(dos.psp(),0x6C,0x00,&cmdtail.buffer[add],&add); + Bit16u skip = 0; + //find first argument, we end up at parseline[256] if there is only one argument (similar for the second), which exists and is 0. + while(skip < 256 && parseline[skip] == 0) skip++; + FCB_Parsename(dos.psp(),0x5C,0x01,parseline + skip,&add); + skip += add; + + //Move to next argument if it exists + while(parseline[skip] != 0) skip++; //This is safe as there is always a 0 in parseline at the end. + while(skip < 256 && parseline[skip] == 0) skip++; //Which is higher than 256 + FCB_Parsename(dos.psp(),0x6C,0x01,parseline + skip,&add); + block.exec.fcb1=RealMake(dos.psp(),0x5C); block.exec.fcb2=RealMake(dos.psp(),0x6C); /* Set the command line in the block and save it */ @@ -550,13 +590,13 @@ char * DOS_Shell::Which(char * name) { if (!GetEnvStr("PATH",temp)) return 0; const char * pathenv=temp.c_str(); if (!pathenv) return 0; - pathenv=strchr(pathenv,'='); + pathenv = strchr(pathenv,'='); if (!pathenv) return 0; pathenv++; Bitu i_path = 0; while (*pathenv) { /* remove ; and ;; at the beginning. (and from the second entry etc) */ - while(*pathenv && (*pathenv ==';')) + while(*pathenv == ';') pathenv++; /* get next entry */ @@ -566,7 +606,7 @@ char * DOS_Shell::Which(char * name) { if(i_path == DOS_PATHLENGTH) { /* If max size. move till next ; and terminate path */ - while(*pathenv != ';') + while(*pathenv && (*pathenv != ';')) pathenv++; path[DOS_PATHLENGTH - 1] = 0; } else path[i_path] = 0; diff --git a/src/winres.rc b/src/winres.rc index cfcc701..176cf8b 100644 --- a/src/winres.rc +++ b/src/winres.rc @@ -1,5 +1,5 @@ -#include "afxres.h" +#include "winresrc.h" // icon resource dosbox_ico ICON "dosbox.ico" @@ -19,12 +19,12 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "Comments", "© 2002-2011 DOSBox Team, published under GNU GPL" + VALUE "Comments", "© 2002-2019 DOSBox Team, published under GNU GPL" VALUE "CompanyName", "DOSBox Team" VALUE "FileDescription", "DOSBox DOS Emulator" VALUE "FileVersion", "0, 74, 0, 0" VALUE "InternalName", "DOSBox" - VALUE "LegalCopyright", "Copyright © 2002-2011 DOSBox Team" + VALUE "LegalCopyright", "Copyright © 2002-2019 DOSBox Team" VALUE "OriginalFilename", "dosbox.exe" VALUE "ProductName", "DOSBox DOS Emulator" VALUE "ProductVersion", "0, 74, 0, 0" diff --git a/visualc_net/dosbox.vcproj b/visualc_net/dosbox.vcproj index 876bd30..5a1bac1 100644 --- a/visualc_net/dosbox.vcproj +++ b/visualc_net/dosbox.vcproj @@ -436,6 +436,9 @@ + + @@ -565,6 +568,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + RelativePath="..\include\midi.h"> + RelativePath="..\include\mixer.h">