diff --git a/.ci/AppImageBuilder.yml b/.ci/AppImageBuilder.yml index c003a693c..be2a7f1c1 100644 --- a/.ci/AppImageBuilder.yml +++ b/.ci/AppImageBuilder.yml @@ -8,6 +8,15 @@ # # Recipe file for appimage-builder. # +# build.sh processes conditional comments based on CMakeCache +# options at the end of each line. For example, a line ending in: +# +# # if QT:BOOL=ON +# +# will be removed from the dynamically-generated copy of this +# file if "QT" is not a boolean option set to ON, either through +# a -D definition or the option's default value in CMakeLists. +# # # Authors: RichardG, # @@ -18,7 +27,7 @@ version: 1 AppDir: path: ./archive_tmp app_info: - id: !ENV 'net.${project_lower}.${project}' + id: !ENV '${project_id}' name: !ENV '${project}' icon: !ENV '${project_icon}' version: !ENV '${project_version}' @@ -34,29 +43,31 @@ AppDir: - sourceline: 'deb http://deb.debian.org/debian bullseye-updates main' key_url: 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xac530d520f2f3269f5e98313a48449044aad5c5d' include: - - libevdev2 + - libedit2 # if (CLI:BOOL=ON|QT:BOOL=OFF) + - libevdev2 # if QT:BOOL=ON - libfluidsynth2 - libfreetype6 - - libgbm1 - - libgl1 - - libgles2 - - libglvnd0 - - libglx0 + - libgbm1 # if QT:BOOL=ON + - libgl1 # if QT:BOOL=ON + - libgles2 # if QT:BOOL=ON + - libglvnd0 # if QT:BOOL=ON + - libglx0 # if QT:BOOL=ON - libgs9 - libpng16-16 - - libqt5core5a - - libqt5gui5 - - libqt5widgets5 - - libslirp0 - - libsndio7.0 - - libwayland-client0 - - libx11-6 - - libx11-xcb1 - - libxcb1 - - libxcb-render0 - - libxcb-shape0 - - libxcb-shm0 - - libxcb-xfixes0 + - libqt5core5a # if QT:BOOL=ON + - libqt5gui5 # if QT:BOOL=ON + - libqt5widgets5 # if QT:BOOL=ON + - libsixel1 # if CLI:BOOL=ON + - libslirp0 # if SLIRP_EXTERNAL:BOOL=ON + - libsndio7.0 # if OPENAL:BOOL=ON + - libwayland-client0 # if QT:BOOL=ON + - libx11-6 # if QT:BOOL=ON + - libx11-xcb1 # if QT:BOOL=ON + - libxcb1 # if QT:BOOL=ON + - libxcb-render0 # if QT:BOOL=ON + - libxcb-shape0 # if QT:BOOL=ON + - libxcb-shm0 # if QT:BOOL=ON + - libxcb-xfixes0 # if QT:BOOL=ON - zlib1g files: exclude: @@ -69,8 +80,11 @@ AppDir: - usr/lib/cmake - usr/lib/pkgconfig - usr/s[a-gi-zA-Z]* - - usr/share/[a-hj-zA-Z]* + - usr/share/[a-hj-ln-zA-Z]* - usr/share/i[a-bd-zA-Z]* + - usr/share/m[a-df-zA-Z]* + - usr/share/metainfo/*.metainfo.xml - var AppImage: arch: !ENV '${arch_appimage}' + file_name: '-n' # nasty hack to disable metainfo validation diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index e9fd8b986..e8a3612a8 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -25,7 +25,7 @@ def buildBranch = env.JOB_BASE_NAME.contains('-') ? 1 : 0 def osArchs = [ 'Windows': ['32', '64'], 'Linux': ['x86', 'x86_64', 'arm32', 'arm64'], - 'macOS': ['x86_64'] + 'macOS': ['x86_64+arm64'] ] def osFlags = [ @@ -45,7 +45,8 @@ def archNames = [ def archNamesMac = [ 'x86_64': 'Intel', - 'arm64': 'Apple Silicon' + 'arm64': 'Apple Silicon', + 'x86_64+arm64': 'Universal (Intel and Apple Silicon)' ] def dynarecNames = [ @@ -60,7 +61,8 @@ def dynarecArchs = [ '64': ['ODR', 'NDR'], 'x86_64': ['ODR', 'NDR'], 'arm32': ['NDR'], - 'arm64': ['NDR'] + 'arm64': ['NDR'], + 'x86_64+arm64': ['ODR', 'NDR'] ] def dynarecFlags = [ diff --git a/.ci/build.sh b/.ci/build.sh index 5442a512f..73eb48fcd 100644 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -20,6 +20,7 @@ # to produce Jenkins-like builds on your local machine by following these notes: # # - Run build.sh without parameters to see its usage +# - Any boolean CMake definitions (-D ...=ON/OFF) must be ON or OFF to ensure correct behavior # - For Windows (MSYS MinGW) builds: # - Packaging requires 7-Zip on Program Files # - Packaging the Ghostscript DLL requires 32-bit and/or 64-bit Ghostscript on Program Files @@ -36,7 +37,9 @@ # build_arch x86_64 (or arm64) # universal_archs (blank) # ui_interactive no -# macosx_deployment_target 10.13 +# macosx_deployment_target 10.13 (for x86_64, or 11.0 for arm64) +# - For universal building on Apple Silicon hardware, install native MacPorts on the default +# /opt/local and Intel MacPorts on /opt/intel, then tell build.sh to build for "x86_64+arm64" # - port is called through sudo to manage dependencies; make sure it is configured # as NOPASSWD in /etc/sudoers if you're doing unattended builds # @@ -93,13 +96,13 @@ make_tar() { # Set common variables. project=86Box -project_lower=86box cwd=$(pwd) # Parse arguments. package_name= arch= tarball_name= +skip_archive=0 strip=0 cmake_flags= while [ $# -gt 0 ] @@ -113,6 +116,11 @@ do shift ;; + -n) + shift + skip_archive=1 + ;; + -s) shift tarball_name="$1" @@ -211,9 +219,10 @@ then # Call build with the correct MSYSTEM. echo [-] Switching to MSYSTEM [$msys] cd "$cwd" - strip_arg= - [ $strip -ne 0 ] && strip_arg="-t " - CHERE_INVOKING=yes MSYSTEM="$msys" bash -lc 'exec "'"$0"'" -b "'"$package_name"'" "'"$arch"'" '"$strip_arg""$cmake_flags" + args= + [ $strip -ne 0 ] && args="-t $args" + [ $skip_archive -ne 0 ] && args="-n $args" + CHERE_INVOKING=yes MSYSTEM="$msys" bash -lc 'exec "'"$0"'" -b "'"$package_name"'" "'"$arch"'" '"$args""$cmake_flags" exit $? fi else @@ -330,21 +339,187 @@ then # macOS lacks nproc, but sysctl can do the same job. alias nproc='sysctl -n hw.logicalcpu' + # Handle universal build. + if echo "$arch" | grep -q '+' + then + # Create temporary directory for merging app bundles. + rm -rf archive_tmp_universal + mkdir archive_tmp_universal + + # Build for each architecture. + merge_src= + for arch_universal in $(echo "$arch" | tr '+' ' ') + do + # Run build for the architecture. + args= + [ $strip -ne 0 ] && args="-t $args" + case $arch_universal in # workaround: force new dynarec on for ARM + arm32 | arm64) cmake_flags_extra="-D NEW_DYNAREC=ON";; + *) cmake_flags_extra=;; + esac + zsh -lc 'exec "'"$0"'" -n -b "universal part" "'"$arch_universal"'" '"$args""$cmake_flags"' '"$cmake_flags_extra" + status=$? + + if [ $status -eq 0 ] + then + # Move app bundle to the temporary directory. + app_bundle_name="archive_tmp/$(ls archive_tmp | grep '.app$')" + mv "$app_bundle_name" "archive_tmp_universal/$arch_universal.app" + status=$? + + # Merge app bundles. + if [ -z "$merge_src" ] + then + # This is the first bundle, nothing to merge with. + merge_src="$arch_universal" + else + # Merge previous bundle with this one. + merge_dest="$merge_src+$arch_universal" + echo [-] Merging app bundles [$merge_src] and [$arch_universal] into [$merge_dest] + + # Merge directory structures. + (cd "archive_tmp_universal/$merge_src.app" && find . -type d && cd "../../archive_tmp_universal/$arch_universal.app" && find . -type d && cd ../..) | sort > universal_listing.txt + cat universal_listing.txt | uniq | while IFS= read line + do + echo "> Directory: $line" + mkdir -p "archive_tmp_universal/$merge_dest.app/$line" + done + + # Create merged file listing. + (cd "archive_tmp_universal/$merge_src.app" && find . -type f && cd "../../archive_tmp_universal/$arch_universal.app" && find . -type f && cd ../..) | sort > universal_listing.txt + + # Copy files that only exist on one bundle. + cat universal_listing.txt | uniq -u | while IFS= read line + do + if [ -e "archive_tmp_universal/$merge_src.app/$line" ] + then + file_src="$merge_src" + else + file_src="$arch_universal" + fi + echo "> Only on [$file_src]: $line" + cp -p "archive_tmp_universal/$file_src.app/$line" "archive_tmp_universal/$merge_dest.app/$line" + done + + # Copy or lipo files that exist on both bundles. + cat universal_listing.txt | uniq -d | while IFS= read line + do + if cmp -s "archive_tmp_universal/$merge_src.app/$line" "archive_tmp_universal/$arch_universal.app/$line" + then + echo "> Identical: $line" + cp -p "archive_tmp_universal/$merge_src.app/$line" "archive_tmp_universal/$merge_dest.app/$line" + elif lipo -create -output "archive_tmp_universal/$merge_dest.app/$line" "archive_tmp_universal/$merge_src.app/$line" "archive_tmp_universal/$arch_universal.app/$line" 2> /dev/null + then + echo "> Merged: $line" + else + echo "> Copied from [$merge_src]: $line" + cp -p "archive_tmp_universal/$merge_src.app/$line" "archive_tmp_universal/$merge_dest.app/$line" + fi + done + + # Merge symlinks. + (cd "archive_tmp_universal/$merge_src.app" && find . -type l && cd "../../archive_tmp_universal/$arch_universal.app" && find . -type l && cd ../..) | sort > universal_listing.txt + cat universal_listing.txt | uniq | while IFS= read line + do + # Get symlink destinations. + other_link_dest= + if [ -e "archive_tmp_universal/$merge_src.app/$line" ] + then + file_src="$merge_src" + other_link_path="archive_tmp_universal/$arch_universal.app/$line" + if [ -L "$other_link_path" ] + then + other_link_dest="$(readlink "$other_link_path")" + elif [ -e "$other_link_path" ] + then + other_link_dest='[not a symlink]' + fi + else + file_src="$arch_universal" + fi + link_dest="$(readlink "archive_tmp_universal/$file_src.app/$line")" + + # Warn if destinations differ. + if [ -n "$other_link_dest" -a "$link_dest" != "$other_link_dest" ] + then + echo "> Symlink: $line => WARNING: different targets" + echo ">> Using: [$merge_src] $link_dest" + echo ">> Other: [$arch_universal] $other_link_dest" + else + echo "> Symlink: $line => $link_dest" + fi + ln -s "$link_dest" "archive_tmp_universal/$merge_dest.app/$line" + done + + # Merge a subsequent bundle with this one. + merge_src="$merge_dest" + fi + fi + + if [ $status -ne 0 ] + then + echo [!] Aborting universal build: [$arch_universal] failed with status [$status] + exit $status + fi + done + + # Rename final app bundle. + rm -rf archive_tmp + mkdir archive_tmp + mv "archive_tmp_universal/$merge_src.app" "$app_bundle_name" + + # Sign final app bundle. + arch -"$(uname -m)" codesign --force --deep -s - "$app_bundle_name" + + # Create zip. + echo [-] Creating artifact archive + cd archive_tmp + zip --symlinks -r "$cwd/$package_name.zip" . + status=$? + + # Check if the archival succeeded. + if [ $status -ne 0 ] + then + echo [!] Artifact archive creation failed with status [$status] + exit 7 + fi + + # All good. + echo [-] Universal build of [$package_name] for [$arch] with flags [$cmake_flags] successful + exit 0 + fi + + # Switch into the correct architecture if required. + case $arch in + x86_64) arch_mac="i386";; + *) arch_mac="$arch";; + esac + if [ "$(arch)" != "$arch" -a "$(arch)" != "$arch_mac" ] + then + # Call build with the correct architecture. + echo [-] Switching to architecture [$arch] + cd "$cwd" + args= + [ $strip -ne 0 ] && args="-t $args" + [ $skip_archive -ne 0 ] && args="-n $args" + arch -"$arch" zsh -lc 'exec "'"$0"'" -b "'"$package_name"'" "'"$arch"'" '"$args""$cmake_flags" + exit $? + fi + echo [-] Using architecture [$(arch)] + # Locate the MacPorts prefix. macports="/opt/local" [ -e "/opt/$arch/bin/port" ] && macports="/opt/$arch" [ "$arch" = "x86_64" -a -e "/opt/intel/bin/port" ] && macports="/opt/intel" + export PATH="$macports/bin:$macports/sbin:$macports/libexec/qt5/bin:$PATH" # Install dependencies. echo [-] Installing dependencies through MacPorts - sudo $macports/bin/port selfupdate - sudo $macports/bin/port install $(cat .ci/dependencies_macports.txt) + sudo "$macports/bin/port" selfupdate + sudo "$macports/bin/port" install $(cat .ci/dependencies_macports.txt) # Point CMake to the toolchain file. [ -e "cmake/$toolchain.cmake" ] && cmake_flags_extra="$cmake_flags_extra -D \"CMAKE_TOOLCHAIN_FILE=cmake/$toolchain.cmake\"" - - # Use OpenAL as MacPorts doesn't package FAudio. - cmake_flags_extra="$cmake_flags_extra -D OPENAL=ON" else # Determine Debian architecture. case $arch in @@ -355,7 +530,7 @@ else esac # Establish general dependencies. - pkgs="cmake ninja-build pkg-config git wget p7zip-full wayland-protocols tar gzip file" + pkgs="cmake ninja-build pkg-config git wget p7zip-full wayland-protocols tar gzip file appstream" if [ "$(dpkg --print-architecture)" = "$arch_deb" ] then pkgs="$pkgs build-essential" @@ -563,8 +738,8 @@ then unzip -j discord_game_sdk.zip "lib/$arch_discord/discord_game_sdk.dylib" -d "archive_tmp/"*".app/Contents/Frameworks" [ ! -e "archive_tmp/"*".app/Contents/Frameworks/discord_game_sdk.dylib" ] && echo [!] No Discord Game SDK for architecture [$arch_discord] - # Sign app bundle. - codesign --force --deep -s - "archive_tmp/"*".app" + # Sign app bundle, unless we're in an universal build. + [ $skip_archive -eq 0 ] && codesign --force --deep -s - "archive_tmp/"*".app" fi else cwd_root=$(pwd) @@ -606,6 +781,10 @@ else sdl_ss=ON fi + # Build SDL2 with video systems (and some dependencies) only if the SDL interface is used. + sdl_ui=OFF + grep -qiE "^QT:BOOL=ON" build/CMakeCache.txt || sdl_ui=ON + # Build rtmidi without JACK support to remove the dependency on libjack. prefix="$cache_dir/rtmidi-4.0.0" if [ -d "$prefix" ] @@ -626,15 +805,25 @@ else wget -qO - https://www.libsdl.org/release/SDL2-2.0.20.tar.gz | tar zxf - -C "$cache_dir" || rm -rf "$prefix" fi rm -rf "$cache_dir/sdlbuild" - cmake -G Ninja -D SDL_DISKAUDIO=OFF -D SDL_DIRECTFB_SHARED=OFF -D SDL_OPENGL=OFF -D SDL_OPENGLES=OFF -D SDL_OSS=OFF -D SDL_ALSA=$sdl_ss \ - -D SDL_ALSA_SHARED=$sdl_ss -D SDL_JACK=$sdl_ss -D SDL_JACK_SHARED=$sdl_ss -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=$sdl_ss \ + cmake -G Ninja -D SDL_SHARED=ON -D SDL_STATIC=OFF \ + \ + -D SDL_AUDIO=$sdl_ss -D SDL_DUMMYAUDIO=$sdl_ss -D SDL_DISKAUDIO=OFF -D SDL_OSS=OFF -D SDL_ALSA=$sdl_ss -D SDL_ALSA_SHARED=$sdl_ss \ + -D SDL_JACK=$sdl_ss -D SDL_JACK_SHARED=$sdl_ss -D SDL_ESD=OFF -D SDL_ESD_SHARED=OFF -D SDL_PIPEWIRE=$sdl_ss \ -D SDL_PIPEWIRE_SHARED=$sdl_ss -D SDL_PULSEAUDIO=$sdl_ss -D SDL_PULSEAUDIO_SHARED=$sdl_ss -D SDL_ARTS=OFF -D SDL_ARTS_SHARED=OFF \ -D SDL_NAS=$sdl_ss -D SDL_NAS_SHARED=$sdl_ss -D SDL_SNDIO=$sdl_ss -D SDL_SNDIO_SHARED=$sdl_ss -D SDL_FUSIONSOUND=OFF \ - -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=$sdl_ss -D SDL_LIBSAMPLERATE_SHARED=$sdl_ss -D SDL_X11=OFF -D SDL_X11_SHARED=OFF \ - -D SDL_WAYLAND=OFF -D SDL_WAYLAND_SHARED=OFF -D SDL_WAYLAND_LIBDECOR=OFF -D SDL_WAYLAND_LIBDECOR_SHARED=OFF -D SDL_WAYLAND_QT_TOUCH=OFF \ - -D SDL_RPI=OFF -D SDL_VIVANTE=OFF -D SDL_VULKAN=OFF -D SDL_KMSDRM=OFF -D SDL_KMSDRM_SHARED=OFF -D SDL_OFFSCREEN=OFF \ - -D SDL_HIDAPI_JOYSTICK=ON -D SDL_VIRTUAL_JOYSTICK=ON -D SDL_SHARED=ON -D SDL_STATIC=OFF -S "$prefix" -B "$cache_dir/sdlbuild" \ - -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" || exit 99 + -D SDL_FUSIONSOUND_SHARED=OFF -D SDL_LIBSAMPLERATE=$sdl_ss -D SDL_LIBSAMPLERATE_SHARED=$sdl_ss \ + \ + -D SDL_VIDEO=$sdl_ui -D SDL_X11=$sdl_ui -D SDL_X11_SHARED=$sdl_ui -D SDL_WAYLAND=$sdl_ui -D SDL_WAYLAND_SHARED=$sdl_ui \ + -D SDL_WAYLAND_LIBDECOR=$sdl_ui -D SDL_WAYLAND_LIBDECOR_SHARED=$sdl_ui -D SDL_WAYLAND_QT_TOUCH=OFF -D SDL_RPI=OFF -D SDL_VIVANTE=OFF \ + -D SDL_VULKAN=OFF -D SDL_KMSDRM=$sdl_ui -D SDL_KMSDRM_SHARED=$sdl_ui -D SDL_OFFSCREEN=$sdl_ui -D SDL_RENDER=$sdl_ui \ + \ + -D SDL_JOYSTICK=ON -D SDL_HIDAPI_JOYSTICK=ON -D SDL_VIRTUAL_JOYSTICK=ON \ + \ + -D SDL_ATOMIC=OFF -D SDL_EVENTS=ON -D SDL_HAPTIC=OFF -D SDL_POWER=OFF -D SDL_THREADS=ON -D SDL_TIMERS=ON -D SDL_FILE=OFF \ + -D SDL_LOADSO=ON -D SDL_CPUINFO=ON -D SDL_FILESYSTEM=$sdl_ui -D SDL_DLOPEN=OFF -D SDL_SENSOR=OFF -D SDL_LOCALE=OFF \ + \ + -D "CMAKE_TOOLCHAIN_FILE=$cwd_root/toolchain.cmake" -D "CMAKE_INSTALL_PREFIX=$cwd_root/archive_tmp/usr" \ + -S "$prefix" -B "$cache_dir/sdlbuild" || exit 99 cmake --build "$cache_dir/sdlbuild" -j$(nproc) || exit 99 cmake --install "$cache_dir/sdlbuild" || exit 99 @@ -653,11 +842,17 @@ else echo $pkg $version >> archive_tmp/README done + # Archive metadata. + project_id=$(ls src/unix/assets/*.*.xml | head -1 | grep -oP '/\K([^/]+)(?=\.[^\.]+\.[^\.]+$)') + metainfo_base=archive_tmp/usr/share/metainfo + mkdir -p "$metainfo_base" + cp -p "src/unix/assets/$project_id."*".xml" "$metainfo_base/$project_id.appdata.xml" + # Archive icons. icon_base=archive_tmp/usr/share/icons mkdir -p "$icon_base" cp -rp src/unix/assets/[0-9]*x[0-9]* "$icon_base/" - icon_name=$(ls "$icon_base/"[0-9]*x[0-9]*/* | head -1 | grep -oP '/\K([^/]+)(?=\.[^\.]+$)') + project_icon=$(ls "$icon_base/"[0-9]*x[0-9]*/* | head -1 | grep -oP '/\K([^/]+)(?=\.[^\.]+$)') # Archive executable, while also stripping it if requested. mkdir -p archive_tmp/usr/local/bin @@ -678,6 +873,13 @@ then exit 6 fi +# Stop if artifact archive creation was disabled. +if [ $skip_archive -ne 0 ] +then + echo [-] Skipping artifact archive creation + exit 0 +fi + # Produce artifact archive. echo [-] Creating artifact archive if is_windows @@ -688,9 +890,9 @@ then status=$? elif is_mac then - # Create zip. (TODO: dmg) + # Create zip. cd archive_tmp - zip -r "$cwd/$package_name.zip" . + zip --symlinks -r "$cwd/$package_name.zip" . status=$? else # Determine AppImage runtime architecture. @@ -702,22 +904,46 @@ else esac # Get version for AppImage metadata. - project_version=$(grep -oP '#define\s+EMU_VERSION\s+"\K([^"]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) + project_version=$(grep -oP '#define\s+EMU_VERSION\s+"\K([^"]+)' "build/src/include/"*"/version.h" 2> /dev/null) [ -z "$project_version" ] && project_version=unknown - build_num=$(grep -oP '#define\s+EMU_BUILD_NUM\s+\K([0-9]+)' "build/src/include/$project_lower/version.h" 2> /dev/null) + build_num=$(grep -oP '#define\s+EMU_BUILD_NUM\s+\K([0-9]+)' "build/src/include/"*"/version.h" 2> /dev/null) [ -n "$build_num" -a "$build_num" != "0" ] && project_version="$project_version-b$build_num" + # Generate modified AppImage metadata to suit build requirements. + cat << EOF > AppImageBuilder-generated.yml +# This file is generated automatically by .ci/build.sh and will be +# overwritten if edited. Please edit .ci/AppImageBuilder.yml instead. +EOF + while IFS= read line + do + # Skip blank or comment lines. + echo "$line" | grep -qE '^(#|$)' && continue + + # Parse "# if OPTION VALUE" condition lines. + condition=$(echo "$line" | grep -oP '# if \K(.+)') + if [ -n "$condition" ] + then + # Skip line if the condition is not matched. + grep -qiE "^$condition" build/CMakeCache.txt || continue + fi + + # Copy line. + echo "$line" >> AppImageBuilder-generated.yml + done < .ci/AppImageBuilder.yml + # Download appimage-builder if necessary. [ ! -e "appimage-builder.AppImage" ] && wget -qO appimage-builder.AppImage \ https://github.com/AppImageCrafters/appimage-builder/releases/download/v0.9.2/appimage-builder-0.9.2-35e3eab-x86_64.AppImage chmod u+x appimage-builder.AppImage - # Remove any dangling AppImages which may interfere with the renaming process. - rm -rf "$project-"*".AppImage" + # Symlink global cache directory. + rm -rf appimage-builder-cache "$project-"*".AppImage" # also remove any dangling AppImages which may interfere with the renaming process + mkdir -p "$cache_dir/appimage-builder-cache" + ln -s "$cache_dir/appimage-builder-cache" appimage-builder-cache # Run appimage-builder in extract-and-run mode for Docker compatibility. - project="$project" project_lower="$project_lower" project_version="$project_version" project_icon="$icon_name" arch_deb="$arch_deb" \ - arch_appimage="$arch_appimage" APPIMAGE_EXTRACT_AND_RUN=1 ./appimage-builder.AppImage --recipe .ci/AppImageBuilder.yml + project="$project" project_id="$project_id" project_version="$project_version" project_icon="$project_icon" arch_deb="$arch_deb" \ + arch_appimage="$arch_appimage" APPIMAGE_EXTRACT_AND_RUN=1 ./appimage-builder.AppImage --recipe AppImageBuilder-generated.yml status=$? # Rename AppImage to the final name if the build succeeded. @@ -727,7 +953,6 @@ else status=$? fi fi -cd .. # Check if the archival succeeded. if [ $status -ne 0 ] diff --git a/.ci/dependencies_macports.txt b/.ci/dependencies_macports.txt index 4171a0a69..88270b4da 100644 --- a/.ci/dependencies_macports.txt +++ b/.ci/dependencies_macports.txt @@ -1,10 +1,10 @@ -cmake@3.22.3_0 -pkgconfig@0.29.2_0 -ninja@1.10.2_4 -freetype@2.11.1_0 -libsdl2@2.0.20_0 -libpng@1.6.37_0 -openal-soft@1.21.1_0 -rtmidi@5.0.0_0 -qt5@5.15.3_0 +cmake +pkgconfig +ninja +freetype +libsdl2 +libpng +FAudio +rtmidi +qt5 wget diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ec26581d9..dc600319e 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -104,7 +104,7 @@ jobs: path: build/artifacts/** llvm-windows: - name: "Windows vcpkg/LLVM (${{ matrix.build.name }} ${{ matrix.target.name }})" + name: "Windows vcpkg/LLVM (${{ matrix.ui.name }}, ${{ matrix.build.name }}, ${{ matrix.dynarec.name }}, ${{ matrix.target.name }})" runs-on: windows-2022 @@ -115,38 +115,25 @@ jobs: fail-fast: true matrix: build: -# - name: Regular ODR -# slug: -ODR -# type: Release -# dev-build: off -# new-dynarec: off -# strip: --strip - - name: Debug ODR - slug: -ODR-Debug - type: Debug - dev-build: off - new-dynarec: off - - name: Dev ODR - slug: -ODR-Dev - type: Debug - dev-build: on - new-dynarec: off -# - name: Regular NDR -# slug: -NDR -# type: Release -# strip: --strip -# dev-build: off -# new-dynarec: on - - name: Debug NDR - slug: -NDR-Debug - type: Debug - dev-build: off - new-dynarec: on - - name: Dev NDR - slug: -NDR-Dev - type: Debug - dev-build: on - new-dynarec: on + - name: Debug + dev: off + slug: -Debug + - name: Dev + dev: on + slug: -Dev + dynarec: + - name: ODR + new: off + slug: -ODR + - name: NDR + new: on + slug: -NDR + ui: + - name: Win32 GUI + qt: off + - name: Qt GUI + qt: on + slug: -Qt target: - name: x86 triplet: x86-windows-static @@ -161,13 +148,19 @@ jobs: toolchain: cmake/llvm-win32-aarch64.cmake vcvars: x64_arm64 exclude: - - build: - new-dynarec: off + - dynarec: + new: off target: name: ARM64 steps: - uses: actions/checkout@v2 + - name: Prepare VS environment + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.target.vcvars }} + - name: Add LLVM to path + run: echo "C:/Program Files/LLVM/bin" >> $env:GITHUB_PATH - name: Download Ninja run: > Invoke-WebRequest https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-win.zip -OutFile ninja-win.zip && @@ -181,33 +174,33 @@ jobs: -name "GitHub" -username "86Box" -password "${{ secrets.GITHUB_TOKEN }}" - - run: dir "C:/Program Files/Microsoft Visual Studio/2022/*/VC/Tools/MSVC/*/include" -include stdatomic.h -recurse | del - - name: vcpkg package restore - if: false - run: vcpkg install freetype libpng openal-soft sdl2 rtmidi --triplet ${{ matrix.target.triplet }} + - name: Fix MSVC atomic headers + run: dir "C:/Program Files/Microsoft Visual Studio/2022/*/VC/Tools/MSVC/*/include" -include stdatomic.h -recurse | del - name: Configure CMake run: > - call "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvarsall.bat" ${{ matrix.target.vcvars }} - - set PATH=C:/Program Files/LLVM/bin;%PATH% - - cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=${{ matrix.build.type }} - -D NEW_DYNAREC=${{ matrix.build.new-dynarec }} + cmake -G Ninja -S . -B build + -D DEV_BUILD=${{ matrix.build.dev}} -D NEW_DYNAREC=${{ matrix.dynarec.new }} -D QT=${{ matrix.ui.qt }} + -D CMAKE_BUILD_TYPE=Debug -D CMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -D VCPKG_CHAINLOAD_TOOLCHAIN_FILE=${{ github.workspace }}/${{ matrix.target.toolchain }} -D VCPKG_TARGET_TRIPLET=${{ matrix.target.triplet }} - -D QT=OFF - shell: cmd - - name: Build + -D VCPKG_HOST_TRIPLET=x64-windows + -D VCPKG_USE_HOST_TOOLS=ON + - name: Fix Qt + if: matrix.ui.qt == 'on' run: | - call "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvarsall.bat" ${{ matrix.target.vcvars }} - cmake --build build - shell: cmd + $qtTargetsPath = "${{ github.workspace }}/build/vcpkg_installed/${{ matrix.target.triplet }}/share/Qt6/Qt6Targets.cmake" + (Get-Content $qtTargetsPath) -replace "^.*-Zc:__cplusplus;-permissive-.*$","#$&" | Set-Content $qtTargetsPath + - name: Reconfigure CMake + if: matrix.ui.qt == 'on' + run: cmake clean build + - name: Build + run: cmake --build build - name: Generate package - run: cmake --install build --prefix ./build/artifacts ${{ matrix.build.strip }} + run: cmake --install build --prefix ./build/artifacts - uses: actions/upload-artifact@v2 with: - name: '86Box${{ matrix.build.slug }}-Windows-LLVM-${{ matrix.target.name }}-gha${{ github.run_number }}' + name: '86Box${{ matrix.ui.slug }}${{ matrix.dynarec.slug }}${{ matrix.build.slug }}-Windows-LLVM-${{ matrix.target.name }}-gha${{ github.run_number }}' path: build/artifacts/** linux: @@ -321,13 +314,13 @@ jobs: run: brew install freetype sdl2 libpng rtmidi qt@5 faudio - name: Configure CMake run: >- + PATH=/usr/local/opt/qt@5/bin:$PATH cmake -S . -B build --toolchain cmake/flags-gcc-x86_64.cmake -D DEV_BRANCH=${{ matrix.build.dev-build }} -D NEW_DYNAREC=${{ matrix.build.new-dynarec }} -D CMAKE_BUILD_TYPE=${{ matrix.build.type }} - -D Qt5_DIR=/usr/local/opt/qt@5/lib/cmake/Qt5 - -D Qt5LinguistTools_DIR=/usr/local/opt/qt@5/lib/cmake/Qt5LinguistTools/ + -D CMAKE_FIND_ROOT_PATH=/usr/local/opt/qt@5 - name: Build run: cmake --build build - name: Generate package diff --git a/.gitignore b/.gitignore index a38da5deb..5b1c5bbd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ # CMake -/CMakeUserPresets.json -/CMakeCache.txt -/build +CMakeUserPresets.json +CMakeCache.txt CMakeFiles +/build Makefile *.a /src/*.exe @@ -26,9 +26,11 @@ Makefile # Build scripts /archive_tmp +/archive_tmp_universal /static2dll.* /pacman.txt /deps.txt +/universal_listing.txt /VERSION *.zip *.tar @@ -39,7 +41,9 @@ Makefile # Visual Studio Code /.vs /.vscode -src/win/RCa04980 + +# Windows resource compiler +RC* # Qt Creator CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt index 294f5b55d..aa3ee449d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ if(MUNT_EXTERNAL) endif() project(86Box - VERSION 3.4 + VERSION 3.5 DESCRIPTION "Emulator of x86-based systems" HOMEPAGE_URL "https://86box.net" LANGUAGES C CXX) @@ -61,6 +61,9 @@ if(VCPKG_TOOLCHAIN) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") set(STATIC_BUILD OFF) endif() + + # `vcpkg.json` defaults to Qt6 + set(USE_QT6 ON) endif() if(WIN32) @@ -188,6 +191,7 @@ endif() if(NOT CMAKE_PROJECT_VERSION_PATCH) set(CMAKE_PROJECT_VERSION_PATCH 0) endif() +set(EMU_VERSION_EX "3.50") if(NOT EMU_BUILD_NUM) set(EMU_BUILD_NUM 0) endif() diff --git a/bumpversion.sh b/bumpversion.sh new file mode 100644 index 000000000..2efbeb952 --- /dev/null +++ b/bumpversion.sh @@ -0,0 +1,87 @@ +#!/bin/sh +# +# 86Box A hypervisor and IBM PC system emulator that specializes in +# running old operating systems and software designed for IBM +# PC systems and compatibles from 1981 through fairly recent +# system designs based on the PCI bus. +# +# This file is part of the 86Box distribution. +# +# Convenience script for changing the emulator's version. +# +# +# Authors: RichardG, +# +# Copyright 2022 RichardG. +# + +# Parse arguments. +newversion="$1" +if [ -z "$(echo $newversion | grep '\.')" ] +then + echo '[!] Usage: bumpversion.sh x.y[.z]' + exit 1 +fi +shift + +# Extract version components. +newversion_maj=$(echo $newversion | cut -d. -f1) +newversion_min=$(echo $newversion | cut -d. -f2) +newversion_patch=$(echo $newversion | cut -d. -f3) +[ -z "$newversion_patch" ] && newversion_patch=0 + +base36() { + if [ $1 -lt 10 ] + then + echo $1 + else + printf '%b' $(printf '\\%03o' $((55 + $1))) + fi +} +newversion_maj_base36=$(base36 $newversion_maj) +newversion_min_base36=$(base36 $newversion_min) +newversion_patch_base36=$(base36 $newversion_patch) + +# Switch to the repository root directory. +cd "$(dirname "$0")" + +get_latest_rom_release() { + # get the latest ROM release from GitHub api + curl --silent "https://api.github.com/repos/86Box/roms/releases/latest" | + grep '"tag_name":' | + sed -E 's/.*"([^"]+)".*/\1/' +} + +pretty_date() { + # Ensure we get the date in English + LANG=en_US.UTF-8 date '+%a %b %d %Y' +} + +# Patch files. +patch_file() { + # Stop if the file doesn't exist. + [ ! -e "$1" ] && return + + # Patch file. + if sed -i -r -e "$3" "$1" + then + echo "[-] Patched $2 on $1" + else + echo "[!] Patching $2 on $1 failed" + fi +} +patch_file CMakeLists.txt VERSION 's/^(\s*VERSION ).+/\1'"$newversion"'/' +patch_file CMakeLists.txt EMU_VERSION_EX 's/(\s*set\(EMU_VERSION_EX\s+")[^"]+/\1'"$newversion_maj_base36.$newversion_min_base36$newversion_patch_base36"'/' +patch_file vcpkg.json version-string 's/(^\s*"version-string"\s*:\s*")[^"]+/\1'"$newversion"'/' +patch_file src/include_make/*/version.h EMU_VERSION 's/(#\s*define\s+EMU_VERSION\s+")[^"]+/\1'"$newversion"'/' +patch_file src/include_make/*/version.h EMU_VERSION_EX 's/(#\s*define\s+EMU_VERSION_EX\s+")[^"]+/\1'"$newversion_maj_base36.$newversion_min_base36$newversion_patch_base36"'/' +patch_file src/include_make/*/version.h EMU_VERSION_MAJ 's/(#\s*define\s+EMU_VERSION_MAJ\s+)[0-9]+/\1'"$newversion_maj"'/' +patch_file src/include_make/*/version.h EMU_VERSION_MIN 's/(#\s*define\s+EMU_VERSION_MIN\s+)[0-9]+/\1'"$newversion_min"'/' +patch_file src/include_make/*/version.h EMU_VERSION_PATCH 's/(#\s*define\s+EMU_VERSION_PATCH\s+)[0-9]+/\1'"$newversion_patch"'/' +patch_file src/include_make/*/version.h COPYRIGHT_YEAR 's/(#\s*define\s+COPYRIGHT_YEAR\s+)[0-9]+/\1'"$(date +%Y)"'/' +patch_file src/include_make/*/version.h EMU_DOCS_URL 's/(#\s*define\s+EMU_DOCS_URL\s+"https:\/\/[^\/]+\/en\/v)[^\/]+/\1'"$newversion_maj.$newversion_min"'/' +patch_file src/unix/assets/*.spec Version 's/(Version:\s+)[0-9].+/\1'"$newversion"'/' +patch_file src/unix/assets/*.spec '%global romver' 's/(^%global\ romver\s+)[0-9]{8}/'"$(get_latest_rom_release)"'/' +patch_file src/unix/assets/*.spec 'changelog version' 's/(^[*]\s.*>\s+)[0-9].+/\1'"$newversion"-1'/' +patch_file src/unix/assets/*.spec 'changelog date' 's/(^[*]\s)[a-zA-Z]{3}\s[a-zA-Z]{3}\s[0-9]{2}\s[0-9]{4}/\1'"$(pretty_date)"'/' +patch_file src/unix/assets/*.metainfo.xml release 's/( #include <86box/gdbstub.h> +// Disable c99-designator to avoid the warnings about int ng +#ifdef __clang__ +#if __has_warning("-Wunused-but-set-variable") +#pragma clang diagnostic ignored "-Wunused-but-set-variable" +#endif +#endif + /* Stuff that used to be globally declared in plat.h but is now extern there and declared here instead. */ @@ -164,6 +171,7 @@ int GAMEBLASTER = 0; /* (C) sound option */ int GUS = 0; /* (C) sound option */ int SSI2001 = 0; /* (C) sound option */ int voodoo_enabled = 0; /* (C) video option */ +int ibm8514_enabled = 0; /* (C) video option */ uint32_t mem_size = 0; /* (C) memory size (Installed on system board)*/ uint32_t isa_mem_size = 0; /* (C) memory size (ISA Memory Cards) */ int cpu_use_dynarec = 0; /* (C) cpu uses/needs Dyna */ @@ -417,7 +425,11 @@ pc_init(int argc, char *argv[]) if ((c >= 16) && !strcmp(&exe_path[c - 16], "/Contents/MacOS/")) { exe_path[c - 16] = '\0'; p = path_get_filename(exe_path); - *p = '\0'; + *p = '\0'; + } + if (!strncmp(exe_path, "/private/var/folders/", 21)) { + ui_msgbox_header(MBX_FATAL, L"App Translocation", EMU_NAME_W L" cannot determine the emulated machine's location due to a macOS security feature. Please move the " EMU_NAME_W L" app to another folder (not /Applications), or make a copy of it and open that copy instead."); + return(0); } #elif !defined(_WIN32) /* Grab the actual path if we are an AppImage. */ @@ -507,7 +519,7 @@ usage: rom_add_path(rpath); } else if (!strcasecmp(argv[c], "--config") || !strcasecmp(argv[c], "-C")) { - if ((c+1) == argc) goto usage; + if ((c+1) == argc || plat_dir_check(argv[c + 1])) goto usage; cfg = argv[++c]; } else if (!strcasecmp(argv[c], "--vmname") || diff --git a/src/chipset/opti895.c b/src/chipset/opti895.c index 00f632f59..8efddb96d 100644 --- a/src/chipset/opti895.c +++ b/src/chipset/opti895.c @@ -43,7 +43,6 @@ typedef struct } opti895_t; -#define ENABLE_OPTI895_LOG 1 #ifdef ENABLE_OPTI895_LOG int opti895_do_log = ENABLE_OPTI895_LOG; diff --git a/src/config.c b/src/config.c index 556a13b09..763453a36 100644 --- a/src/config.c +++ b/src/config.c @@ -599,6 +599,12 @@ load_general(void) lang_id = plat_language_code(p); } + mouse_sensitivity = config_get_double(cat, "mouse_sensitivity", 1.0); + if (mouse_sensitivity < 0.5) + mouse_sensitivity = 0.5; + else if (mouse_sensitivity > 2.0) + mouse_sensitivity = 2.0; + p = config_get_string(cat, "iconset", NULL); if (p != NULL) strcpy(icon_set, p); @@ -924,6 +930,7 @@ load_video(void) } voodoo_enabled = !!config_get_int(cat, "voodoo", 0); + ibm8514_enabled = !!config_get_int(cat, "8514a", 0); } @@ -2306,6 +2313,11 @@ save_general(void) else config_delete_var(cat, "confirm_save"); + if (mouse_sensitivity != 1.0) + config_set_double(cat, "mouse_sensitivity", mouse_sensitivity); + else + config_delete_var(cat, "mouse_sensitivity"); + if (lang_id == DEFAULT_LANGUAGE) config_delete_var(cat, "language"); else @@ -2457,6 +2469,11 @@ save_video(void) else config_set_int(cat, "voodoo", voodoo_enabled); + if (ibm8514_enabled == 0) + config_delete_var(cat, "8514a"); + else + config_set_int(cat, "8514a", ibm8514_enabled); + delete_section_if_empty(cat); } diff --git a/src/cpu/x86.c b/src/cpu/x86.c index 60bc33b9b..77dd3b139 100644 --- a/src/cpu/x86.c +++ b/src/cpu/x86.c @@ -36,6 +36,8 @@ #include <86box/pci.h> #include <86box/ppi.h> #include <86box/timer.h> +#include <86box/video.h> +#include <86box/vid_svga.h> /* The opcode of the instruction currently being executed. */ uint8_t opcode; @@ -341,6 +343,9 @@ softresetx86(void) if (soft_reset_mask) return; + if (ibm8514_enabled) + vga_on = 1; + reset_common(0); } diff --git a/src/discord.c b/src/discord.c index 12a5a0396..27b8c6fdf 100644 --- a/src/discord.c +++ b/src/discord.c @@ -89,7 +89,9 @@ discord_update_activity(int paused) *(paren - 1) = '\0'; #pragma GCC diagnostic push +#if defined(__GNUC__) #pragma GCC diagnostic ignored "-Wformat-truncation" +#endif if (strlen(vm_name) < 100) { snprintf(activity.details, sizeof(activity.details), "Running \"%s\"", vm_name); diff --git a/src/include/86box/86box.h b/src/include/86box/86box.h index 8b84100fd..61269fddb 100644 --- a/src/include/86box/86box.h +++ b/src/include/86box/86box.h @@ -111,7 +111,8 @@ extern int sound_is_float, /* (C) sound uses FP values */ GAMEBLASTER, /* (C) sound option */ GUS, GUSMAX, /* (C) sound option */ SSI2001, /* (C) sound option */ - voodoo_enabled; /* (C) video option */ + voodoo_enabled, /* (C) video option */ + ibm8514_enabled; /* (C) video option */ extern uint32_t mem_size; /* (C) memory size (Installed on system board) */ extern uint32_t isa_mem_size; /* (C) memory size (ISA Memory Cards) */ extern int cpu, /* (C) cpu type */ @@ -130,6 +131,7 @@ extern int enable_discord; /* (C) enable Discord integration */ extern int is_pentium; /* TODO: Move back to cpu/cpu.h when it's figured out, how to remove that hack from the ET4000/W32p. */ extern int fixed_size_x, fixed_size_y; +extern double mouse_sensitivity; /* (C) Mouse sensitivity scale */ extern char exe_path[2048]; /* path (dir) of executable */ diff --git a/src/include/86box/keyboard.h b/src/include/86box/keyboard.h index b87f2552a..ff588c3b4 100644 --- a/src/include/86box/keyboard.h +++ b/src/include/86box/keyboard.h @@ -73,6 +73,7 @@ extern const device_t keyboard_xt_olivetti_device; extern const device_t keyboard_xt_zenith_device; extern const device_t keyboard_at_device; extern const device_t keyboard_at_ami_device; +extern const device_t keyboard_at_samsung_device; extern const device_t keyboard_at_toshiba_device; extern const device_t keyboard_at_olivetti_device; extern const device_t keyboard_at_ncr_device; diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 85a287e1a..fae7fb587 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -818,6 +818,12 @@ extern int machine_xt_pc700_init(const machine_t *); extern int machine_xt_pc500_init(const machine_t *); extern int machine_xt_vendex_init(const machine_t *); extern int machine_xt_znic_init(const machine_t *); +extern int machine_xt_super16t_init(const machine_t *); +extern int machine_xt_super16te_init(const machine_t *); +extern int machine_xt_top88_init(const machine_t *); +extern int machine_xt_kaypropc_init(const machine_t *); +extern int machine_xt_sansx16_init(const machine_t *); +extern int machine_xt_bw230_init(const machine_t *); extern int machine_xt_iskra3104_init(const machine_t *); diff --git a/src/include/86box/mca.h b/src/include/86box/mca.h index 68aa2563f..f41eda9cf 100644 --- a/src/include/86box/mca.h +++ b/src/include/86box/mca.h @@ -5,8 +5,10 @@ extern void mca_init(int nr_cards); extern void mca_add(uint8_t (*read)(int addr, void *priv), void (*write)(int addr, uint8_t val, void *priv), uint8_t (*feedb)(void *priv), void (*reset)(void *priv), void *priv); extern void mca_set_index(int index); extern uint8_t mca_read(uint16_t port); +extern uint8_t mca_read_index(uint16_t port, int index); extern void mca_write(uint16_t port, uint8_t val); extern uint8_t mca_feedb(void); +extern int mca_get_nr_cards(void); extern void mca_reset(void); extern void ps2_cache_clean(void); diff --git a/src/include/86box/plat_dir.h b/src/include/86box/plat_dir.h index 41bd7fe62..46b57ee34 100644 --- a/src/include/86box/plat_dir.h +++ b/src/include/86box/plat_dir.h @@ -60,11 +60,7 @@ typedef struct { /* Function prototypes. */ -#ifdef UNICODE -extern DIR *opendirw(const wchar_t *); -#else extern DIR *opendir(const char *); -#endif extern struct dirent *readdir(DIR *); extern long telldir(DIR *); extern void seekdir(DIR *, long); diff --git a/src/include/86box/resource.h b/src/include/86box/resource.h index 4365243c0..f92eb919d 100644 --- a/src/include/86box/resource.h +++ b/src/include/86box/resource.h @@ -183,6 +183,7 @@ #define IDC_COMBO_VIDEO 1021 #define IDC_CHECK_VOODOO 1022 #define IDC_BUTTON_VOODOO 1023 +#define IDC_CHECK_IBM8514 1024 #define IDC_INPUT 1030 /* input config */ #define IDC_COMBO_MOUSE 1031 diff --git a/src/include/86box/version.h.in b/src/include/86box/version.h.in index 18ff9a00e..0e6595536 100644 --- a/src/include/86box/version.h.in +++ b/src/include/86box/version.h.in @@ -22,7 +22,7 @@ #define EMU_VERSION "@CMAKE_PROJECT_VERSION@" #define EMU_VERSION_W LSTR(EMU_VERSION) -#define EMU_VERSION_EX "@CMAKE_PROJECT_VERSION_MAJOR@.@CMAKE_PROJECT_VERSION_MINOR@@CMAKE_PROJECT_VERSION_PATCH@" +#define EMU_VERSION_EX "@EMU_VERSION_EX@" #define EMU_VERSION_MAJ @CMAKE_PROJECT_VERSION_MAJOR@ #define EMU_VERSION_MIN @CMAKE_PROJECT_VERSION_MINOR@ #define EMU_VERSION_PATCH @CMAKE_PROJECT_VERSION_PATCH@ diff --git a/src/include/86box/vid_8514a.h b/src/include/86box/vid_8514a.h new file mode 100644 index 000000000..af46b166d --- /dev/null +++ b/src/include/86box/vid_8514a.h @@ -0,0 +1,111 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Emulation of the 8514/A card from IBM for the MCA bus and + * generic ISA bus clones without vendor extensions. + * + * + * + * Authors: TheCollector1995 + * + * Copyright 2022 TheCollector1995. + */ + +#ifndef VIDEO_8514A_H +# define VIDEO_8514A_H + +typedef struct ibm8514_t +{ + uint8_t pos_regs[8]; + + int force_old_addr; + int type; + + uint32_t vram_size; + uint32_t vram_mask; + + PALETTE vgapal; + uint8_t dac_mask, dac_status; + uint32_t *map8; + int dac_addr, dac_pos, dac_r, dac_g; + + struct { + uint16_t subsys_cntl; + uint16_t setup_md; + uint8_t advfunc_cntl, ext_advfunc_cntl; + uint16_t cur_y, cur_y_bitres; + uint16_t cur_x, cur_x_bitres; + int16_t desty_axstp; + int16_t destx_distp; + int16_t err_term; + int16_t maj_axis_pcnt; + uint16_t cmd, cmd_back; + uint16_t short_stroke; + uint16_t bkgd_color; + uint16_t frgd_color; + uint16_t wrt_mask; + uint16_t rd_mask; + uint16_t color_cmp; + uint16_t bkgd_mix; + uint16_t frgd_mix; + uint16_t multifunc_cntl; + uint16_t multifunc[16]; + int16_t clip_left, clip_top; + uint8_t pix_trans[2]; + int poly_draw; + int ssv_state; + int x1, x2, y1, y2; + int sys_cnt, sys_cnt2; + int temp_cnt; + int16_t cx, cy; + int sx, sy; + int dx, dy; + uint32_t src, dest; + uint32_t newsrc_blt, newdest_blt; + uint32_t newdest_in, newdest_out; + uint8_t *writemono, *nibbleset; + int x_count, xx_count, y_count; + int input, output; + + uint16_t cur_x_bit12, cur_y_bit12; + int ssv_len; + uint8_t ssv_dir; + uint8_t ssv_draw; + int odd_in, odd_out; + + uint16_t scratch; + int fill_state, fill_drop; + } accel; + + uint16_t test; + + int v_total, dispend, v_syncstart, split, + h_disp, h_disp_old, h_total, h_disp_time, rowoffset, + dispon, hdisp_on, linecountff, + vc, linepos, oddeven, cursoron, blink, scrollcache, + firstline, lastline, firstline_draw, lastline_draw, + displine, fullchange, x_add, y_add; + uint32_t ma, maback; + + uint8_t *vram, *changedvram, linedbl; + + uint8_t data_available, data_available2; + uint8_t scanmodulos, rowcount; + int htotal, hdisp, vtadj, vdadj, vsadj, sc, + vtb, vdb, vsb, vsyncstart, vsyncwidth; + int vtotal, vdisp; + int disp_cntl, interlace; + uint8_t subsys_cntl, subsys_stat; + + volatile int force_busy, force_busy2; + + int blitter_busy; + uint64_t blitter_time; + uint64_t status_time; +} ibm8514_t; +#endif /*VIDEO_8514A_H*/ diff --git a/src/include/86box/vid_svga.h b/src/include/86box/vid_svga.h index 5109207d0..30d66f436 100644 --- a/src/include/86box/vid_svga.h +++ b/src/include/86box/vid_svga.h @@ -17,6 +17,9 @@ * Copyright 2016-2020 Miran Grca. */ +#include <86box/thread.h> +#include <86box/vid_8514a.h> + #ifndef VIDEO_SVGA_H # define VIDEO_SVGA_H @@ -45,6 +48,7 @@ typedef union { typedef struct svga_t { + ibm8514_t dev8514; mem_mapping_t mapping; uint8_t fast, chain4, chain2_write, chain2_read, @@ -167,6 +171,11 @@ typedef struct svga_t void *ramdac, *clock_gen; } svga_t; +extern svga_t *svga_8514; +extern int vga_on; + +extern void ibm8514_poll(ibm8514_t *dev, svga_t *svga); +extern void ibm8514_recalctimings(svga_t *svga); extern int svga_init(const device_t *info, svga_t *svga, void *p, int memsize, void (*recalctimings_ex)(struct svga_t *svga), diff --git a/src/include/86box/video.h b/src/include/86box/video.h index 44bf94a08..7253e1049 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -200,6 +200,9 @@ extern void agpgart_set_gart(void *handle, uint32_t base); #ifdef EMU_DEVICE_H +/* IBM 8514/A and generic clones*/ +extern void ibm8514_device_add(void); + /* ATi Mach64 */ extern const device_t mach64gx_isa_device; extern const device_t mach64gx_vlb_device; diff --git a/src/include_make/86box/version.h b/src/include_make/86box/version.h index 8bf418791..548ca28a6 100644 --- a/src/include_make/86box/version.h +++ b/src/include_make/86box/version.h @@ -20,11 +20,11 @@ #define EMU_NAME "86Box" #define EMU_NAME_W LSTR(EMU_NAME) -#define EMU_VERSION "3.4" +#define EMU_VERSION "3.5" #define EMU_VERSION_W LSTR(EMU_VERSION) -#define EMU_VERSION_EX "3.40" +#define EMU_VERSION_EX "3.50" #define EMU_VERSION_MAJ 3 -#define EMU_VERSION_MIN 4 +#define EMU_VERSION_MIN 5 #define EMU_VERSION_PATCH 0 #define EMU_BUILD_NUM 0 @@ -40,7 +40,7 @@ #define EMU_ROMS_URL "https://github.com/86Box/roms/releases/latest" #define EMU_ROMS_URL_W LSTR(EMU_ROMS_URL) #ifdef RELEASE_BUILD -# define EMU_DOCS_URL "https://86box.readthedocs.io/en/v3.2/" +# define EMU_DOCS_URL "https://86box.readthedocs.io/en/v3.5/" #else # define EMU_DOCS_URL "https://86box.readthedocs.io" #endif diff --git a/src/machine/m_at_286_386sx.c b/src/machine/m_at_286_386sx.c index c853b153b..5f98d750c 100644 --- a/src/machine/m_at_286_386sx.c +++ b/src/machine/m_at_286_386sx.c @@ -245,6 +245,7 @@ machine_at_px286_init(const machine_t *model) return ret; } + int machine_at_micronics386_init(const machine_t *model) { @@ -252,17 +253,15 @@ machine_at_micronics386_init(const machine_t *model) ret = bios_load_interleaved("roms/machines/micronics386/386-Micronics-09-00021-EVEN.BIN", "roms/machines/micronics386/386-Micronics-09-00021-ODD.BIN", - 0x000f0000, 131072, 0); + 0x000f0000, 65536, 0); if (bios_only || !ret) return ret; machine_at_init(model); - device_add(&neat_device); - if (fdc_type == FDC_INTERNAL) - device_add(&fdc_at_device); + device_add(&fdc_at_device); return ret; } diff --git a/src/machine/m_at_386dx_486.c b/src/machine/m_at_386dx_486.c index 6fe53e5e2..5944132e5 100644 --- a/src/machine/m_at_386dx_486.c +++ b/src/machine/m_at_386dx_486.c @@ -236,7 +236,7 @@ machine_at_spc6000a_init(const machine_t *model) if (fdc_type == FDC_INTERNAL) device_add(&fdc_at_device); - device_add(&keyboard_at_ami_device); + device_add(&keyboard_at_samsung_device); return ret; } diff --git a/src/machine/m_xt.c b/src/machine/m_xt.c index dd7ba507f..f95ee2fe0 100644 --- a/src/machine/m_xt.c +++ b/src/machine/m_xt.c @@ -451,8 +451,113 @@ machine_xt_vendex_init(const machine_t *model) machine_xt_clone_init(model); + return ret; +} + +int +machine_xt_super16t_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/super16t/Hyundai SUPER-16T - System BIOS HEA v1.12Ta (16k)(MBM27128)(1986).BIN", + 0x000fc000, 16384, 0); + + if (bios_only || !ret) + return ret; + + machine_xt_clone_init(model); + /* On-board FDC cannot be disabled */ device_add(&fdc_xt_device); return ret; } + +int +machine_xt_super16te_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/super16te/Hyundai SUPER-16TE - System BIOS v2.00Id (16k)(D27128A)(1989).BIN", + 0x000fc000, 16384, 0); + + if (bios_only || !ret) + return ret; + + machine_xt_clone_init(model); + + /* On-board FDC cannot be disabled */ + device_add(&fdc_xt_device); + + return ret; +} + +int +machine_xt_top88_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/top88/Hyosung Topstar 88T - BIOS version 3.0.bin", + 0x000fc000, 16384, 0); + + if (bios_only || !ret) + return ret; + + machine_xt_clone_init(model); + + /* On-board FDC cannot be disabled */ + device_add(&fdc_xt_device); + + return ret; +} + +int +machine_xt_kaypropc_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/kaypropc/Kaypro_v2.03K.bin", + 0x000fe000, 8192, 0); + + if (bios_only || !ret) + return ret; + + machine_xt_clone_init(model); + + return ret; +} + +int +machine_xt_sansx16_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/sansx16/tmm27128ad.bin.bin", + 0x000fc000, 16384, 0); + + if (bios_only || !ret) + return ret; + + machine_xt_clone_init(model); + + /* On-board FDC cannot be disabled */ + device_add(&fdc_xt_device); + + return ret; +} + +int +machine_xt_bw230_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/bw230/bondwell.bin", + 0x000fe000, 8192, 0); + + if (bios_only || !ret) + return ret; + + machine_xt_clone_init(model); + + return ret; +} diff --git a/src/machine/m_xt_olivetti.c b/src/machine/m_xt_olivetti.c index a8743cfc5..8541fa2c3 100644 --- a/src/machine/m_xt_olivetti.c +++ b/src/machine/m_xt_olivetti.c @@ -752,8 +752,8 @@ machine_xt_m24_init(const machine_t *model) int ret; m24_kbd_t *m24_kbd; - ret = bios_load_interleaved("roms/machines/m24/olivetti_m24_version_1.43_low.bin", - "roms/machines/m24/olivetti_m24_version_1.43_high.bin", + ret = bios_load_interleaved("roms/machines/m24/olivetti_m24_bios_version_1.44_low_even.bin", + "roms/machines/m24/olivetti_m24_bios_version_1.44_high_odd.bin", 0x000fc000, 16384, 0); if (bios_only || !ret) diff --git a/src/machine/machine.c b/src/machine/machine.c index 5813c1d1a..8bd83b6ba 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -114,6 +114,12 @@ machine_init_ex(int m) if (bios_only || !ret) return ret; + if (gfxcard != VID_NONE) { + if (ibm8514_enabled) { + ibm8514_device_add(); + } + } + /* Reset the graphics card (or do nothing if it was already done by the machine's init function). */ video_reset(gfxcard); diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 373b75bb2..6a8e74ded 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -188,12 +188,17 @@ const machine_t machines[] = { { "[8088] IBM XT (1986)", "ibmxt86", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt86_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 256, 640, 64, 0, NULL, NULL }, { "[8088] American XT Computer", "americxt", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_americxt_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, { "[8088] AMI XT clone", "amixt", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_amixt_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, + { "[8088] Bondwell BW230", "bw230", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_bw230_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, { "[8088] Columbia Data Products MPC-1600", "mpc1600", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_mpc1600_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 512, 64, 0, NULL, NULL }, { "[8088] Compaq Portable", "portable", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_compaq_portable_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 640, 128, 0, NULL, NULL }, { "[8088] DTK PIM-TB10-Z", "dtk", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_dtk_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, { "[8088] Eagle PC Spirit", "pcspirit", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_pcspirit_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 640, 64, 0, NULL, NULL }, { "[8088] Generic XT clone", "genxt", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_genxt_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, + { "[8088] Hyosung Topstar 88T", "top88", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_top88_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 640, 64, 0, NULL, NULL }, + { "[8088] Hyundai SUPER-16T", "super16t", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_super16t_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 4772728, 7159092, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 640, 64, 0, NULL, NULL }, + { "[8088] Hyundai SUPER-16TE", "super16te", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_super16te_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 10000000, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 640, 64, 0, NULL, NULL }, { "[8088] Juko ST", "jukopc", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_jukopc_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, + { "[8088] Kaypro PC", "kaypropc", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_kaypropc_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 640, 64, 0, NULL, NULL }, { "[8088] Multitech PC-500", "pc500", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_pc500_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 640, 64, 0, NULL, NULL }, { "[8088] Multitech PC-700", "pc700", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_pc700_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 128, 640, 64, 0, NULL, NULL }, { "[8088] NCR PC4i", "pc4i", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_pc4i_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 256, 640, 256, 0, NULL, NULL }, @@ -201,6 +206,7 @@ const machine_t machines[] = { { "[8088] OpenXT", "openxt", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_openxt_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, { "[8088] Philips P3105/NMS9100", "p3105", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_p3105_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_XTA, 256, 768, 256, 0, NULL, NULL }, { "[8088] Phoenix XT clone", "pxxt", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_pxxt_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, + { "[8088] Sanyo SX-16", "sansx16", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_sansx16_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 256, 640, 256, 0, NULL, NULL }, { "[8088] Schneider EuroPC", "europc", MACHINE_TYPE_8088, MACHINE_CHIPSET_PROPRIETARY, machine_europc_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088_EUROPC, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_XTA | MACHINE_MOUSE, 512, 640, 128, 15, NULL, NULL }, { "[8088] Super PC/Turbo XT", "pcxt", MACHINE_TYPE_8088, MACHINE_CHIPSET_DISCRETE, machine_xt_pcxt_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_FLAGS_NONE, 64, 640, 64, 0, NULL, NULL }, { "[8088] Tandy 1000", "tandy", MACHINE_TYPE_8088, MACHINE_CHIPSET_PROPRIETARY, machine_tandy_init, 0, 0, MACHINE_AVAILABLE, 0 , CPU_PKG_8088_EUROPC, CPU_BLOCK_NONE, 0, 0, 0, 0, 0, 0, MACHINE_PC, MACHINE_VIDEO_FIXED, 128, 640, 128, 0, tandy1k_get_device, NULL }, diff --git a/src/mca.c b/src/mca.c index 5a9b355f8..4ef00318d 100644 --- a/src/mca.c +++ b/src/mca.c @@ -45,6 +45,20 @@ uint8_t mca_read(uint16_t port) return mca_card_read[mca_index](port, mca_priv[mca_index]); } +uint8_t mca_read_index(uint16_t port, int index) +{ + if (mca_index >= mca_nr_cards) + return 0xff; + if (!mca_card_read[index]) + return 0xff; + return mca_card_read[index](port, mca_priv[index]); +} + +int mca_get_nr_cards(void) +{ + return mca_nr_cards; +} + void mca_write(uint16_t port, uint8_t val) { if (mca_index >= mca_nr_cards) diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 204641947..e5a03a8ce 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -23,6 +23,10 @@ if(SLIRP_EXTERNAL) find_package(PkgConfig REQUIRED) pkg_check_modules(SLIRP REQUIRED IMPORTED_TARGET slirp) target_link_libraries(86Box PkgConfig::SLIRP) + + if(WIN32) + target_link_libraries(PkgConfig::SLIRP INTERFACE wsock32 ws2_32 iphlpapi iconv) + endif() else() add_subdirectory(slirp) target_link_libraries(86Box slirp) diff --git a/src/network/net_dp8390.c b/src/network/net_dp8390.c index 819412454..a5f26b69f 100644 --- a/src/network/net_dp8390.c +++ b/src/network/net_dp8390.c @@ -42,11 +42,11 @@ dp8390_log(const char *fmt, ...) { va_list ap; - if (dp8390_do_log >= lvl) { +// if (dp8390_do_log >= lvl) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); - } +// } } #else #define dp8390_log(lvl, fmt, ...) @@ -97,8 +97,8 @@ dp8390_chipmem_read(dp8390_t *dev, uint32_t addr, unsigned int len) uint32_t retval = 0; #ifdef ENABLE_DP8390_LOG - if ((len > 1) && (addr & (len - 1)) - dp3890_log("DP8390: unaligned chipmem word read\n"); + if ((len > 1) && (addr & (len - 1))) + dp8390_log("DP8390: unaligned chipmem word read\n"); #endif dp8390_log("DP8390: Chipmem Read Address=%04x\n", addr); @@ -126,7 +126,7 @@ dp8390_chipmem_write(dp8390_t *dev, uint32_t addr, uint32_t val, unsigned len) int i; #ifdef ENABLE_DP8390_LOG - if ((len > 1) && (addr & (len - 1)) + if ((len > 1) && (addr & (len - 1))) dp8390_log("DP8390: unaligned chipmem word write\n"); #endif @@ -199,7 +199,7 @@ dp8390_write_cr(dp8390_t *dev, uint32_t val) dev->remote_start = dev->remote_dma = dev->bound_ptr * 256; dev->remote_bytes = (uint16_t) dp8390_chipmem_read(dev, dev->bound_ptr * 256 + 2, 2); dp8390_log("DP8390: sending buffer #x%x length %d\n", - dev->dp8390.remote_start, dev->dp8390.remote_bytes); + dev->remote_start, dev->remote_bytes); } /* Check for start-tx */ @@ -283,7 +283,7 @@ dp8390_rx_common(void *priv, uint8_t *buf, int io_len) int endbytes; if (io_len != 60) - dp8390_log("%s: rx_frame with length %d\n", dev->name, io_len); + dp8390_log("rx_frame with length %d\n", io_len); if ((dev->CR.stop != 0) || (dev->page_start == 0)) return 0; @@ -706,7 +706,7 @@ dp8390_page0_write(dp8390_t *dev, uint32_t off, uint32_t val, unsigned len) dev->DCR.longaddr = ((val & 0x04) == 0x04); /* illegal ? */ dev->DCR.loop = ((val & 0x08) == 0x08); dev->DCR.auto_rx = ((val & 0x10) == 0x10); /* also illegal ? */ - dev->DCR.fifo_size = (val & 0x50) >> 5; + dev->DCR.fifo_size = (val & 0x60) >> 5; break; case 0x0f: /* IMR */ @@ -801,9 +801,9 @@ dp8390_page1_write(dp8390_t *dev, uint32_t off, uint32_t val, unsigned len) dev->physaddr[off - 1] = val; if (off == 6) dp8390_log("DP8390: Physical address set to %02x:%02x:%02x:%02x:%02x:%02x\n", - dev->dp8390->physaddr[0], dev->dp8390.physaddr[1], - dev->dp8390->physaddr[2], dev->dp8390.physaddr[3], - dev->dp8390->physaddr[4], dev->dp8390.physaddr[5]); + dev->physaddr[0], dev->physaddr[1], + dev->physaddr[2], dev->physaddr[3], + dev->physaddr[4], dev->physaddr[5]); break; case 0x07: /* CURR */ diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 053cf691d..7a69b940d 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -19,9 +19,20 @@ if(QT_STATIC AND MINGW) set(CMAKE_PREFIX_PATH "$ENV{MSYSTEM_PREFIX}/qt${QT_MAJOR}-static") endif() +if(VCPKG_TOOLCHAIN AND VCPKG_USE_HOST_TOOLS) + set(QT_HOST_PATH "${VCPKG_INSTALLED_DIR}/${VCPKG_HOST_TRIPLET}/tools/Qt${QT_MAJOR}") + set(QT_HOST_PATH_CMAKE_DIR ${VCPKG_INSTALLED_DIR}/${VCPKG_HOST_TRIPLET}) + set(Qt${QT_MAJOR}LinguistTools_ROOT ${QT_HOST_PATH_CMAKE_DIR}) +endif() + +# CMake is a bitch and calls the Harfbuzz config twice on MinGW + Qt6 +# if config mode is preferred :) +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF) + find_package(Threads REQUIRED) find_package(Qt${QT_MAJOR} COMPONENTS Core Widgets Network OpenGL REQUIRED) -find_package(Qt${QT_MAJOR}LinguistTools REQUIRED) +find_package(Qt${QT_MAJOR}LinguistTools REQUIRED NO_CMAKE_FIND_ROOT_PATH) + # TODO: Is this the correct way to do this, and is it required on any # other platforms or with Qt 5? if(APPLE AND USE_QT6) @@ -145,6 +156,16 @@ add_library(ui STATIC qt_unixmanagerfilter.cpp qt_unixmanagerfilter.hpp + qt_vulkanwindowrenderer.hpp + qt_vulkanwindowrenderer.cpp + + qt_vulkanrenderer.hpp + qt_vulkanrenderer.cpp + + qt_mcadevicelist.hpp + qt_mcadevicelist.cpp + qt_mcadevicelist.ui + ../qt_resources.qrc ) @@ -166,7 +187,7 @@ if(WIN32) # MSVC linker adds its own manifest to the executable, which fails if # we include ours in 86Box.rc. We therefore need to pass the manifest # directly as as a source file, so the linker can use that instead. - set_property(SOURCE ../win/86Box-qt.rc PROPERTY COMPILE_DEFINITIONS NO_INCLUDE_MANIFEST) + set_property(SOURCE ../win/86Box-qt.rc DIRECTORY .. PROPERTY COMPILE_DEFINITIONS NO_INCLUDE_MANIFEST) target_sources(86Box PRIVATE ../win/86Box.manifest) endif() @@ -213,9 +234,18 @@ target_link_libraries( Threads::Threads ) -# needed for static builds -if (WIN32) - qt_import_plugins(plat INCLUDE Qt${QT_MAJOR}::QWindowsIntegrationPlugin Qt${QT_MAJOR}::QICOPlugin QWindowsVistaStylePlugin) +if(WIN32) + if(STATIC_BUILD) + # needed for static builds + qt_import_plugins(plat INCLUDE Qt${QT_MAJOR}::QWindowsIntegrationPlugin Qt${QT_MAJOR}::QICOPlugin Qt${QT_MAJOR}::QWindowsVistaStylePlugin) + else() + install(CODE " + get_filename_component(CMAKE_INSTALL_PREFIX_ABSOLUTE \${CMAKE_INSTALL_PREFIX} ABSOLUTE) + execute_process( + COMMAND $ + \"\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/$\") + ") + endif() endif() # loads a macro to install Qt5 plugins on macOS @@ -301,13 +331,9 @@ endif() set(QM_FILES) file(GLOB po_files "${CMAKE_CURRENT_SOURCE_DIR}/languages/*.po") foreach(po_file ${po_files}) - get_target_property(LCONVERT_EXECUTABLE Qt${QT_MAJOR}::lconvert IMPORTED_LOCATION) - get_filename_component(_lconvert_bin_dir "${LCONVERT_EXECUTABLE}" DIRECTORY) - find_program(LCONVERT_EXECUTABLE lconvert HINTS "${_lconvert_bin_dir}") - get_filename_component(PO_FILE_NAME ${po_file} NAME_WE) add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/86box_${PO_FILE_NAME}.qm" - COMMAND ${LCONVERT_EXECUTABLE} -i ${po_file} -o ${CMAKE_CURRENT_BINARY_DIR}/86box_${PO_FILE_NAME}.qm + COMMAND "$" -i ${po_file} -o ${CMAKE_CURRENT_BINARY_DIR}/86box_${PO_FILE_NAME}.qm WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" DEPENDS "${po_file}") list(APPEND QM_FILES "${CMAKE_CURRENT_BINARY_DIR}/86box_${PO_FILE_NAME}.qm") diff --git a/src/qt/qt.c b/src/qt/qt.c index 259f79c88..19ced376e 100644 --- a/src/qt/qt.c +++ b/src/qt/qt.c @@ -47,6 +47,8 @@ plat_vidapi(char* api) { return 2; } else if (!strcasecmp(api, "qt_opengl3")) { return 3; + } else if (!strcasecmp(api, "qt_vulkan")) { + return 4; } return 0; @@ -68,6 +70,9 @@ char* plat_vidapi_name(int api) { case 3: name = "qt_opengl3"; break; + case 4: + name = "qt_vulkan"; + break; default: fatal("Unknown renderer: %i\n", api); break; diff --git a/src/qt/qt_hardwarerenderer.cpp b/src/qt/qt_hardwarerenderer.cpp index 2e38197de..f285b5745 100644 --- a/src/qt/qt_hardwarerenderer.cpp +++ b/src/qt/qt_hardwarerenderer.cpp @@ -150,6 +150,7 @@ void HardwareRenderer::paintGL() { texcoords.push_back(QVector2D((float)source.x() / 2048.f, (float)(source.y() + source.height()) / 2048.f)); texcoords.push_back(QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y() + source.height()) / 2048.f)); texcoords.push_back(QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y()) / 2048.f)); + m_vbo[PROGRAM_VERTEX_ATTRIBUTE].bind(); m_vbo[PROGRAM_VERTEX_ATTRIBUTE].write(0, verts.data(), sizeof(QVector2D) * 4); m_vbo[PROGRAM_VERTEX_ATTRIBUTE].release(); m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].bind(); m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].write(0, texcoords.data(), sizeof(QVector2D) * 4); m_vbo[PROGRAM_TEXCOORD_ATTRIBUTE].release(); diff --git a/src/qt/qt_machinestatus.cpp b/src/qt/qt_machinestatus.cpp index 75062aede..39c98b6aa 100644 --- a/src/qt/qt_machinestatus.cpp +++ b/src/qt/qt_machinestatus.cpp @@ -361,7 +361,11 @@ void MachineStatus::refresh(QStatusBar* sbar) { connect((ClickableLabel*)d->cassette.label.get(), &ClickableLabel::clicked, [](QPoint pos) { MediaMenu::ptr->cassetteMenu->popup(pos - QPoint(0, MediaMenu::ptr->cassetteMenu->sizeHint().height())); }); + connect((ClickableLabel*)d->cassette.label.get(), &ClickableLabel::dropped, [](QString str) { + MediaMenu::ptr->cassetteMount(str, false); + }); d->cassette.label->setToolTip(MediaMenu::ptr->cassetteMenu->title()); + d->cassette.label->setAcceptDrops(true); sbar->addWidget(d->cassette.label.get()); } @@ -372,7 +376,11 @@ void MachineStatus::refresh(QStatusBar* sbar) { connect((ClickableLabel*)d->cartridge[i].label.get(), &ClickableLabel::clicked, [i](QPoint pos) { MediaMenu::ptr->cartridgeMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->cartridgeMenus[i]->sizeHint().height())); }); + connect((ClickableLabel*)d->cartridge[i].label.get(), &ClickableLabel::dropped, [i](QString str) { + MediaMenu::ptr->cartridgeMount(i, str); + }); d->cartridge[i].label->setToolTip(MediaMenu::ptr->cartridgeMenus[i]->title()); + d->cartridge[i].label->setAcceptDrops(true); sbar->addWidget(d->cartridge[i].label.get()); } } @@ -392,7 +400,11 @@ void MachineStatus::refresh(QStatusBar* sbar) { connect((ClickableLabel*)d->fdd[i].label.get(), &ClickableLabel::clicked, [i](QPoint pos) { MediaMenu::ptr->floppyMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->floppyMenus[i]->sizeHint().height())); }); + connect((ClickableLabel*)d->fdd[i].label.get(), &ClickableLabel::dropped, [i](QString str) { + MediaMenu::ptr->floppyMount(i, str, false); + }); d->fdd[i].label->setToolTip(MediaMenu::ptr->floppyMenus[i]->title()); + d->fdd[i].label->setAcceptDrops(true); sbar->addWidget(d->fdd[i].label.get()); }); @@ -403,7 +415,11 @@ void MachineStatus::refresh(QStatusBar* sbar) { connect((ClickableLabel*)d->cdrom[i].label.get(), &ClickableLabel::clicked, [i](QPoint pos) { MediaMenu::ptr->cdromMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->cdromMenus[i]->sizeHint().height())); }); + connect((ClickableLabel*)d->cdrom[i].label.get(), &ClickableLabel::dropped, [i](QString str) { + MediaMenu::ptr->cdromMount(i, str); + }); d->cdrom[i].label->setToolTip(MediaMenu::ptr->cdromMenus[i]->title()); + d->cdrom[i].label->setAcceptDrops(true); sbar->addWidget(d->cdrom[i].label.get()); }); @@ -414,7 +430,11 @@ void MachineStatus::refresh(QStatusBar* sbar) { connect((ClickableLabel*)d->zip[i].label.get(), &ClickableLabel::clicked, [i](QPoint pos) { MediaMenu::ptr->zipMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->zipMenus[i]->sizeHint().height())); }); + connect((ClickableLabel*)d->zip[i].label.get(), &ClickableLabel::dropped, [i](QString str) { + MediaMenu::ptr->zipMount(i, str, false); + }); d->zip[i].label->setToolTip(MediaMenu::ptr->zipMenus[i]->title()); + d->zip[i].label->setAcceptDrops(true); sbar->addWidget(d->zip[i].label.get()); }); @@ -425,7 +445,11 @@ void MachineStatus::refresh(QStatusBar* sbar) { connect((ClickableLabel*)d->mo[i].label.get(), &ClickableLabel::clicked, [i](QPoint pos) { MediaMenu::ptr->moMenus[i]->popup(pos - QPoint(0, MediaMenu::ptr->moMenus[i]->sizeHint().height())); }); + connect((ClickableLabel*)d->mo[i].label.get(), &ClickableLabel::dropped, [i](QString str) { + MediaMenu::ptr->moMount(i, str, false); + }); d->mo[i].label->setToolTip(MediaMenu::ptr->moMenus[i]->title()); + d->mo[i].label->setAcceptDrops(true); sbar->addWidget(d->mo[i].label.get()); }); diff --git a/src/qt/qt_machinestatus.hpp b/src/qt/qt_machinestatus.hpp index 6f83234df..ba30d36f2 100644 --- a/src/qt/qt_machinestatus.hpp +++ b/src/qt/qt_machinestatus.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -19,10 +20,35 @@ class ClickableLabel : public QLabel { signals: void clicked(QPoint); void doubleClicked(QPoint); + void dropped(QString); protected: void mousePressEvent(QMouseEvent* event) override { emit clicked(event->globalPos()); } void mouseDoubleClickEvent(QMouseEvent* event) override { emit doubleClicked(event->globalPos()); } + void dragEnterEvent(QDragEnterEvent* event) override + { + if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) { + event->setDropAction(Qt::CopyAction); + event->acceptProposedAction(); + } + else event->ignore(); + } + void dragMoveEvent(QDragMoveEvent* event) override + { + if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) { + event->setDropAction(Qt::CopyAction); + event->acceptProposedAction(); + } + else event->ignore(); + } + void dropEvent(QDropEvent* event) override + { + if (event->dropAction() == Qt::CopyAction) + { + emit dropped(event->mimeData()->urls()[0].toLocalFile()); + } + else event->ignore(); + } }; class MachineStatus : public QObject diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 3058ca59f..fff1cde05 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -26,6 +26,7 @@ #include "qt_specifydimensions.h" #include "qt_soundgain.hpp" #include "qt_progsettings.hpp" +#include "qt_mcadevicelist.hpp" #include "qt_rendererstack.hpp" #include "qt_renderercommon.hpp" @@ -37,6 +38,7 @@ extern "C" { #include <86box/plat.h> #include <86box/discord.h> #include <86box/video.h> +#include <86box/machine.h> #include <86box/vid_ega.h> #include <86box/version.h> @@ -207,12 +209,12 @@ MainWindow::MainWindow(QWidget *parent) : qt_mouse_capture(mouse_capture); if (mouse_capture) { this->grabKeyboard(); - if (ui->stackedWidget->mouse_capture) - ui->stackedWidget->mouse_capture(this->windowHandle()); + if (ui->stackedWidget->mouse_capture_func) + ui->stackedWidget->mouse_capture_func(this->windowHandle()); } else { this->releaseKeyboard(); - if (ui->stackedWidget->mouse_uncapture) - ui->stackedWidget->mouse_uncapture(); + if (ui->stackedWidget->mouse_uncapture_func) + ui->stackedWidget->mouse_uncapture_func(); } }); @@ -276,9 +278,15 @@ MainWindow::MainWindow(QWidget *parent) : vid_api = 0; ui->actionHardware_Renderer_OpenGL->setVisible(false); ui->actionHardware_Renderer_OpenGL_ES->setVisible(false); + ui->actionVulkan->setVisible(false); ui->actionOpenGL_3_0_Core->setVisible(false); } +#if !QT_CONFIG(vulkan) + if (vid_api == 4) vid_api = 0; + ui->actionVulkan->setVisible(false); +#endif + QActionGroup* actGroup = nullptr; actGroup = new QActionGroup(this); @@ -286,6 +294,7 @@ MainWindow::MainWindow(QWidget *parent) : actGroup->addAction(ui->actionHardware_Renderer_OpenGL); actGroup->addAction(ui->actionHardware_Renderer_OpenGL_ES); actGroup->addAction(ui->actionOpenGL_3_0_Core); + actGroup->addAction(ui->actionVulkan); actGroup->setExclusive(true); connect(actGroup, &QActionGroup::triggered, [this](QAction* action) { @@ -304,6 +313,9 @@ MainWindow::MainWindow(QWidget *parent) : case 3: ui->stackedWidget->switchRenderer(RendererStack::Renderer::OpenGL3); break; + case 4: + ui->stackedWidget->switchRenderer(RendererStack::Renderer::Vulkan); + break; } }); @@ -509,8 +521,12 @@ void MainWindow::closeEvent(QCloseEvent *event) { } qt_nvr_save(); config_save(); - if (ui->stackedWidget->mouse_exit) - ui->stackedWidget->mouse_exit(); + + if (ui->stackedWidget->mouse_exit_func) + ui->stackedWidget->mouse_exit_func(); + + ui->stackedWidget->switchRenderer(RendererStack::Renderer::Software); + QApplication::processEvents(); event->accept(); } @@ -1420,6 +1436,7 @@ bool MainWindow::eventFilter(QObject* receiver, QEvent* event) void MainWindow::refreshMediaMenu() { mm->refresh(ui->menuMedia); status->refresh(ui->statusbar); + ui->actionMCA_devices->setVisible(machine_has_bus(machine, MACHINE_BUS_MCA)); } void MainWindow::showMessage(const QString& header, const QString& message) { @@ -1440,6 +1457,10 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { if (send_keyboard_input && !(kbd_req_capture && !mouse_capture && !video_fullscreen)) { + // Windows keys in Qt have one-to-one mapping. + if (event->key() == Qt::Key_Super_L || event->key() == Qt::Key_Super_R) { + keyboard_input(1, event->key() == Qt::Key_Super_L ? 0x15B : 0x15C); + } else #ifdef Q_OS_MACOS processMacKeyboardInput(true, event); #else @@ -1467,6 +1488,9 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) if (!send_keyboard_input) return; + if (event->key() == Qt::Key_Super_L || event->key() == Qt::Key_Super_R) { + keyboard_input(0, event->key() == Qt::Key_Super_L ? 0x15B : 0x15C); + } else #ifdef Q_OS_MACOS processMacKeyboardInput(false, event); #else @@ -1854,3 +1878,12 @@ void MainWindow::on_actionRenderer_options_triggered() if (dlg) dlg->exec(); } + +void MainWindow::on_actionMCA_devices_triggered() +{ + auto dlg = new MCADeviceList(this); + + if (dlg) + dlg->exec(); +} + diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 84d08f8ed..ffecdd773 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -103,6 +103,8 @@ private slots: void showMessage_(const QString& header, const QString& message); void getTitle_(wchar_t* title); + void on_actionMCA_devices_triggered(); + protected: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 60b77d290..b26e11f55 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -54,7 +54,7 @@ 0 0 724 - 22 + 23 @@ -89,6 +89,8 @@ + + @@ -102,6 +104,7 @@ + @@ -726,6 +729,22 @@ Renderer options... + + + true + + + &Vulkan + + + 4 + + + + + MCA devices... + + diff --git a/src/qt/qt_mcadevicelist.cpp b/src/qt/qt_mcadevicelist.cpp new file mode 100644 index 000000000..f1a39e358 --- /dev/null +++ b/src/qt/qt_mcadevicelist.cpp @@ -0,0 +1,42 @@ +#include "qt_mcadevicelist.hpp" +#include "ui_qt_mcadevicelist.h" + +extern "C" +{ +#include <86box/86box.h> +#include <86box/video.h> +#include <86box/mca.h> +#include <86box/plat.h> +} + +MCADeviceList::MCADeviceList(QWidget *parent) : + QDialog(parent), + ui(new Ui::MCADeviceList) +{ + ui->setupUi(this); + + startblit(); + if (mca_get_nr_cards() == 0) + { + ui->listWidget->addItem(QObject::tr("No MCA devices.")); + ui->listWidget->setDisabled(true); + } + else + { + for (int i = 0; i < mca_get_nr_cards(); i++) + { + uint32_t deviceId = (mca_read_index(0x00, i) | (mca_read_index(0x01, i) << 8)); + if (deviceId != 0xFFFF) + { + QString hexRepresentation = QString::number(deviceId, 16).toUpper(); + ui->listWidget->addItem(QString("Slot %1: 0x%2 (@%3.ADF)").arg(i + 1).arg(hexRepresentation, hexRepresentation)); + } + } + } + endblit(); +} + +MCADeviceList::~MCADeviceList() +{ + delete ui; +} diff --git a/src/qt/qt_mcadevicelist.hpp b/src/qt/qt_mcadevicelist.hpp new file mode 100644 index 000000000..e45992519 --- /dev/null +++ b/src/qt/qt_mcadevicelist.hpp @@ -0,0 +1,22 @@ +#ifndef QT_MCADEVICELIST_HPP +#define QT_MCADEVICELIST_HPP + +#include + +namespace Ui { +class MCADeviceList; +} + +class MCADeviceList : public QDialog +{ + Q_OBJECT + +public: + explicit MCADeviceList(QWidget *parent = nullptr); + ~MCADeviceList(); + +private: + Ui::MCADeviceList *ui; +}; + +#endif // QT_MCADEVICELIST_HPP diff --git a/src/qt/qt_mcadevicelist.ui b/src/qt/qt_mcadevicelist.ui new file mode 100644 index 000000000..2efac1886 --- /dev/null +++ b/src/qt/qt_mcadevicelist.ui @@ -0,0 +1,74 @@ + + + MCADeviceList + + + + 0 + 0 + 400 + 300 + + + + MCA devices + + + + + + List of MCA devices: + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + MCADeviceList + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + MCADeviceList + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/qt/qt_mediamenu.cpp b/src/qt/qt_mediamenu.cpp index 5b0dffe47..e6d4bc859 100644 --- a/src/qt/qt_mediamenu.cpp +++ b/src/qt/qt_mediamenu.cpp @@ -232,6 +232,18 @@ void MediaMenu::cassetteUpdateMenu() { cassetteMenu->setTitle(QString::asprintf(tr("Cassette: %s").toUtf8().constData(), (name.isEmpty() ? tr("(empty)") : name).toUtf8().constData())); } +void MediaMenu::cartridgeMount(int i, const QString &filename) +{ + cart_close(i); + QByteArray filenameBytes = filename.toUtf8(); + cart_load(i, filenameBytes.data()); + + ui_sb_update_icon_state(SB_CARTRIDGE | i, filename.isEmpty() ? 1 : 0); + cartridgeUpdateMenu(i); + ui_sb_update_tip(SB_CARTRIDGE | i); + config_save(); +} + void MediaMenu::cartridgeSelectImage(int i) { auto filename = QFileDialog::getOpenFileName( parentWidget, @@ -245,14 +257,7 @@ void MediaMenu::cartridgeSelectImage(int i) { if (filename.isEmpty()) { return; } - cart_close(i); - QByteArray filenameBytes = filename.toUtf8(); - cart_load(i, filenameBytes.data()); - - ui_sb_update_icon_state(SB_CARTRIDGE | i, filename.isEmpty() ? 1 : 0); - cartridgeUpdateMenu(i); - ui_sb_update_tip(SB_CARTRIDGE | i); - config_save(); + cartridgeMount(i, filename); } void MediaMenu::cartridgeEject(int i) { @@ -363,27 +368,8 @@ void MediaMenu::cdromMute(int i) { sound_cd_thread_reset(); } -void MediaMenu::cdromMount(int i, int dir) { - QString filename; - QFileInfo fi(cdrom[i].image_path); - - if (dir) { - filename = QFileDialog::getExistingDirectory( - parentWidget); - } else { - filename = QFileDialog::getOpenFileName( - parentWidget, - QString(), - QString(), - tr("CD-ROM images") % - util::DlgFilter({ "iso","cue" }) % - tr("All files") % - util::DlgFilter({ "*" }, true)); - } - - if (filename.isEmpty()) { - return; - } +void MediaMenu::cdromMount(int i, const QString &filename) +{ QByteArray fn = filename.toUtf8().data(); cdrom[i].prev_host_drive = cdrom[i].host_drive; @@ -408,6 +394,31 @@ void MediaMenu::cdromMount(int i, int dir) { config_save(); } +void MediaMenu::cdromMount(int i, int dir) { + QString filename; + QFileInfo fi(cdrom[i].image_path); + + if (dir) { + filename = QFileDialog::getExistingDirectory( + parentWidget); + } else { + filename = QFileDialog::getOpenFileName( + parentWidget, + QString(), + QString(), + tr("CD-ROM images") % + util::DlgFilter({ "iso","cue" }) % + tr("All files") % + util::DlgFilter({ "*" }, true)); + } + + if (filename.isEmpty()) { + return; + } + + cdromMount(i, filename); +} + void MediaMenu::cdromEject(int i) { cdrom_eject(i); cdromUpdateMenu(i); diff --git a/src/qt/qt_mediamenu.hpp b/src/qt/qt_mediamenu.hpp index 0ff8f7214..85275608a 100644 --- a/src/qt/qt_mediamenu.hpp +++ b/src/qt/qt_mediamenu.hpp @@ -25,6 +25,7 @@ public: void cassetteUpdateMenu(); void cartridgeSelectImage(int i); + void cartridgeMount(int i, const QString& filename); void cartridgeEject(int i); void cartridgeUpdateMenu(int i); @@ -37,6 +38,7 @@ public: void cdromMute(int i); void cdromMount(int i, int dir); + void cdromMount(int i, const QString& filename); void cdromEject(int i); void cdromReload(int i); void cdromUpdateMenu(int i); diff --git a/src/qt/qt_platform.cpp b/src/qt/qt_platform.cpp index 2c2f1ea87..35612ca72 100644 --- a/src/qt/qt_platform.cpp +++ b/src/qt/qt_platform.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -348,7 +349,7 @@ plat_pause(int p) if (p) { wcsncpy(oldtitle, ui_window_title(NULL), sizeof_w(oldtitle) - 1); wcscpy(title, oldtitle); - QObject::tr(" - PAUSED").toWCharArray(paused_msg); + paused_msg[QObject::tr(" - PAUSED").toWCharArray(paused_msg)] = 0; wcscat(title, paused_msg); ui_window_title(title); } else { @@ -378,7 +379,7 @@ plat_power_off(void) cycles -= 99999999; cpu_thread_run = 0; - main_window->close(); + QTimer::singleShot(0, main_window, &QMainWindow::close); } void set_language(uint32_t id) { diff --git a/src/qt/qt_progsettings.cpp b/src/qt/qt_progsettings.cpp index d5bfdaa2f..803fddc24 100644 --- a/src/qt/qt_progsettings.cpp +++ b/src/qt/qt_progsettings.cpp @@ -110,6 +110,9 @@ ProgSettings::ProgSettings(QWidget *parent) : ui->comboBoxLanguage->setCurrentIndex(ui->comboBoxLanguage->findData(i.key())); } } + + mouseSensitivity = mouse_sensitivity; + ui->horizontalSlider->setValue(mouseSensitivity * 100.); } void ProgSettings::accept() @@ -132,6 +135,7 @@ void ProgSettings::accept() connect(main_window, &MainWindow::updateStatusBarActivity, main_window->status.get(), &MachineStatus::setActivity); connect(main_window, &MainWindow::updateStatusBarEmpty, main_window->status.get(), &MachineStatus::setEmpty); connect(main_window, &MainWindow::statusBarMessage, main_window->status.get(), &MachineStatus::message, Qt::QueuedConnection); + mouse_sensitivity = mouseSensitivity; QDialog::accept(); } @@ -193,3 +197,16 @@ void ProgSettings::on_pushButtonLanguage_released() { ui->comboBoxLanguage->setCurrentIndex(0); } + +void ProgSettings::on_horizontalSlider_valueChanged(int value) +{ + mouseSensitivity = (double)value / 100.; +} + + +void ProgSettings::on_pushButton_2_clicked() +{ + mouseSensitivity = 1.0; + ui->horizontalSlider->setValue(100); +} + diff --git a/src/qt/qt_progsettings.hpp b/src/qt/qt_progsettings.hpp index 75fba149a..07cf62051 100644 --- a/src/qt/qt_progsettings.hpp +++ b/src/qt/qt_progsettings.hpp @@ -57,10 +57,15 @@ private slots: void on_pushButton_released(); void on_pushButtonLanguage_released(); + void on_horizontalSlider_valueChanged(int value); + + void on_pushButton_2_clicked(); + private: Ui::ProgSettings *ui; friend class MainWindow; + double mouseSensitivity; }; #endif // QT_PROGSETTINGS_HPP diff --git a/src/qt/qt_progsettings.ui b/src/qt/qt_progsettings.ui index b64272f8b..11bb39b74 100644 --- a/src/qt/qt_progsettings.ui +++ b/src/qt/qt_progsettings.ui @@ -6,35 +6,38 @@ 0 0 - 370 - 228 + 458 + 303 - 370 - 228 + 0 + 0 - 370 - 228 + 16777215 + 16777215 Preferences - - + + QLayout::SetFixedSize + + + - Qt::Vertical + Qt::Horizontal - 20 - 40 + 40 + 20 @@ -48,6 +51,13 @@ + + + + Mouse sensitivity: + + + @@ -55,26 +65,7 @@ - - - - Language: - - - - - - - false - - - - (Default) - - - - - + Qt::Horizontal @@ -84,6 +75,47 @@ + + + + Default + + + + + + + Icon set: + + + + + + + Default + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Language: + + + @@ -97,32 +129,39 @@ - - - - Icon set: + + + + 50 - - - - - - Default + + 200 + + + 10 + + + 20 + + + 100 - - - - Qt::Horizontal - - - 40 - 20 - + + + + + + false - + + + (Default) + + + diff --git a/src/qt/qt_renderercommon.hpp b/src/qt/qt_renderercommon.hpp index 1f94781e4..a966b730f 100644 --- a/src/qt/qt_renderercommon.hpp +++ b/src/qt/qt_renderercommon.hpp @@ -20,6 +20,8 @@ public: void onResize(int width, int height); virtual void finalize() { } + virtual uint32_t getBytesPerRow() { return 2048 * 4; } + virtual std::vector> getBuffers() = 0; /* Does renderer implement options dialog */ diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp index 3e20f1f40..576e675b5 100644 --- a/src/qt/qt_rendererstack.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -24,13 +24,17 @@ #include "qt_hardwarerenderer.hpp" #include "qt_openglrenderer.hpp" #include "qt_softwarerenderer.hpp" +#include "qt_vulkanwindowrenderer.hpp" #include "qt_mainwindow.hpp" #include "qt_util.hpp" #include "evdev_mouse.hpp" +#include + #include +#include #ifdef __APPLE__ # include @@ -40,8 +44,11 @@ extern "C" { #include <86box/mouse.h> #include <86box/plat.h> #include <86box/video.h> + +double mouse_sensitivity = 1.0; } +extern "C" void macos_poll_mouse(); extern MainWindow *main_window; RendererStack::RendererStack(QWidget *parent) : QStackedWidget(parent) @@ -54,10 +61,8 @@ RendererStack::RendererStack(QWidget *parent) if (!mouse_type || (mouse_type[0] == '\0') || !stricmp(mouse_type, "auto")) { if (QApplication::platformName().contains("wayland")) strcpy(auto_mouse_type, "wayland"); - else if (QApplication::platformName() == "eglfs") + else if (QApplication::platformName() == "eglfs" || QApplication::platformName() == "xcb") strcpy(auto_mouse_type, "evdev"); - else if (QApplication::platformName() == "xcb") - strcpy(auto_mouse_type, "xinput2"); else auto_mouse_type[0] = '\0'; mouse_type = auto_mouse_type; @@ -65,30 +70,22 @@ RendererStack::RendererStack(QWidget *parent) # ifdef WAYLAND if (!stricmp(mouse_type, "wayland")) { - this->mouse_init = wl_init; - this->mouse_poll = wl_mouse_poll; - this->mouse_capture = wl_mouse_capture; - this->mouse_uncapture = wl_mouse_uncapture; - } else + wl_init(); + this->mouse_poll_func = wl_mouse_poll; + this->mouse_capture_func = wl_mouse_capture; + this->mouse_uncapture_func = wl_mouse_uncapture; + } # endif # ifdef EVDEV_INPUT if (!stricmp(mouse_type, "evdev")) { - this->mouse_init = evdev_init; - this->mouse_poll = evdev_mouse_poll; - } else -# endif - if (!stricmp(mouse_type, "xinput2")) { - extern void xinput2_init(); - extern void xinput2_poll(); - extern void xinput2_exit(); - this->mouse_init = xinput2_init; - this->mouse_poll = xinput2_poll; - this->mouse_exit = xinput2_exit; + evdev_init(); + this->mouse_poll_func = evdev_mouse_poll; } +# endif +#endif +#ifdef __APPLE__ + this->mouse_poll_func = macos_poll_mouse; #endif - - if (this->mouse_init) - this->mouse_init(); } RendererStack::~RendererStack() @@ -96,7 +93,6 @@ RendererStack::~RendererStack() delete ui; } -extern "C" void macos_poll_mouse(); void qt_mouse_capture(int on) { @@ -119,18 +115,19 @@ qt_mouse_capture(int on) void RendererStack::mousePoll() { -#ifdef __APPLE__ - return macos_poll_mouse(); -#else /* !defined __APPLE__ */ +#ifndef __APPLE__ mouse_x = mousedata.deltax; mouse_y = mousedata.deltay; mouse_z = mousedata.deltaz; mousedata.deltax = mousedata.deltay = mousedata.deltaz = 0; - mouse_buttons = mousedata.mousebuttons; + mouse_buttons = mousedata.mousebuttons; - if (this->mouse_poll) - this->mouse_poll(); -#endif /* !defined __APPLE__ */ + if (this->mouse_poll_func) +#endif + this->mouse_poll_func(); + + mouse_x *= mouse_sensitivity; + mouse_y *= mouse_sensitivity; } int ignoreNextMouseEvent = 1; @@ -243,6 +240,7 @@ void RendererStack::createRenderer(Renderer renderer) { switch (renderer) { + default: case Renderer::Software: { auto sw = new SoftwareRenderer(this); @@ -280,13 +278,13 @@ RendererStack::createRenderer(Renderer renderer) rendererWindow = hw; connect(this, &RendererStack::blitToRenderer, hw, &OpenGLRenderer::onBlit, Qt::QueuedConnection); connect(hw, &OpenGLRenderer::initialized, [=]() { - /* Buffers are awailable only after initialization. */ + /* Buffers are available only after initialization. */ imagebufs = rendererWindow->getBuffers(); endblit(); emit rendererChanged(); }); connect(hw, &OpenGLRenderer::errorInitializing, [=]() { - /* Renderer could initialize, fallback to software. */ + /* Renderer not could initialize, fallback to software. */ imagebufs = {}; endblit(); QTimer::singleShot(0, this, [this]() { switchRenderer(Renderer::Software); }); @@ -294,8 +292,46 @@ RendererStack::createRenderer(Renderer renderer) current.reset(this->createWindowContainer(hw, this)); break; } +#if QT_CONFIG(vulkan) + case Renderer::Vulkan: + { + this->createWinId(); + VulkanWindowRenderer *hw = nullptr; + try { + hw = new VulkanWindowRenderer(this); + } catch(std::runtime_error& e) { + auto msgBox = new QMessageBox(QMessageBox::Critical, "86Box", e.what() + QString("\nFalling back to software rendering."), QMessageBox::Ok); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->show(); + imagebufs = {}; + endblit(); + QTimer::singleShot(0, this, [this]() { switchRenderer(Renderer::Software); }); + current.reset(nullptr); + break; + }; + rendererWindow = hw; + connect(this, &RendererStack::blitToRenderer, hw, &VulkanWindowRenderer::onBlit, Qt::QueuedConnection); + connect(hw, &VulkanWindowRenderer::rendererInitialized, [=]() { + /* Buffers are available only after initialization. */ + imagebufs = rendererWindow->getBuffers(); + endblit(); + emit rendererChanged(); + }); + connect(hw, &VulkanWindowRenderer::errorInitializing, [=]() { + /* Renderer could not initialize, fallback to software. */ + auto msgBox = new QMessageBox(QMessageBox::Critical, "86Box", QString("Failed to initialize Vulkan renderer.\nFalling back to software rendering."), QMessageBox::Ok); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->show(); + imagebufs = {}; + endblit(); + QTimer::singleShot(0, this, [this]() { switchRenderer(Renderer::Software); }); + }); + current.reset(this->createWindowContainer(hw, this)); + break; + } +#endif } - + if (current.get() == nullptr) return; current->setFocusPolicy(Qt::NoFocus); current->setFocusProxy(this); addWidget(current.get()); @@ -304,7 +340,7 @@ RendererStack::createRenderer(Renderer renderer) currentBuf = 0; - if (renderer != Renderer::OpenGL3) { + if (renderer != Renderer::OpenGL3 && renderer != Renderer::Vulkan) { imagebufs = rendererWindow->getBuffers(); endblit(); emit rendererChanged(); @@ -315,7 +351,7 @@ RendererStack::createRenderer(Renderer renderer) void RendererStack::blit(int x, int y, int w, int h) { - if ((w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || imagebufs.empty() || std::get(imagebufs[currentBuf])->test_and_set()) { + if ((x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || imagebufs.empty() || std::get(imagebufs[currentBuf])->test_and_set()) { video_blit_complete(); return; } @@ -325,7 +361,7 @@ RendererStack::blit(int x, int y, int w, int h) sh = this->h = h; uint8_t *imagebits = std::get(imagebufs[currentBuf]); for (int y1 = y; y1 < (y + h); y1++) { - auto scanline = imagebits + (y1 * (2048) * 4) + (x * 4); + auto scanline = imagebits + (y1 * rendererWindow->getBytesPerRow()) + (x * 4); video_copy(scanline, &(buffer32->line[y1][x]), w * 4); } diff --git a/src/qt/qt_rendererstack.hpp b/src/qt/qt_rendererstack.hpp index 89f300ada..45daf838d 100644 --- a/src/qt/qt_rendererstack.hpp +++ b/src/qt/qt_rendererstack.hpp @@ -44,7 +44,8 @@ public: Software, OpenGL, OpenGLES, - OpenGL3 + OpenGL3, + Vulkan }; void switchRenderer(Renderer renderer); @@ -64,11 +65,10 @@ public: rendererWindow->onResize(width, height); } - void (*mouse_init)() = nullptr; - void (*mouse_poll)() = nullptr; - void (*mouse_capture)(QWindow *window) = nullptr; - void (*mouse_uncapture)() = nullptr; - void (*mouse_exit)() = nullptr; + void (*mouse_poll_func)() = nullptr; + void (*mouse_capture_func)(QWindow *window) = nullptr; + void (*mouse_uncapture_func)() = nullptr; + void (*mouse_exit_func)() = nullptr; signals: void blitToRenderer(int buf_idx, int x, int y, int w, int h); diff --git a/src/qt/qt_settings.cpp b/src/qt/qt_settings.cpp index 25493ae2d..ea4a86373 100644 --- a/src/qt/qt_settings.cpp +++ b/src/qt/qt_settings.cpp @@ -43,6 +43,8 @@ extern "C" #include #include #include +#include +#include class SettingsModel : public QAbstractListModel { public: @@ -103,7 +105,6 @@ Settings::Settings(QWidget *parent) : ui(new Ui::Settings) { ui->setupUi(this); - ui->listView->setModel(new SettingsModel(this)); Harddrives::busTrackClass = new SettingsBusTracking; @@ -131,8 +132,6 @@ Settings::Settings(QWidget *parent) : ui->stackedWidget->addWidget(otherRemovable); ui->stackedWidget->addWidget(otherPeripherals); - ui->listView->setFixedWidth(ui->listView->sizeHintForColumn(0) + 5); - connect(machine, &SettingsMachine::currentMachineChanged, display, &SettingsDisplay::onCurrentMachineChanged); connect(machine, &SettingsMachine::currentMachineChanged, input, &SettingsInput::onCurrentMachineChanged); connect(machine, &SettingsMachine::currentMachineChanged, sound, &SettingsSound::onCurrentMachineChanged); @@ -144,6 +143,8 @@ Settings::Settings(QWidget *parent) : ui->stackedWidget->setCurrentIndex(current.row()); }); + ui->listView->setMaximumWidth(ui->listView->sizeHintForColumn(0) + qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent)); + Settings::settings = this; } diff --git a/src/qt/qt_settings.ui b/src/qt/qt_settings.ui index 66047cffd..ec3198ebe 100644 --- a/src/qt/qt_settings.ui +++ b/src/qt/qt_settings.ui @@ -30,7 +30,11 @@ - + + + QListView::ListMode + + diff --git a/src/qt/qt_settingsdisplay.cpp b/src/qt/qt_settingsdisplay.cpp index 394033a47..84963e833 100644 --- a/src/qt/qt_settingsdisplay.cpp +++ b/src/qt/qt_settingsdisplay.cpp @@ -46,6 +46,7 @@ SettingsDisplay::~SettingsDisplay() void SettingsDisplay::save() { gfxcard = ui->comboBoxVideo->currentData().toInt(); voodoo_enabled = ui->checkBoxVoodoo->isChecked() ? 1 : 0; + ibm8514_enabled = ui->checkBox8514->isChecked() ? 1 : 0; } void SettingsDisplay::onCurrentMachineChanged(int machineId) { @@ -113,6 +114,13 @@ void SettingsDisplay::on_comboBoxVideo_currentIndexChanged(int index) { ui->checkBoxVoodoo->setChecked(voodoo_enabled); } ui->pushButtonConfigureVoodoo->setEnabled(machineHasPci && ui->checkBoxVoodoo->isChecked()); + + bool hasIsa16 = machine_has_bus(machineId, MACHINE_BUS_ISA16) > 0; + bool has_MCA = machine_has_bus(machineId, MACHINE_BUS_MCA) > 0; + ui->checkBox8514->setEnabled(hasIsa16 || has_MCA); + if (hasIsa16 || has_MCA) { + ui->checkBox8514->setChecked(ibm8514_enabled > 0); + } } void SettingsDisplay::on_checkBoxVoodoo_stateChanged(int state) { diff --git a/src/qt/qt_settingsdisplay.ui b/src/qt/qt_settingsdisplay.ui index 2ed153a21..275af9fd3 100644 --- a/src/qt/qt_settingsdisplay.ui +++ b/src/qt/qt_settingsdisplay.ui @@ -63,7 +63,14 @@ - + + + + 8514/A + + + + Qt::Vertical diff --git a/src/qt/qt_settingssound.cpp b/src/qt/qt_settingssound.cpp index 6b12023a6..56391569a 100644 --- a/src/qt/qt_settingssound.cpp +++ b/src/qt/qt_settingssound.cpp @@ -144,7 +144,7 @@ void SettingsSound::onCurrentMachineChanged(int machineId) { ui->checkBoxFloat32->setChecked(sound_is_float > 0); bool hasIsa = machine_has_bus(machineId, MACHINE_BUS_ISA) > 0; - bool hasIsa16 = machine_has_bus(machineId, MACHINE_BUS_ISA) > 0; + bool hasIsa16 = machine_has_bus(machineId, MACHINE_BUS_ISA16) > 0; ui->checkBoxCMS->setEnabled(hasIsa); ui->pushButtonConfigureCMS->setEnabled((GAMEBLASTER > 0) && hasIsa); ui->checkBoxGUS->setEnabled(hasIsa16); diff --git a/src/qt/qt_vulkanrenderer.cpp b/src/qt/qt_vulkanrenderer.cpp new file mode 100644 index 000000000..c62154669 --- /dev/null +++ b/src/qt/qt_vulkanrenderer.cpp @@ -0,0 +1,1007 @@ +/**************************************************************************** +** +** Copyright (C) 2022 Cacodemon345 +** Copyright (C) 2017 The Qt Company Ltd. +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include +#include "qt_vulkanrenderer.hpp" +#if QT_CONFIG(vulkan) +#include + +extern "C" +{ +#include <86box/86box.h> +} + +// Use a triangle strip to get a quad. +// +// Note that the vertex data and the projection matrix assume OpenGL. With +// Vulkan Y is negated in clip space and the near/far plane is at 0/1 instead +// of -1/1. These will be corrected for by an extra transformation when +// calculating the modelview-projection matrix. +static float vertexData[] = { // Y up, front = CW + // x, y, z, u, v + -1, -1, 0, 0, 1, + -1, 1, 0, 0, 0, + 1, -1, 0, 1, 1, + 1, 1, 0, 1, 0 +}; + +static const int UNIFORM_DATA_SIZE = 16 * sizeof(float); + +static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) +{ + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +VulkanRenderer2::VulkanRenderer2(QVulkanWindow *w) + : m_window(w) +{ +} + +VkShaderModule VulkanRenderer2::createShader(const QString &name) +{ + QFile file(name); + if (!file.open(QIODevice::ReadOnly)) { + qWarning("Failed to read shader %s", qPrintable(name)); + return VK_NULL_HANDLE; + } + QByteArray blob = file.readAll(); + file.close(); + + VkShaderModuleCreateInfo shaderInfo; + memset(&shaderInfo, 0, sizeof(shaderInfo)); + shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shaderInfo.codeSize = blob.size(); + shaderInfo.pCode = reinterpret_cast(blob.constData()); + VkShaderModule shaderModule; + VkResult err = m_devFuncs->vkCreateShaderModule(m_window->device(), &shaderInfo, nullptr, &shaderModule); + if (err != VK_SUCCESS) { + qWarning("Failed to create shader module: %d", err); + return VK_NULL_HANDLE; + } + + return shaderModule; +} + +bool VulkanRenderer2::createTexture() +{ + QImage img(2048, 2048, QImage::Format_RGBA8888_Premultiplied); + img.fill(QColor(0, 0, 0)); + + QVulkanFunctions *f = m_window->vulkanInstance()->functions(); + VkDevice dev = m_window->device(); + + m_texFormat = VK_FORMAT_B8G8R8A8_UNORM; + + // Now we can either map and copy the image data directly, or have to go + // through a staging buffer to copy and convert into the internal optimal + // tiling format. + VkFormatProperties props; + f->vkGetPhysicalDeviceFormatProperties(m_window->physicalDevice(), m_texFormat, &props); + const bool canSampleLinear = (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); + const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); + if (!canSampleLinear && !canSampleOptimal) { + qWarning("Neither linear nor optimal image sampling is supported for RGBA8"); + return false; + } + + static bool alwaysStage = qEnvironmentVariableIntValue("QT_VK_FORCE_STAGE_TEX"); + + if (canSampleLinear && !alwaysStage) { + if (!createTextureImage(img.size(), &m_texImage, &m_texMem, + VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT, + m_window->hostVisibleMemoryIndex())) + return false; + + if (!writeLinearImage(img, m_texImage, m_texMem)) + return false; + + m_texLayoutPending = true; + } else { + if (!createTextureImage(img.size(), &m_texStaging, &m_texStagingMem, + VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + m_window->hostVisibleMemoryIndex())) + return false; + + if (!createTextureImage(img.size(), &m_texImage, &m_texMem, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + m_window->deviceLocalMemoryIndex())) + return false; + + if (!writeLinearImage(img, m_texStaging, m_texStagingMem)) + return false; + + m_texStagingPending = true; + } + + VkImageViewCreateInfo viewInfo; + memset(&viewInfo, 0, sizeof(viewInfo)); + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = m_texImage; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = m_texFormat; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.levelCount = viewInfo.subresourceRange.layerCount = 1; + + VkResult err = m_devFuncs->vkCreateImageView(dev, &viewInfo, nullptr, &m_texView); + if (err != VK_SUCCESS) { + qWarning("Failed to create image view for texture: %d", err); + return false; + } + + m_texSize = img.size(); + + return true; +} + +bool VulkanRenderer2::createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem, + VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex) +{ + VkDevice dev = m_window->device(); + + VkImageCreateInfo imageInfo; + memset(&imageInfo, 0, sizeof(imageInfo)); + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.format = m_texFormat; + imageInfo.extent.width = size.width(); + imageInfo.extent.height = size.height(); + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.tiling = tiling; + imageInfo.usage = usage; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + VkResult err = m_devFuncs->vkCreateImage(dev, &imageInfo, nullptr, image); + if (err != VK_SUCCESS) { + qWarning("Failed to create linear image for texture: %d", err); + return false; + } + + VkMemoryRequirements memReq; + m_devFuncs->vkGetImageMemoryRequirements(dev, *image, &memReq); + + if (!(memReq.memoryTypeBits & (1 << memIndex))) { + VkPhysicalDeviceMemoryProperties physDevMemProps; + m_window->vulkanInstance()->functions()->vkGetPhysicalDeviceMemoryProperties(m_window->physicalDevice(), &physDevMemProps); + for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) { + if (!(memReq.memoryTypeBits & (1 << i))) + continue; + memIndex = i; + } + } + + VkMemoryAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + nullptr, + memReq.size, + memIndex + }; + qDebug("allocating %u bytes for texture image", uint32_t(memReq.size)); + + err = m_devFuncs->vkAllocateMemory(dev, &allocInfo, nullptr, mem); + if (err != VK_SUCCESS) { + qWarning("Failed to allocate memory for linear image: %d", err); + return false; + } + + err = m_devFuncs->vkBindImageMemory(dev, *image, *mem, 0); + if (err != VK_SUCCESS) { + qWarning("Failed to bind linear image memory: %d", err); + return false; + } + + return true; +} + +bool VulkanRenderer2::writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory) +{ + VkDevice dev = m_window->device(); + + VkImageSubresource subres = { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, // mip level + 0 + }; + VkSubresourceLayout layout; + m_devFuncs->vkGetImageSubresourceLayout(dev, image, &subres, &layout); + + uchar *p; + VkResult err = m_devFuncs->vkMapMemory(dev, memory, layout.offset, layout.size, 0, reinterpret_cast(&p)); + if (err != VK_SUCCESS) { + qWarning("Failed to map memory for linear image: %d", err); + return false; + } + + for (int y = 0; y < img.height(); ++y) { + const uchar *line = img.constScanLine(y); + memcpy(p, line, img.width() * 4); + p += layout.rowPitch; + } + + m_devFuncs->vkUnmapMemory(dev, memory); + return true; +} + +void VulkanRenderer2::ensureTexture() +{ + if (!m_texLayoutPending && !m_texStagingPending) + return; + + Q_ASSERT(m_texLayoutPending != m_texStagingPending); + VkCommandBuffer cb = m_window->currentCommandBuffer(); + + VkImageMemoryBarrier barrier; + memset(&barrier, 0, sizeof(barrier)); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1; + + if (m_texLayoutPending) { + m_texLayoutPending = false; + + barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.image = m_texImage; + + m_devFuncs->vkCmdPipelineBarrier(cb, + VK_PIPELINE_STAGE_HOST_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &barrier); + + VkDevice dev = m_window->device(); + + VkImageSubresource subres = { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, // mip level + 0 + }; + VkSubresourceLayout layout; + m_devFuncs->vkGetImageSubresourceLayout(dev, m_texImage, &subres, &layout); + + VkResult err = m_devFuncs->vkMapMemory(dev, m_texMem, layout.offset, layout.size, 0, reinterpret_cast(&mappedPtr)); + if (err != VK_SUCCESS) { + qWarning("Failed to map memory for linear image: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + imagePitch = layout.rowPitch; + + if (qobject_cast(m_window)) { + emit qobject_cast(m_window)->rendererInitialized(); + } + } else { + m_texStagingPending = false; + + if (!m_texStagingTransferLayout) { + barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.image = m_texStaging; + m_devFuncs->vkCmdPipelineBarrier(cb, + VK_PIPELINE_STAGE_HOST_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &barrier); + + barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.image = m_texImage; + m_devFuncs->vkCmdPipelineBarrier(cb, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &barrier); + + VkDevice dev = m_window->device(); + + VkImageSubresource subres = { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, // mip level + 0 + }; + VkSubresourceLayout layout; + m_devFuncs->vkGetImageSubresourceLayout(dev, m_texStaging, &subres, &layout); + + VkResult err = m_devFuncs->vkMapMemory(dev, m_texStagingMem, layout.offset, layout.size, 0, reinterpret_cast(&mappedPtr)); + if (err != VK_SUCCESS) { + qWarning("Failed to map memory for linear image: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + imagePitch = layout.rowPitch; + + if (qobject_cast(m_window)) { + emit qobject_cast(m_window)->rendererInitialized(); + } + + m_texStagingTransferLayout = true; + } + + VkImageCopy copyInfo; + memset(©Info, 0, sizeof(copyInfo)); + copyInfo.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyInfo.srcSubresource.layerCount = 1; + copyInfo.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyInfo.dstSubresource.layerCount = 1; + copyInfo.extent.width = m_texSize.width(); + copyInfo.extent.height = m_texSize.height(); + copyInfo.extent.depth = 1; + m_devFuncs->vkCmdCopyImage(cb, m_texStaging, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + m_texImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Info); + + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.image = m_texImage; + m_devFuncs->vkCmdPipelineBarrier(cb, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &barrier); + } +} + +void VulkanRenderer2::updateSamplers() +{ + static int cur_video_filter_method = -1; + + if (cur_video_filter_method != video_filter_method) { + cur_video_filter_method = video_filter_method; + m_devFuncs->vkDeviceWaitIdle(m_window->device()); + + VkDescriptorImageInfo descImageInfo = { + cur_video_filter_method == 1 ? m_linearSampler : m_sampler, + m_texView, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }; + + for (int i = 0; i < m_window->concurrentFrameCount(); i++) { + VkWriteDescriptorSet descWrite[2]; + memset(descWrite, 0, sizeof(descWrite)); + descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descWrite[0].dstSet = m_descSet[i]; + descWrite[0].dstBinding = 0; + descWrite[0].descriptorCount = 1; + descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descWrite[0].pBufferInfo = &m_uniformBufInfo[i]; + + descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descWrite[1].dstSet = m_descSet[i]; + descWrite[1].dstBinding = 1; + descWrite[1].descriptorCount = 1; + descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descWrite[1].pImageInfo = &descImageInfo; + m_devFuncs->vkUpdateDescriptorSets(m_window->device(), 2, descWrite, 0, nullptr); + } + } +} + +void VulkanRenderer2::initResources() +{ + qDebug("initResources"); + + VkDevice dev = m_window->device(); + m_devFuncs = m_window->vulkanInstance()->deviceFunctions(dev); + + // The setup is similar to hellovulkantriangle. The difference is the + // presence of a second vertex attribute (texcoord), a sampler, and that we + // need blending. + + const int concurrentFrameCount = m_window->concurrentFrameCount(); + const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits; + const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment; + qDebug("uniform buffer offset alignment is %u", (uint) uniAlign); + VkBufferCreateInfo bufInfo; + memset(&bufInfo, 0, sizeof(bufInfo)); + bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + // Our internal layout is vertex, uniform, uniform, ... with each uniform buffer start offset aligned to uniAlign. + const VkDeviceSize vertexAllocSize = aligned(sizeof(vertexData), uniAlign); + const VkDeviceSize uniformAllocSize = aligned(UNIFORM_DATA_SIZE, uniAlign); + bufInfo.size = vertexAllocSize + concurrentFrameCount * uniformAllocSize; + bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + + VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_buf); + if (err != VK_SUCCESS) { + qWarning("Failed to create buffer: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + VkMemoryRequirements memReq; + m_devFuncs->vkGetBufferMemoryRequirements(dev, m_buf, &memReq); + + VkMemoryAllocateInfo memAllocInfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + nullptr, + memReq.size, + m_window->hostVisibleMemoryIndex() + }; + + err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_bufMem); + if (err != VK_SUCCESS) { + qWarning("Failed to allocate memory: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + err = m_devFuncs->vkBindBufferMemory(dev, m_buf, m_bufMem, 0); + if (err != VK_SUCCESS) { + qWarning("Failed to bind buffer memory: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + quint8 *p; + err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, memReq.size, 0, reinterpret_cast(&p)); + if (err != VK_SUCCESS) { + qWarning("Failed to map memory: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + memcpy(p, vertexData, sizeof(vertexData)); + QMatrix4x4 ident; + memset(m_uniformBufInfo, 0, sizeof(m_uniformBufInfo)); + for (int i = 0; i < concurrentFrameCount; ++i) { + const VkDeviceSize offset = vertexAllocSize + i * uniformAllocSize; + memcpy(p + offset, ident.constData(), 16 * sizeof(float)); + m_uniformBufInfo[i].buffer = m_buf; + m_uniformBufInfo[i].offset = offset; + m_uniformBufInfo[i].range = uniformAllocSize; + } + m_devFuncs->vkUnmapMemory(dev, m_bufMem); + + VkVertexInputBindingDescription vertexBindingDesc = { + 0, // binding + 5 * sizeof(float), + VK_VERTEX_INPUT_RATE_VERTEX + }; + VkVertexInputAttributeDescription vertexAttrDesc[] = { + { // position + 0, // location + 0, // binding + VK_FORMAT_R32G32B32_SFLOAT, + 0 + }, + { // texcoord + 1, + 0, + VK_FORMAT_R32G32_SFLOAT, + 3 * sizeof(float) + } + }; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.pNext = nullptr; + vertexInputInfo.flags = 0; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDesc; + vertexInputInfo.vertexAttributeDescriptionCount = 2; + vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc; + + // Sampler. + VkSamplerCreateInfo samplerInfo; + memset(&samplerInfo, 0, sizeof(samplerInfo)); + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_NEAREST; + samplerInfo.minFilter = VK_FILTER_NEAREST; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.maxAnisotropy = 1.0f; + samplerInfo.maxLod = 0.25; + err = m_devFuncs->vkCreateSampler(dev, &samplerInfo, nullptr, &m_sampler); + if (err != VK_SUCCESS) { + qWarning("Failed to create sampler: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + err = m_devFuncs->vkCreateSampler(dev, &samplerInfo, nullptr, &m_linearSampler); + if (err != VK_SUCCESS) { + qWarning("Failed to create sampler: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + // Texture. + if (!createTexture()) { + qWarning("Failed to create texture"); + return emit qobject_cast(m_window)->errorInitializing(); + } + + // Set up descriptor set and its layout. + VkDescriptorPoolSize descPoolSizes[2] = { + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(concurrentFrameCount) }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, uint32_t(concurrentFrameCount) } + }; + VkDescriptorPoolCreateInfo descPoolInfo; + memset(&descPoolInfo, 0, sizeof(descPoolInfo)); + descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descPoolInfo.maxSets = concurrentFrameCount; + descPoolInfo.poolSizeCount = 2; + descPoolInfo.pPoolSizes = descPoolSizes; + err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_descPool); + if (err != VK_SUCCESS) { + qWarning("Failed to create descriptor pool: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + VkDescriptorSetLayoutBinding layoutBinding[2] = + { + { + 0, // binding + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 1, // descriptorCount + VK_SHADER_STAGE_VERTEX_BIT, + nullptr + }, + { + 1, // binding + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1, // descriptorCount + VK_SHADER_STAGE_FRAGMENT_BIT, + nullptr + } + }; + VkDescriptorSetLayoutCreateInfo descLayoutInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + nullptr, + 0, + 2, // bindingCount + layoutBinding + }; + err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout); + if (err != VK_SUCCESS) { + qWarning("Failed to create descriptor set layout: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + for (int i = 0; i < concurrentFrameCount; ++i) { + VkDescriptorSetAllocateInfo descSetAllocInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + nullptr, + m_descPool, + 1, + &m_descSetLayout + }; + err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &m_descSet[i]); + if (err != VK_SUCCESS) { + qWarning("Failed to allocate descriptor set: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + VkWriteDescriptorSet descWrite[2]; + memset(descWrite, 0, sizeof(descWrite)); + descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descWrite[0].dstSet = m_descSet[i]; + descWrite[0].dstBinding = 0; + descWrite[0].descriptorCount = 1; + descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descWrite[0].pBufferInfo = &m_uniformBufInfo[i]; + + VkDescriptorImageInfo descImageInfo = { + video_filter_method == 1 ? m_linearSampler : m_sampler, + m_texView, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }; + + descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descWrite[1].dstSet = m_descSet[i]; + descWrite[1].dstBinding = 1; + descWrite[1].descriptorCount = 1; + descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descWrite[1].pImageInfo = &descImageInfo; + + m_devFuncs->vkUpdateDescriptorSets(dev, 2, descWrite, 0, nullptr); + } + + // Pipeline cache + VkPipelineCacheCreateInfo pipelineCacheInfo; + memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo)); + pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache); + if (err != VK_SUCCESS) { + qWarning("Failed to create pipeline cache: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + // Pipeline layout + VkPipelineLayoutCreateInfo pipelineLayoutInfo; + memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &m_descSetLayout; + err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout); + if (err != VK_SUCCESS) { + qWarning("Failed to create pipeline layout: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + // Shaders +/* +#version 440 + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; + +layout(location = 0) out vec2 v_texcoord; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + v_texcoord = texcoord; + gl_Position = ubuf.mvp * position; +} +*/ + VkShaderModule vertShaderModule = createShader(QStringLiteral(":/texture_vert.spv")); +/* +#version 440 + +layout(location = 0) in vec2 v_texcoord; + +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D tex; + +void main() +{ + fragColor = texture(tex, v_texcoord); +} +*/ + VkShaderModule fragShaderModule = createShader(QStringLiteral(":/texture_frag.spv")); + + // Graphics pipeline + VkGraphicsPipelineCreateInfo pipelineInfo; + memset(&pipelineInfo, 0, sizeof(pipelineInfo)); + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + + VkPipelineShaderStageCreateInfo shaderStages[2] = { + { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + nullptr, + 0, + VK_SHADER_STAGE_VERTEX_BIT, + vertShaderModule, + "main", + nullptr + }, + { + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + nullptr, + 0, + VK_SHADER_STAGE_FRAGMENT_BIT, + fragShaderModule, + "main", + nullptr + } + }; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + + pipelineInfo.pVertexInputState = &vertexInputInfo; + + VkPipelineInputAssemblyStateCreateInfo ia; + memset(&ia, 0, sizeof(ia)); + ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + pipelineInfo.pInputAssemblyState = &ia; + + // The viewport and scissor will be set dynamically via vkCmdSetViewport/Scissor. + // This way the pipeline does not need to be touched when resizing the window. + VkPipelineViewportStateCreateInfo vp; + memset(&vp, 0, sizeof(vp)); + vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + vp.viewportCount = 1; + vp.scissorCount = 1; + pipelineInfo.pViewportState = &vp; + + VkPipelineRasterizationStateCreateInfo rs; + memset(&rs, 0, sizeof(rs)); + rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rs.polygonMode = VK_POLYGON_MODE_FILL; + rs.cullMode = VK_CULL_MODE_BACK_BIT; + rs.frontFace = VK_FRONT_FACE_CLOCKWISE; + rs.lineWidth = 1.0f; + pipelineInfo.pRasterizationState = &rs; + + VkPipelineMultisampleStateCreateInfo ms; + memset(&ms, 0, sizeof(ms)); + ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + pipelineInfo.pMultisampleState = &ms; + + VkPipelineDepthStencilStateCreateInfo ds; + memset(&ds, 0, sizeof(ds)); + ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + ds.depthTestEnable = VK_TRUE; + ds.depthWriteEnable = VK_TRUE; + ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + pipelineInfo.pDepthStencilState = &ds; + + VkPipelineColorBlendStateCreateInfo cb; + memset(&cb, 0, sizeof(cb)); + cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + // assume pre-multiplied alpha, blend, write out all of rgba + VkPipelineColorBlendAttachmentState att; + memset(&att, 0, sizeof(att)); + att.colorWriteMask = 0xF; + att.blendEnable = VK_TRUE; + att.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + att.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + att.colorBlendOp = VK_BLEND_OP_ADD; + att.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + att.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + att.alphaBlendOp = VK_BLEND_OP_ADD; + cb.attachmentCount = 1; + cb.pAttachments = &att; + pipelineInfo.pColorBlendState = &cb; + + VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dyn; + memset(&dyn, 0, sizeof(dyn)); + dyn.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dyn.dynamicStateCount = sizeof(dynEnable) / sizeof(VkDynamicState); + dyn.pDynamicStates = dynEnable; + pipelineInfo.pDynamicState = &dyn; + + pipelineInfo.layout = m_pipelineLayout; + pipelineInfo.renderPass = m_window->defaultRenderPass(); + + err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline); + if (err != VK_SUCCESS) { + qWarning("Failed to create graphics pipeline: %d", err); + return emit qobject_cast(m_window)->errorInitializing(); + } + + if (vertShaderModule) + m_devFuncs->vkDestroyShaderModule(dev, vertShaderModule, nullptr); + if (fragShaderModule) + m_devFuncs->vkDestroyShaderModule(dev, fragShaderModule, nullptr); + + pclog("Vulkan device: %s\n", m_window->physicalDeviceProperties()->deviceName); + pclog("Vulkan API version: %d.%d.%d\n", VK_VERSION_MAJOR(m_window->physicalDeviceProperties()->apiVersion), VK_VERSION_MINOR(m_window->physicalDeviceProperties()->apiVersion), VK_VERSION_PATCH(m_window->physicalDeviceProperties()->apiVersion)); + pclog("Vulkan driver version: %d.%d.%d\n", VK_VERSION_MAJOR(m_window->physicalDeviceProperties()->driverVersion), VK_VERSION_MINOR(m_window->physicalDeviceProperties()->driverVersion), VK_VERSION_PATCH(m_window->physicalDeviceProperties()->driverVersion)); +} + +void VulkanRenderer2::initSwapChainResources() +{ + qDebug("initSwapChainResources"); + + // Projection matrix + m_proj = m_window->clipCorrectionMatrix(); // adjust for Vulkan-OpenGL clip space differences +} + +void VulkanRenderer2::releaseSwapChainResources() +{ + qDebug("releaseSwapChainResources"); +} + +void VulkanRenderer2::releaseResources() +{ + qDebug("releaseResources"); + + VkDevice dev = m_window->device(); + + if (m_sampler) { + m_devFuncs->vkDestroySampler(dev, m_sampler, nullptr); + m_sampler = VK_NULL_HANDLE; + } + + if (m_linearSampler) { + m_devFuncs->vkDestroySampler(dev, m_linearSampler, nullptr); + m_linearSampler = VK_NULL_HANDLE; + } + + if (m_texStaging) { + m_devFuncs->vkDestroyImage(dev, m_texStaging, nullptr); + m_texStaging = VK_NULL_HANDLE; + } + + if (m_texStagingMem) { + m_devFuncs->vkFreeMemory(dev, m_texStagingMem, nullptr); + m_texStagingMem = VK_NULL_HANDLE; + } + + if (m_texView) { + m_devFuncs->vkDestroyImageView(dev, m_texView, nullptr); + m_texView = VK_NULL_HANDLE; + } + + if (m_texImage) { + m_devFuncs->vkDestroyImage(dev, m_texImage, nullptr); + m_texImage = VK_NULL_HANDLE; + } + + if (m_texMem) { + m_devFuncs->vkFreeMemory(dev, m_texMem, nullptr); + m_texMem = VK_NULL_HANDLE; + } + + if (m_pipeline) { + m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr); + m_pipeline = VK_NULL_HANDLE; + } + + if (m_pipelineLayout) { + m_devFuncs->vkDestroyPipelineLayout(dev, m_pipelineLayout, nullptr); + m_pipelineLayout = VK_NULL_HANDLE; + } + + if (m_pipelineCache) { + m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr); + m_pipelineCache = VK_NULL_HANDLE; + } + + if (m_descSetLayout) { + m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_descSetLayout, nullptr); + m_descSetLayout = VK_NULL_HANDLE; + } + + if (m_descPool) { + m_devFuncs->vkDestroyDescriptorPool(dev, m_descPool, nullptr); + m_descPool = VK_NULL_HANDLE; + } + + if (m_buf) { + m_devFuncs->vkDestroyBuffer(dev, m_buf, nullptr); + m_buf = VK_NULL_HANDLE; + } + + if (m_bufMem) { + m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr); + m_bufMem = VK_NULL_HANDLE; + } +} + +void VulkanRenderer2::startNextFrame() +{ + VkDevice dev = m_window->device(); + VkCommandBuffer cb = m_window->currentCommandBuffer(); + const QSize sz = m_window->swapChainImageSize(); + + updateSamplers(); + // Add the necessary barriers and do the host-linear -> device-optimal copy, if not yet done. + ensureTexture(); + + VkClearColorValue clearColor = {{ 0, 0, 0, 1 }}; + VkClearDepthStencilValue clearDS = { 1, 0 }; + VkClearValue clearValues[2]; + memset(clearValues, 0, sizeof(clearValues)); + clearValues[0].color = clearColor; + clearValues[1].depthStencil = clearDS; + + VkRenderPassBeginInfo rpBeginInfo; + memset(&rpBeginInfo, 0, sizeof(rpBeginInfo)); + rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rpBeginInfo.renderPass = m_window->defaultRenderPass(); + rpBeginInfo.framebuffer = m_window->currentFramebuffer(); + rpBeginInfo.renderArea.extent.width = sz.width(); + rpBeginInfo.renderArea.extent.height = sz.height(); + rpBeginInfo.clearValueCount = 2; + rpBeginInfo.pClearValues = clearValues; + VkCommandBuffer cmdBuf = m_window->currentCommandBuffer(); + m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + quint8 *p; + VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem, m_uniformBufInfo[m_window->currentFrame()].offset, + UNIFORM_DATA_SIZE, 0, reinterpret_cast(&p)); + if (err != VK_SUCCESS) + qFatal("Failed to map memory: %d", err); + QMatrix4x4 m = m_proj; + //m.rotate(m_rotation, 0, 0, 1); + memcpy(p, m.constData(), 16 * sizeof(float)); + m_devFuncs->vkUnmapMemory(dev, m_bufMem); + p = nullptr; + + // Second pass for texture coordinates. + err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, + sizeof(vertexData), 0, reinterpret_cast(&p)); + if (err != VK_SUCCESS) + qFatal("Failed to map memory: %d", err); + + float* floatData = (float*)p; + auto source = qobject_cast(m_window)->source; + auto destination = qobject_cast(m_window)->destination; + floatData[3] = (float)source.x() / 2048.f; + floatData[9] = (float)(source.y()) / 2048.f; + floatData[8] = (float)source.x() / 2048.f; + floatData[4] = (float)(source.y() + source.height()) / 2048.f; + floatData[13] = (float)(source.x() + source.width()) / 2048.f; + floatData[19] = (float)(source.y()) / 2048.f; + floatData[18] = (float)(source.x() + source.width()) / 2048.f; + floatData[14] = (float)(source.y() + source.height()) / 2048.f; + + m_devFuncs->vkUnmapMemory(dev, m_bufMem); + + m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); + m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, + &m_descSet[m_window->currentFrame()], 0, nullptr); + VkDeviceSize vbOffset = 0; + m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_buf, &vbOffset); + + VkViewport viewport; + viewport.x = destination.x() * m_window->devicePixelRatio(); + viewport.y = destination.y() * m_window->devicePixelRatio(); + viewport.width = destination.width() * m_window->devicePixelRatio(); + viewport.height = destination.height() * m_window->devicePixelRatio(); + viewport.minDepth = 0; + viewport.maxDepth = 1; + m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport); + + VkRect2D scissor; + scissor.offset.x = viewport.x; + scissor.offset.y = viewport.y; + scissor.extent.width = viewport.width; + scissor.extent.height = viewport.height; + m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor); + + m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0); + + m_devFuncs->vkCmdEndRenderPass(cmdBuf); + + if (m_texStagingTransferLayout) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.image = m_texImage; + m_devFuncs->vkCmdPipelineBarrier(cb, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &barrier); + m_texStagingPending = true; + } + + m_window->frameReady(); + m_window->requestUpdate(); // render continuously, throttled by the presentation rate +} +#endif diff --git a/src/qt/qt_vulkanrenderer.hpp b/src/qt/qt_vulkanrenderer.hpp new file mode 100644 index 000000000..abd4d7a13 --- /dev/null +++ b/src/qt/qt_vulkanrenderer.hpp @@ -0,0 +1,95 @@ +#pragma once +/**************************************************************************** +** +** Copyright (C) 2022 Cacodemon345 +** Copyright (C) 2017 The Qt Company Ltd. +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include + +#if QT_CONFIG(vulkan) +#include "qt_vulkanwindowrenderer.hpp" + +class VulkanRenderer2 : public QVulkanWindowRenderer +{ +public: + void* mappedPtr = nullptr; + size_t imagePitch = 2048 * 4; + VulkanRenderer2(QVulkanWindow *w); + + void initResources() override; + void initSwapChainResources() override; + void releaseSwapChainResources() override; + void releaseResources() override; + + void startNextFrame() override; + +private: + VkShaderModule createShader(const QString &name); + bool createTexture(); + bool createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem, + VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex); + bool writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory); + void ensureTexture(); + void updateSamplers(); + + QVulkanWindow *m_window; + QVulkanDeviceFunctions *m_devFuncs; + + VkDeviceMemory m_bufMem = VK_NULL_HANDLE; + VkBuffer m_buf = VK_NULL_HANDLE; + VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT]; + + VkDescriptorPool m_descPool = VK_NULL_HANDLE; + VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE; + VkDescriptorSet m_descSet[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT]; + + VkPipelineCache m_pipelineCache = VK_NULL_HANDLE; + VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; + VkPipeline m_pipeline = VK_NULL_HANDLE; + + VkSampler m_sampler = VK_NULL_HANDLE; + VkSampler m_linearSampler = VK_NULL_HANDLE; + VkImage m_texImage = VK_NULL_HANDLE; + VkDeviceMemory m_texMem = VK_NULL_HANDLE; + bool m_texLayoutPending = false; + VkImageView m_texView = VK_NULL_HANDLE; + VkImage m_texStaging = VK_NULL_HANDLE; + VkDeviceMemory m_texStagingMem = VK_NULL_HANDLE; + bool m_texStagingPending = false; + bool m_texStagingTransferLayout = false; + QSize m_texSize; + VkFormat m_texFormat; + + QMatrix4x4 m_proj; + float m_rotation = 0.0f; +}; +#endif diff --git a/src/qt/qt_vulkanwindowrenderer.cpp b/src/qt/qt_vulkanwindowrenderer.cpp new file mode 100644 index 000000000..cb3b96e14 --- /dev/null +++ b/src/qt/qt_vulkanwindowrenderer.cpp @@ -0,0 +1,853 @@ +#include "qt_vulkanwindowrenderer.hpp" + +#include +#include + +#if QT_CONFIG(vulkan) +#include +#include +#include +#include + +#include "qt_mainwindow.hpp" +#include "qt_vulkanrenderer.hpp" + +extern "C" +{ +#include <86box/86box.h> +#include <86box/video.h> +} +#if 0 +extern MainWindow* main_window; +/* +#version 450 + +layout(location = 0) out vec2 v_texcoord; + +mat4 mvp = mat4( +vec4(1.0, 0, 0, 0), +vec4(0, 1.0, 0, 0), +vec4(0, 0, 1.0, 0), +vec4(0, 0, 0, 1.0) +); + +// Y coordinate in Vulkan viewport space is flipped as compared to OpenGL. +vec4 vertCoords[4] = vec4[]( +vec4(-1.0, -1.0, 0, 1), +vec4(1.0, -1.0, 0, 1), +vec4(-1.0, 1.0, 0, 1), +vec4(1.0, 1.0, 0, 1) +); + +layout(push_constant) uniform texCoordBuf { + vec2 texCoords[4]; +} texCoordUniform; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + v_texcoord = texCoordUniform.texCoords[gl_VertexIndex]; + gl_Position = mvp * vertCoords[gl_VertexIndex]; +} +*/ +// Compiled with glslangValidator -V +static const uint8_t vertShaderCode[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x6d, 0x76, 0x70, 0x00, 0x05, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x76, 0x65, 0x72, 0x74, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65, + 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, + 0x42, 0x75, 0x66, 0x00, 0x06, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, + 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x55, 0x6e, 0x69, 0x66, + 0x6f, 0x72, 0x6d, 0x00, 0x05, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x67, 0x6c, 0x5f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, + 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2d, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; + +/* +#version 440 + +layout(location = 0) in vec2 v_texcoord; + +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D tex; + +void main() +{ + fragColor = texture(tex, v_texcoord); +} +*/ + +static const uint8_t fragShaderCode[] +{ + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, + 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, + 0x72, 0x64, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x15, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; + +class VulkanRendererEmu : public QVulkanWindowRenderer +{ + VulkanWindowRenderer* m_window{nullptr}; + QVulkanDeviceFunctions* m_devFuncs{nullptr}; + VmaAllocator allocator{nullptr}; + VmaAllocation allocation{nullptr}; + VkImage image{nullptr}; + VkImageView imageView{nullptr}; + VkPipeline pipeline{nullptr}; + VkPipelineLayout pipelineLayout{nullptr}; + VkDescriptorSetLayout descLayout{nullptr}; + VkDescriptorPool descPool{nullptr}; + VkDescriptorSet descSet{nullptr}; + VkSampler sampler{nullptr}, nearestSampler{nullptr}; + bool imageLayoutTransitioned{false}; + int cur_video_filter_method = -1; +private: + void updateOptions() { + VkResult res; + if (cur_video_filter_method == video_filter_method) return; + if (!descPool) { + VkDescriptorPoolSize descSize{}; + descSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descSize.descriptorCount = 2; + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &descSize; + poolInfo.maxSets = 2; + + if ((res = m_devFuncs->vkCreateDescriptorPool(m_window->device(), &poolInfo, nullptr, &descPool)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create descriptor pool. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &descLayout; + + if ((res = m_devFuncs->vkAllocateDescriptorSets(m_window->device(), &allocInfo, &descSet)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create descriptor set. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + }} + VkDescriptorImageInfo imageDescInfo{}; + imageDescInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageDescInfo.imageView = imageView; + imageDescInfo.sampler = video_filter_method == 0 ? nearestSampler : sampler; + + VkWriteDescriptorSet descWrite{}; + descWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descWrite.dstSet = descSet; + descWrite.dstBinding = 1; + descWrite.dstArrayElement = 0; + descWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descWrite.descriptorCount = 1; + descWrite.pImageInfo = &imageDescInfo; + + m_devFuncs->vkUpdateDescriptorSets(m_window->device(), 1, &descWrite, 0, nullptr); + cur_video_filter_method = video_filter_method; + } +public: + void* mappedPtr = nullptr; + uint32_t imagePitch{2048 * 4}; + VulkanRendererEmu(VulkanWindowRenderer *w) : m_window(w), m_devFuncs(nullptr) { } + + void initResources() override + { + VmaAllocatorCreateInfo info{}; + info.instance = m_window->vulkanInstance()->vkInstance(); + info.device = m_window->device(); + info.physicalDevice = m_window->physicalDevice(); + VmaVulkanFunctions funcs{}; + funcs.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)m_window->vulkanInstance()->getInstanceProcAddr("vkGetInstanceProcAddr"); + funcs.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)m_window->vulkanInstance()->getInstanceProcAddr("vkGetDeviceProcAddr"); + info.pVulkanFunctions = &funcs; + if (vmaCreateAllocator(&info, &allocator) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create Vulkan allocator. Switch to another renderer"); + return; + } + + m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device()); + + VkResult res; + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.format = VK_FORMAT_B8G8R8A8_UNORM; + imageInfo.extent.width = imageInfo.extent.height = 2048; + imageInfo.extent.depth = imageInfo.arrayLayers = imageInfo.mipLevels = 1; + imageInfo.tiling = VK_IMAGE_TILING_LINEAR; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + VmaAllocationInfo allocatedInfo{}; + VmaAllocationCreateInfo allocInfo{}; + allocInfo.pUserData = allocInfo.pool = nullptr; + allocInfo.requiredFlags = allocInfo.preferredFlags = 0; + allocInfo.usage = VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO; + allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + + if ((res = vmaCreateImage(allocator, &imageInfo, &allocInfo, &image, &allocation, &allocatedInfo)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create Vulkan image. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + }; + + VkImageViewCreateInfo imageViewInfo{}; + imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewInfo.format = VK_FORMAT_B8G8R8A8_UNORM; + imageViewInfo.image = image; + imageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewInfo.subresourceRange.baseMipLevel = 0; + imageViewInfo.subresourceRange.levelCount = 1; + imageViewInfo.subresourceRange.baseArrayLayer = 0; + imageViewInfo.subresourceRange.layerCount = 1; + imageViewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + imageViewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + imageViewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + imageViewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + + if ((res = m_devFuncs->vkCreateImageView(m_window->device(), &imageViewInfo, nullptr, &imageView)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create Vulkan image view. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + + // Begin Pipeline creation. + { + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = nullptr; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = {2048, 2048}; + + VkViewport viewport{}; + viewport.x = m_window->destination.x(); + viewport.y = m_window->destination.y(); + viewport.width = (float)m_window->destination.width(); + viewport.height = (float)m_window->destination.height(); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkDynamicState dynState = VK_DYNAMIC_STATE_VIEWPORT; + VkPipelineDynamicStateCreateInfo dynamicState{}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = 1; + dynamicState.pDynamicStates = &dynState; + + VkPushConstantRange range{}; + range.offset = 0; + range.size = sizeof(float) * 8; + range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + // Sampler binding start. + VkDescriptorSetLayoutBinding samplerLayoutBinding{}; + samplerLayoutBinding.binding = 1; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.pImmutableSamplers = nullptr; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &samplerLayoutBinding; + + if ((res = m_devFuncs->vkCreateDescriptorSetLayout(m_window->device(), &layoutInfo, nullptr, &descLayout))) { + QMessageBox::critical(main_window, "86Box", "Could not create descriptor set layout. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + // Sampler binding end. + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descLayout; + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = ⦥ + + if ((res = m_devFuncs->vkCreatePipelineLayout(m_window->device(), &pipelineLayoutInfo, nullptr, &pipelineLayout)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create pipeline layout. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + + // Shader loading start. + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = sizeof(vertShaderCode); + createInfo.pCode = (uint32_t*)vertShaderCode; + VkShaderModule vertModule{nullptr}, fragModule{nullptr}; + if ((res = m_devFuncs->vkCreateShaderModule(m_window->device(), &createInfo, nullptr, &vertModule)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create vertex shader. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + + createInfo.codeSize = sizeof(fragShaderCode); + createInfo.pCode = (uint32_t*)fragShaderCode; + if ((res = m_devFuncs->vkCreateShaderModule(m_window->device(), &createInfo, nullptr, &fragModule)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create fragment shader. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{vertShaderStageInfo}; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragModule; + + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; + // Shader loading end. + + VkPipelineDepthStencilStateCreateInfo depthInfo{}; + depthInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthInfo.pNext = nullptr; + depthInfo.depthTestEnable = VK_TRUE; + depthInfo.depthWriteEnable = VK_TRUE; + depthInfo.depthBoundsTestEnable = VK_FALSE; + depthInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthInfo.stencilTestEnable = VK_FALSE; + depthInfo.maxDepthBounds = 1.0f; + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = &depthInfo; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.layout = pipelineLayout; + pipelineInfo.renderPass = m_window->defaultRenderPass(); + pipelineInfo.subpass = 0; + + if ((res = m_devFuncs->vkCreateGraphicsPipelines(m_window->device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline)) != VK_SUCCESS) { + m_devFuncs->vkDestroyShaderModule(m_window->device(), vertModule, nullptr); + m_devFuncs->vkDestroyShaderModule(m_window->device(), fragModule, nullptr); + QMessageBox::critical(main_window, "86Box", "Could not create graphics pipeline. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + m_devFuncs->vkDestroyShaderModule(m_window->device(), vertModule, nullptr); + m_devFuncs->vkDestroyShaderModule(m_window->device(), fragModule, nullptr); + } + + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0; + + if ((res = m_devFuncs->vkCreateSampler(m_window->device(), &samplerInfo, nullptr, &sampler)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create linear image sampler. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + + samplerInfo.magFilter = samplerInfo.minFilter = VK_FILTER_NEAREST; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + + if ((res = m_devFuncs->vkCreateSampler(m_window->device(), &samplerInfo, nullptr, &nearestSampler)) != VK_SUCCESS) { + QMessageBox::critical(main_window, "86Box", "Could not create nearest image sampler. Switch to another renderer. " + Vulkan_GetResultString(res)); + return; + } + + updateOptions(); + + VkImageSubresource resource{}; + resource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + resource.arrayLayer = 0; + resource.mipLevel = 0; + VkSubresourceLayout layout{}; + m_devFuncs->vkGetImageSubresourceLayout(m_window->device(), image, &resource, &layout); + + imagePitch = layout.rowPitch; + mappedPtr = (uint8_t*)allocatedInfo.pMappedData + layout.offset; + emit m_window->rendererInitialized(); + } + + void releaseResources() override + { + if (pipeline) m_devFuncs->vkDestroyPipeline(m_window->device(), pipeline, nullptr); + if (pipelineLayout) m_devFuncs->vkDestroyPipelineLayout(m_window->device(), pipelineLayout, nullptr); + if (descSet) m_devFuncs->vkDestroyDescriptorPool(m_window->device(), descPool, nullptr); + if (descLayout) m_devFuncs->vkDestroyDescriptorSetLayout(m_window->device(), descLayout, nullptr); + if (imageView) m_devFuncs->vkDestroyImageView(m_window->device(), imageView, nullptr); + if (image) vmaDestroyImage(allocator, image, allocation); + if (sampler) m_devFuncs->vkDestroySampler(m_window->device(), sampler, nullptr); + if (nearestSampler) m_devFuncs->vkDestroySampler(m_window->device(), nearestSampler, nullptr); + image = nullptr; + pipeline = nullptr; + pipelineLayout = nullptr; + imageView = nullptr; + descLayout = nullptr; + descPool = nullptr; + descSet = nullptr; + sampler = nullptr; + vmaDestroyAllocator(allocator); + allocator = nullptr; + } + + QString Vulkan_GetResultString(VkResult result) + { + switch ((int)result) { + case VK_SUCCESS: + return "VK_SUCCESS"; + case VK_NOT_READY: + return "VK_NOT_READY"; + case VK_TIMEOUT: + return "VK_TIMEOUT"; + case VK_EVENT_SET: + return "VK_EVENT_SET"; + case VK_EVENT_RESET: + return "VK_EVENT_RESET"; + case VK_INCOMPLETE: + return "VK_INCOMPLETE"; + case VK_ERROR_OUT_OF_HOST_MEMORY: + return "VK_ERROR_OUT_OF_HOST_MEMORY"; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + return "VK_ERROR_OUT_OF_DEVICE_MEMORY"; + case VK_ERROR_INITIALIZATION_FAILED: + return "VK_ERROR_INITIALIZATION_FAILED"; + case VK_ERROR_DEVICE_LOST: + return "VK_ERROR_DEVICE_LOST"; + case VK_ERROR_MEMORY_MAP_FAILED: + return "VK_ERROR_MEMORY_MAP_FAILED"; + case VK_ERROR_LAYER_NOT_PRESENT: + return "VK_ERROR_LAYER_NOT_PRESENT"; + case VK_ERROR_EXTENSION_NOT_PRESENT: + return "VK_ERROR_EXTENSION_NOT_PRESENT"; + case VK_ERROR_FEATURE_NOT_PRESENT: + return "VK_ERROR_FEATURE_NOT_PRESENT"; + case VK_ERROR_INCOMPATIBLE_DRIVER: + return "VK_ERROR_INCOMPATIBLE_DRIVER"; + case VK_ERROR_TOO_MANY_OBJECTS: + return "VK_ERROR_TOO_MANY_OBJECTS"; + case VK_ERROR_FORMAT_NOT_SUPPORTED: + return "VK_ERROR_FORMAT_NOT_SUPPORTED"; + case VK_ERROR_FRAGMENTED_POOL: + return "VK_ERROR_FRAGMENTED_POOL"; + case VK_ERROR_UNKNOWN: + return "VK_ERROR_UNKNOWN"; + case VK_ERROR_OUT_OF_POOL_MEMORY: + return "VK_ERROR_OUT_OF_POOL_MEMORY"; + case VK_ERROR_INVALID_EXTERNAL_HANDLE: + return "VK_ERROR_INVALID_EXTERNAL_HANDLE"; + case VK_ERROR_FRAGMENTATION: + return "VK_ERROR_FRAGMENTATION"; + case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: + return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS"; + case VK_ERROR_SURFACE_LOST_KHR: + return "VK_ERROR_SURFACE_LOST_KHR"; + case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR"; + case VK_SUBOPTIMAL_KHR: + return "VK_SUBOPTIMAL_KHR"; + case VK_ERROR_OUT_OF_DATE_KHR: + return "VK_ERROR_OUT_OF_DATE_KHR"; + case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: + return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR"; + case VK_ERROR_VALIDATION_FAILED_EXT: + return "VK_ERROR_VALIDATION_FAILED_EXT"; + case VK_ERROR_INVALID_SHADER_NV: + return "VK_ERROR_INVALID_SHADER_NV"; +#if VK_HEADER_VERSION >= 135 && VK_HEADER_VERSION < 162 + case VK_ERROR_INCOMPATIBLE_VERSION_KHR: + return "VK_ERROR_INCOMPATIBLE_VERSION_KHR"; +#endif + case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: + return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; + case VK_ERROR_NOT_PERMITTED_EXT: + return "VK_ERROR_NOT_PERMITTED_EXT"; + case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: + return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT"; + case VK_THREAD_IDLE_KHR: + return "VK_THREAD_IDLE_KHR"; + case VK_THREAD_DONE_KHR: + return "VK_THREAD_DONE_KHR"; + case VK_OPERATION_DEFERRED_KHR: + return "VK_OPERATION_DEFERRED_KHR"; + case VK_OPERATION_NOT_DEFERRED_KHR: + return "VK_OPERATION_NOT_DEFERRED_KHR"; + case VK_PIPELINE_COMPILE_REQUIRED_EXT: + return "VK_PIPELINE_COMPILE_REQUIRED_EXT"; + default: + break; + } + if (result < 0) { + return "VK_ERROR_"; + } + return "VK_"; + } + + void startNextFrame() override + { + m_devFuncs->vkDeviceWaitIdle(m_window->device()); + VkClearValue values[2]; + auto cmdBufs = m_window->currentCommandBuffer(); + memset(values, 0, sizeof(values)); + values[0].depthStencil = { 1, 0 }; + values[1].depthStencil = { 1, 0 }; + VkRenderPassBeginInfo info{}; + VkSubpassDependency deps{}; + info.pClearValues = values; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.framebuffer = m_window->currentFramebuffer(); + info.renderArea = VkRect2D{{0, 0}, {(uint32_t)m_window->swapChainImageSize().width(), (uint32_t)m_window->swapChainImageSize().height()}}; + info.clearValueCount = 2; + info.renderPass = m_window->defaultRenderPass(); + + updateOptions(); + if (!imageLayoutTransitioned) { + VkPipelineStageFlags srcflags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_HOST_BIT, dstflags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = image; + barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + m_devFuncs->vkCmdPipelineBarrier(cmdBufs, srcflags, dstflags, 0, 0, nullptr, 0, nullptr, 1, &barrier); + imageLayoutTransitioned = true; + } + vmaFlushAllocation(allocator, allocation, 0, VK_WHOLE_SIZE); + VkViewport viewport{}; + viewport.x = m_window->destination.x(); + viewport.y = m_window->destination.y(); + viewport.width = (float)m_window->destination.width(); + viewport.height = (float)m_window->destination.height(); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + m_devFuncs->vkCmdSetViewport(cmdBufs, 0, 1, &viewport); + m_devFuncs->vkCmdBeginRenderPass(m_window->currentCommandBuffer(), &info, VK_SUBPASS_CONTENTS_INLINE); + m_devFuncs->vkCmdBindPipeline(cmdBufs, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + m_devFuncs->vkCmdBindDescriptorSets(cmdBufs, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descSet, 0, nullptr); + std::array texcoords; + auto source = m_window->source; + texcoords[0] = (QVector2D((float)source.x() / 2048.f, (float)(source.y()) / 2048.f)); + texcoords[2] = (QVector2D((float)source.x() / 2048.f, (float)(source.y() + source.height()) / 2048.f)); + texcoords[1] = (QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y()) / 2048.f)); + texcoords[3] = (QVector2D((float)(source.x() + source.width()) / 2048.f, (float)(source.y() + source.height()) / 2048.f)); + m_devFuncs->vkCmdPushConstants(cmdBufs, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(QVector2D) * 4, texcoords.data()); + m_devFuncs->vkCmdDraw(cmdBufs, 4, 1, 0, 0); + m_devFuncs->vkCmdEndRenderPass(cmdBufs); + + m_window->frameReady(); + m_devFuncs->vkDeviceWaitIdle(m_window->device()); + } +}; +#endif + +VulkanWindowRenderer::VulkanWindowRenderer(QWidget* parent) + : QVulkanWindow(parent->windowHandle()) +{ + parentWidget = parent; + instance.setLayers(QByteArrayList() << "VK_LAYER_KHRONOS_validation"); + instance.setExtensions(QByteArrayList() << "VK_EXT_debug_report"); + instance.setApiVersion(QVersionNumber(1, 0)); + if (!instance.create()) { + throw std::runtime_error("Could not create Vulkan instance"); + } + uint32_t physicalDevices = 0; + instance.functions()->vkEnumeratePhysicalDevices(instance.vkInstance(), &physicalDevices, nullptr); + if (physicalDevices == 0) { + throw std::runtime_error("No physical devices available."); + } + qDebug() << instance.layers(); + setVulkanInstance(&instance); + setPhysicalDeviceIndex(0); + setPreferredColorFormats({VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_A8B8G8R8_UNORM_PACK32}); + setFlags(Flag::PersistentResources); + buf_usage = std::vector(1); + buf_usage[0].clear(); +} + +QVulkanWindowRenderer* VulkanWindowRenderer::createRenderer() +{ + renderer = new VulkanRenderer2(this); + return renderer; +} + +void VulkanWindowRenderer::resizeEvent(QResizeEvent *event) { + onResize(width(), height()); + + QVulkanWindow::resizeEvent(event); +} + +bool VulkanWindowRenderer::event(QEvent *event) +{ + bool res = false; + if (!eventDelegate(event, res)) return QVulkanWindow::event(event); + return res; +} + +void VulkanWindowRenderer::onBlit(int buf_idx, int x, int y, int w, int h) +{ + source.setRect(x, y, w, h); + if (isExposed()) requestUpdate(); + buf_usage[0].clear(); +} + +uint32_t VulkanWindowRenderer::getBytesPerRow() +{ + return renderer->imagePitch; +} + +std::vector> VulkanWindowRenderer::getBuffers() +{ + return std::vector{std::make_tuple((uint8_t*)renderer->mappedPtr, &this->buf_usage[0])}; +} +#endif diff --git a/src/qt/qt_vulkanwindowrenderer.hpp b/src/qt/qt_vulkanwindowrenderer.hpp new file mode 100644 index 000000000..df252d393 --- /dev/null +++ b/src/qt/qt_vulkanwindowrenderer.hpp @@ -0,0 +1,39 @@ +#ifndef VULKANWINDOWRENDERER_HPP +#define VULKANWINDOWRENDERER_HPP + +#include + +#if QT_CONFIG(vulkan) +#include "qt_renderercommon.hpp" +#include "qt_vulkanrenderer.hpp" + +class VulkanRenderer2; + +class VulkanWindowRenderer : public QVulkanWindow, public RendererCommon +{ + Q_OBJECT +public: + VulkanWindowRenderer(QWidget* parent); +public slots: + void onBlit(int buf_idx, int x, int y, int w, int h); +signals: + void rendererInitialized(); + void errorInitializing(); +protected: + virtual std::vector> getBuffers() override; + void resizeEvent(QResizeEvent*) override; + bool event(QEvent*) override; + uint32_t getBytesPerRow() override; +private: + QVulkanInstance instance; + + QVulkanWindowRenderer* createRenderer() override; + + friend class VulkanRendererEmu; + friend class VulkanRenderer2; + + VulkanRenderer2* renderer; +}; +#endif + +#endif // VULKANWINDOWRENDERER_HPP diff --git a/src/qt/texture_frag.spv b/src/qt/texture_frag.spv new file mode 100644 index 000000000..7521ef6ee Binary files /dev/null and b/src/qt/texture_frag.spv differ diff --git a/src/qt/texture_vert.spv b/src/qt/texture_vert.spv new file mode 100644 index 000000000..6292c0de3 Binary files /dev/null and b/src/qt/texture_vert.spv differ diff --git a/src/qt/wl_mouse.cpp b/src/qt/wl_mouse.cpp index 75a03813c..916fe2338 100644 --- a/src/qt/wl_mouse.cpp +++ b/src/qt/wl_mouse.cpp @@ -25,6 +25,11 @@ #include #include +extern "C" +{ +#include <86box/plat.h> +} + static zwp_relative_pointer_manager_v1* rel_manager = nullptr; static zwp_relative_pointer_v1* rel_pointer = nullptr; static zwp_pointer_constraints_v1* conf_pointer_interface = nullptr; @@ -70,9 +75,19 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id, } } +static void +display_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) +{ + plat_mouse_capture(0); + zwp_relative_pointer_manager_v1_destroy(rel_manager); + zwp_pointer_constraints_v1_destroy(conf_pointer_interface); + rel_manager = nullptr; + conf_pointer_interface = nullptr; +} + static const struct wl_registry_listener registry_listener = { display_handle_global, - nullptr + display_global_remove }; void wl_init() @@ -91,9 +106,11 @@ void wl_init() void wl_mouse_capture(QWindow *window) { - rel_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(rel_manager, (wl_pointer*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_pointer")); - zwp_relative_pointer_v1_add_listener(rel_pointer, &rel_listener, nullptr); - conf_pointer = zwp_pointer_constraints_v1_lock_pointer(conf_pointer_interface, (wl_surface*)QGuiApplication::platformNativeInterface()->nativeResourceForWindow("surface", window), (wl_pointer*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_pointer"), nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + if (rel_manager) { + rel_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(rel_manager, (wl_pointer*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_pointer")); + zwp_relative_pointer_v1_add_listener(rel_pointer, &rel_listener, nullptr); + } + if (conf_pointer_interface) conf_pointer = zwp_pointer_constraints_v1_lock_pointer(conf_pointer_interface, (wl_surface*)QGuiApplication::platformNativeInterface()->nativeResourceForWindow("surface", window), (wl_pointer*)QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_pointer"), nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); } void wl_mouse_uncapture() diff --git a/src/qt_resources.qrc b/src/qt_resources.qrc index 045c3659d..fec56ad71 100644 --- a/src/qt_resources.qrc +++ b/src/qt_resources.qrc @@ -57,4 +57,8 @@ win/icons/send_cae.ico win/icons/settings.ico + + qt/texture_vert.spv + qt/texture_frag.spv + diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt index db700670d..087f62cb1 100644 --- a/src/sound/CMakeLists.txt +++ b/src/sound/CMakeLists.txt @@ -49,6 +49,7 @@ else() # Use FAudio, a reimplementation of XAudio2 pkg_check_modules(FAUDIO IMPORTED_TARGET FAudio) if(FAUDIO_FOUND) + include_directories(${FAUDIO_INCLUDE_DIRS}) target_link_libraries(86Box PkgConfig::FAUDIO) else() find_path(FAUDIO_INCLUDE_DIR NAMES "FAudio.h") diff --git a/src/sound/midi_rtmidi.cpp b/src/sound/midi_rtmidi.cpp index 43f0695ee..c65a42b87 100644 --- a/src/sound/midi_rtmidi.cpp +++ b/src/sound/midi_rtmidi.cpp @@ -36,6 +36,13 @@ extern "C" #include <86box/midi_rtmidi.h> #include <86box/config.h> +// Disable c99-designator to avoid the warnings in rtmidi_*_device +#ifdef __clang__ +#if __has_warning("-Wc99-designator") +#pragma clang diagnostic ignored "-Wc99-designator" +#endif +#endif + static RtMidiOut * midiout = nullptr; static RtMidiIn * midiin = nullptr; static int midi_out_id = 0, midi_in_id = 0; diff --git a/src/unix/assets/86Box.spec b/src/unix/assets/86Box.spec index 4809db3e8..1ef0aeb14 100644 --- a/src/unix/assets/86Box.spec +++ b/src/unix/assets/86Box.spec @@ -12,10 +12,10 @@ # After a successful build, you can install the RPMs as follows: # sudo dnf install RPMS/$(uname -m)/86Box-3* RPMS/noarch/86Box-roms* -%global romver 20220319 +%global romver 20220523 Name: 86Box -Version: 3.4 +Version: 3.5 Release: 1%{?dist} Summary: Classic PC emulator License: GPLv2+ @@ -27,6 +27,7 @@ Source1: https://github.com/86Box/roms/archive/refs/tags/%{romver}.tar.gz BuildRequires: cmake BuildRequires: desktop-file-utils BuildRequires: extra-cmake-modules +BuildRequires: freetype-devel BuildRequires: gcc-c++ BuildRequires: libFAudio-devel BuildRequires: libappstream-glib @@ -84,7 +85,7 @@ Collection of ROMs for use with 86Box. # install icons for i in 48 64 72 96 128 192 256 512; do mkdir -p $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/${i}x${i}/apps - cp src/unix/assets/${i}x${i}/net.86box.86Box.png $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/${i}x${i}/apps/net.86box.86Box.png + cp src/unix/assets/${i}x${i}/net.86box.86Box.png $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/${i}x${i}/apps done # install desktop file @@ -119,5 +120,5 @@ popd %{_bindir}/roms %changelog -* Sat Mar 19 2022 Robert de Rooy 3.3-1 -- Initial RPM release +* Mon May 23 2022 Robert de Rooy 3.5-1 +- Bump release diff --git a/src/unix/assets/net.86box.86Box.metainfo.xml b/src/unix/assets/net.86box.86Box.metainfo.xml index cfcfdd3c9..9593da9ea 100644 --- a/src/unix/assets/net.86box.86Box.metainfo.xml +++ b/src/unix/assets/net.86box.86Box.metainfo.xml @@ -10,7 +10,7 @@ net.86box.86Box.desktop - + diff --git a/src/unix/unix.c b/src/unix/unix.c index a7ef1abe7..3ab5d46e6 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -806,7 +806,7 @@ plat_init_rom_paths() #else char default_rom_path[1024] = { '\0 '}; getDefaultROMPath(default_rom_path); - rom_path_add(default_rom_path); + rom_add_path(default_rom_path); #endif } diff --git a/src/unix/unix_sdl.c b/src/unix/unix_sdl.c index d86b5f72c..aeeb9d60c 100644 --- a/src/unix/unix_sdl.c +++ b/src/unix/unix_sdl.c @@ -43,6 +43,7 @@ int title_set = 0; int resize_pending = 0; int resize_w = 0; int resize_h = 0; +double mouse_sensitivity = 1.0; /* Unused. */ static uint8_t interpixels[17842176]; extern void RenderImGui(); diff --git a/src/unix/unix_thread.c b/src/unix/unix_thread.c index dc7625e12..5221d90eb 100644 --- a/src/unix/unix_thread.c +++ b/src/unix/unix_thread.c @@ -157,6 +157,18 @@ thread_wait_mutex(mutex_t *_mutex) } +int +thread_test_mutex(mutex_t *_mutex) +{ + if (_mutex == NULL) + return(0); + pt_mutex_t *mutex = (pt_mutex_t *)_mutex; + + return + pthread_mutex_trylock(&mutex->mutex) != 0; +} + + int thread_release_mutex(mutex_t *_mutex) { diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 3e1ffcc71..ca455ba72 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -16,7 +16,7 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.c vid_compaq_cga.c vid_mda.c vid_hercules.c vid_herculesplus.c vid_incolor.c vid_colorplus.c vid_genius.c vid_pgc.c vid_im1024.c - vid_sigma.c vid_wy700.c vid_ega.c vid_ega_render.c vid_svga.c + vid_sigma.c vid_wy700.c vid_ega.c vid_ega_render.c vid_svga.c vid_8514a.c vid_svga_render.c vid_ddc.c vid_vga.c vid_ati_eeprom.c vid_ati18800.c vid_ati28800.c vid_ati_mach64.c vid_ati68860_ramdac.c vid_bt48x_ramdac.c vid_av9194.c vid_icd2061.c vid_ics2494.c vid_ics2595.c vid_cl54xx.c diff --git a/src/video/vid_8514a.c b/src/video/vid_8514a.c new file mode 100644 index 000000000..cfed45bfd --- /dev/null +++ b/src/video/vid_8514a.c @@ -0,0 +1,3772 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Emulation of the 8514/A card from IBM for the MCA bus and + * generic ISA bus clones without vendor extensions. + * + * + * + * Authors: Sarah Walker, + * Miran Grca, + * + * Copyright 2008-2019 Sarah Walker. + * Copyright 2016-2019 Miran Grca. + */ +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/io.h> +#include <86box/machine.h> +#include <86box/mem.h> +#include <86box/timer.h> +#include <86box/mca.h> +#include <86box/rom.h> +#include <86box/plat.h> +#include <86box/thread.h> +#include <86box/video.h> +#include <86box/vid_svga.h> +#include <86box/vid_svga_render.h> +#include <86box/vid_8514a.h> +#include "cpu.h" + +#define INT_VSY (1 << 0) +#define INT_GE_BSY (1 << 1) +#define INT_FIFO_OVR (1 << 2) +#define INT_FIFO_EMP (1 << 3) +#define INT_MASK 0xf + +#define FIFO_MASK (FIFO_SIZE - 1) +#define FIFO_ENTRY_SIZE (1 << 31) + +#define FIFO_ENTRIES_8514 (dev->fifo_write_idx - dev->fifo_read_idx) +#define FIFO_FULL_8514 ((dev->fifo_write_idx - dev->fifo_read_idx) >= FIFO_SIZE) +#define FIFO_EMPTY_8514 (dev->fifo_read_idx == dev->fifo_write_idx) + +#define FIFO_TYPE_8514 0xff000000 +#define FIFO_ADDR_8514 0x00ffffff + +static void ibm8514_accel_out_fifo(ibm8514_t *dev, uint16_t port, uint32_t val, int len); +static void ibm8514_accel_outb(uint16_t port, uint8_t val, void *p); +static void ibm8514_accel_outw(uint16_t port, uint16_t val, void *p); +static uint8_t ibm8514_accel_inb(uint16_t port, void *p); +static uint16_t ibm8514_accel_inw(uint16_t port, void *p); + +static void ibm8514_short_stroke_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, ibm8514_t *dev, uint8_t ssv, int len); +static void ibm8514_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, ibm8514_t *dev, int len); + +#define READ_PIXTRANS_WORD(cx, n) \ + if (cmd <= 1) { \ + temp = dev->vram[((dev->accel.cy * 1024) + (cx) + (n)) & dev->vram_mask]; \ + temp |= (dev->vram[((dev->accel.cy * 1024) + (cx) + (n + 1)) & dev->vram_mask] << 8); \ + } else { \ + temp = dev->vram[(dev->accel.dest + (cx) + (n)) & dev->vram_mask]; \ + temp |= (dev->vram[(dev->accel.dest + (cx) + (n + 1)) & dev->vram_mask] << 8); \ + } + +#define READ(addr, dat) \ + dat = dev->vram[(addr) & (dev->vram_mask)]; + +#define MIX(mixmode, dest_dat, src_dat) { \ + switch ((mixmode) ? (dev->accel.frgd_mix & 0x1f) : (dev->accel.bkgd_mix & 0x1f)) { \ + case 0x00: dest_dat = ~dest_dat; break; \ + case 0x01: dest_dat = 0; break; \ + case 0x02: dest_dat = ~0; break; \ + case 0x03: dest_dat = dest_dat; break; \ + case 0x04: dest_dat = ~src_dat; break; \ + case 0x05: dest_dat = src_dat ^ dest_dat; break; \ + case 0x06: dest_dat = ~(src_dat ^ dest_dat); break; \ + case 0x07: dest_dat = src_dat; break; \ + case 0x08: dest_dat = ~(src_dat & dest_dat); break; \ + case 0x09: dest_dat = ~src_dat | dest_dat; break; \ + case 0x0a: dest_dat = src_dat | ~dest_dat; break; \ + case 0x0b: dest_dat = src_dat | dest_dat; break; \ + case 0x0c: dest_dat = src_dat & dest_dat; break; \ + case 0x0d: dest_dat = src_dat & ~dest_dat; break; \ + case 0x0e: dest_dat = ~src_dat & dest_dat; break; \ + case 0x0f: dest_dat = ~(src_dat | dest_dat); break; \ + case 0x10: dest_dat = MIN(src_dat, dest_dat); break; \ + case 0x11: dest_dat = dest_dat - src_dat; break; \ + case 0x12: dest_dat = src_dat - dest_dat; break; \ + case 0x13: dest_dat = src_dat + dest_dat; break; \ + case 0x14: dest_dat = MAX(src_dat, dest_dat); break; \ + case 0x15: dest_dat = (dest_dat - src_dat) / 2; break; \ + case 0x16: dest_dat = (src_dat - dest_dat) / 2; break; \ + case 0x17: dest_dat = (dest_dat + src_dat) / 2; break; \ + case 0x18: dest_dat = MAX(0, (dest_dat - src_dat)); break; \ + case 0x19: dest_dat = MAX(0, (dest_dat - src_dat)); break; \ + case 0x1a: dest_dat = MAX(0, (src_dat - dest_dat)); break; \ + case 0x1b: dest_dat = MIN(0xff, (dest_dat + src_dat)); break; \ + case 0x1c: dest_dat = MAX(0, (dest_dat - src_dat)) / 2; break; \ + case 0x1d: dest_dat = MAX(0, (dest_dat - src_dat)) / 2; break; \ + case 0x1e: dest_dat = MAX(0, (src_dat - dest_dat)) / 2; break; \ + case 0x1f: dest_dat = (0xff < (src_dat + dest_dat)) ? 0xff : ((src_dat + dest_dat) / 2); break; \ + } \ + } + +#define WRITE(addr, dat) \ + dev->vram[((addr)) & (dev->vram_mask)] = dat; \ + dev->changedvram[(((addr)) & (dev->vram_mask)) >> 12] = changeframecount; + + +static int +ibm8514_cpu_src(ibm8514_t *dev) +{ + if (!(dev->accel.cmd & 0x100)) + return 0; + + if (dev->accel.cmd & 1) + return 1; + + return 0; +} + +static int +ibm8514_cpu_dest(ibm8514_t *dev) +{ + if (!(dev->accel.cmd & 0x100)) + return 0; + + if (dev->accel.cmd & 1) + return 0; + + return 1; +} + + +static void +ibm8514_accel_out_pixtrans(ibm8514_t *dev, uint16_t port, uint16_t val, int len) +{ + uint8_t nibble = 0; + uint32_t pixelxfer = 0, monoxfer = 0xffffffff; + int pixcnt = 0; + int pixcntl = (dev->accel.multifunc[0x0a] >> 6) & 3; + int frgd_mix = (dev->accel.frgd_mix >> 5) & 3; + int bkgd_mix = (dev->accel.bkgd_mix >> 5) & 3; + int cmd = dev->accel.cmd >> 13; + int and3 = dev->accel.cur_x & 3; + + if (dev->accel.cmd & 0x100) { + if (len != 1) { + /*Bus size*/ + if (dev->accel.cmd & 0x200) /*16-bit*/ + pixcnt = 16; + else /*8-bit*/ + pixcnt = 8; + + /*Pixel transfer data mode, can't be the same as Foreground/Background CPU data*/ + if (pixcntl == 2) { + if ((frgd_mix == 2) || (bkgd_mix == 2)) { + pixelxfer = val; + } else { + if (dev->accel.cmd & 2) { + if (pixcnt == 16) { + if ((cmd >= 2) && (dev->accel.cmd & 0x1000)) + val = (val >> 8) | (val << 8); + } + if (and3 == 3) { + if (dev->accel.cmd & 0x1000) + goto regular_nibble; + if (val & 0x02) + nibble |= 0x10; + if (val & 0x04) + nibble |= 0x08; + if (val & 0x08) + nibble |= 0x04; + if (val & 0x10) + nibble |= 0x02; + if (val & 0x200) + nibble |= 0x01; + if (val & 0x400) + nibble |= 0x80; + if (val & 0x800) + nibble |= 0x40; + if (val & 0x1000) + nibble |= 0x20; + } else if (and3 == 2) { + if (dev->accel.cmd & 0x1000) + goto regular_nibble; + if (val & 0x02) + nibble |= 0x20; + if (val & 0x04) + nibble |= 0x10; + if (val & 0x08) + nibble |= 0x08; + if (val & 0x10) + nibble |= 0x04; + if (val & 0x200) + nibble |= 0x02; + if (val & 0x400) + nibble |= 0x01; + if (val & 0x800) + nibble |= 0x80; + if (val & 0x1000) + nibble |= 0x40; + } else if (and3 == 1) { + if (dev->accel.cmd & 0x1000) + goto regular_nibble; + if (val & 0x02) + nibble |= 0x40; + if (val & 0x04) + nibble |= 0x20; + if (val & 0x08) + nibble |= 0x10; + if (val & 0x10) + nibble |= 0x08; + if (val & 0x200) + nibble |= 0x04; + if (val & 0x400) + nibble |= 0x02; + if (val & 0x800) + nibble |= 0x01; + if (val & 0x1000) + nibble |= 0x80; + } else { +regular_nibble: + if (val & 0x02) + nibble |= 0x80; + if (val & 0x04) + nibble |= 0x40; + if (val & 0x08) + nibble |= 0x20; + if (val & 0x10) + nibble |= 0x10; + if (val & 0x200) + nibble |= 0x08; + if (val & 0x400) + nibble |= 0x04; + if (val & 0x800) + nibble |= 0x02; + if (val & 0x1000) + nibble |= 0x01; + } + + if ((and3 == 0) || (dev->accel.cmd & 0x1000) || (dev->accel.cmd & 8)) { + if (dev->accel.cmd & 8) { + monoxfer = val; + } else + monoxfer = nibble; + ibm8514_accel_start(pixcnt, 1, monoxfer, pixelxfer, dev, len); + if (dev->accel.nibbleset != NULL) { + free(dev->accel.nibbleset); + dev->accel.nibbleset = NULL; + } + if (dev->accel.writemono != NULL) { + free(dev->accel.writemono); + dev->accel.writemono = NULL; + } + return; + } + + dev->accel.writemono[dev->accel.x_count] = nibble; + if (val & 0x1c00) { + if (and3 == 1) { + if (val & 0x1000) + dev->accel.nibbleset[dev->accel.x_count] = 0x80; + else + dev->accel.nibbleset[dev->accel.x_count] = 0; + } else if (and3 == 2) { + if (val & 0x1000) { + if (val & 0x800) + dev->accel.nibbleset[dev->accel.x_count] = 0xc0; + else + dev->accel.nibbleset[dev->accel.x_count] = 0x40; + } else if (val & 0x800) { + if (val & 0x1000) + dev->accel.nibbleset[dev->accel.x_count] = 0xc0; + else + dev->accel.nibbleset[dev->accel.x_count] = 0x80; + } else + dev->accel.nibbleset[dev->accel.x_count] = 0; + } else if (and3 == 3) { + if (val & 0x1000) { + if (val & 0x800) { + if (val & 0x400) + dev->accel.nibbleset[dev->accel.x_count] = 0xe0; + else + dev->accel.nibbleset[dev->accel.x_count] = 0x60; + } else if (val & 0x400) { + if (val & 0x800) + dev->accel.nibbleset[dev->accel.x_count] = 0xe0; + else + dev->accel.nibbleset[dev->accel.x_count] = 0xa0; + } else + dev->accel.nibbleset[dev->accel.x_count] = 0x20; + } else if (val & 0x800) { + if (val & 0x400) { + if (val & 0x1000) + dev->accel.nibbleset[dev->accel.x_count] = 0xe0; + else + dev->accel.nibbleset[dev->accel.x_count] = 0xc0; + } else if (val & 0x1000) { + if (val & 0x400) + dev->accel.nibbleset[dev->accel.x_count] = 0xe0; + else + dev->accel.nibbleset[dev->accel.x_count] = 0x60; + } else + dev->accel.nibbleset[dev->accel.x_count] = 0x40; + } else if (val & 0x400) { + if (val & 0x800) { + if (val & 0x1000) + dev->accel.nibbleset[dev->accel.x_count] = 0xe0; + else + dev->accel.nibbleset[dev->accel.x_count] = 0xc0; + } else if (val & 0x1000) { + if (val & 0x800) + dev->accel.nibbleset[dev->accel.x_count] = 0xe0; + else + dev->accel.nibbleset[dev->accel.x_count] = 0xa0; + } else + dev->accel.nibbleset[dev->accel.x_count] = 0x80; + } else + dev->accel.nibbleset[dev->accel.x_count] = 0; + } + } else + dev->accel.nibbleset[dev->accel.x_count] = 0; + + dev->accel.x_count++; + if (dev->accel.x_count == dev->accel.sys_cnt) { + for (int i = 0; i < dev->accel.x_count; i++) { + dev->accel.writemono[i] &= ~dev->accel.nibbleset[i]; + dev->accel.writemono[i] |= dev->accel.nibbleset[i + 1]; + ibm8514_accel_start(pixcnt, 1, dev->accel.writemono[i], pixelxfer, dev, len); + } + + dev->accel.x_count = 0; + if (dev->accel.nibbleset != NULL) { + free(dev->accel.nibbleset); + dev->accel.nibbleset = NULL; + } + if (dev->accel.writemono != NULL) { + free(dev->accel.writemono); + dev->accel.writemono = NULL; + } + } + return; + } + monoxfer = val; + } + } else { + pixelxfer = val; + } + ibm8514_accel_start(pixcnt, 1, monoxfer, pixelxfer, dev, len); + } + } +} + +static void +ibm8514_accel_out_fifo(ibm8514_t *dev, uint16_t port, uint32_t val, int len) +{ + switch (port) { + case 0x82e8: + case 0xc2e8: + if (len == 1) { + dev->accel.cur_y_bit12 = (dev->accel.cur_y_bit12 & 0xf00) | val; + dev->accel.cur_y = (dev->accel.cur_y & 0x700) | val; + } else { + dev->accel.cur_y_bit12 = val & 0xfff; + dev->accel.cur_y = val & 0x7ff; + } + break; + case 0x82e9: + case 0xc2e9: + if (len == 1) { + dev->accel.cur_y_bit12 = (dev->accel.cur_y_bit12 & 0xff) | ((val & 0x0f) << 8); + dev->accel.cur_y = (dev->accel.cur_y & 0xff) | ((val & 0x07) << 8); + } + break; + + case 0x86e8: + case 0xc6e8: + if (len == 1) { + dev->accel.cur_x_bit12 = (dev->accel.cur_x_bit12 & 0xf00) | val; + dev->accel.cur_x = (dev->accel.cur_x & 0x700) | val; + } else { + dev->accel.cur_x_bit12 = val & 0xfff; + dev->accel.cur_x = val & 0x7ff; + } + break; + case 0x86e9: + case 0xc6e9: + if (len == 1) { + dev->accel.cur_x_bit12 = (dev->accel.cur_x_bit12 & 0xff) | ((val & 0x0f) << 8); + dev->accel.cur_x = (dev->accel.cur_x & 0xff) | ((val & 0x07) << 8); + } + break; + + case 0x8ae8: + case 0xcae8: + if (len == 1) + dev->accel.desty_axstp = (dev->accel.desty_axstp & 0x3f00) | val; + else { + dev->accel.desty_axstp = val & 0x3fff; + if (val & 0x2000) + dev->accel.desty_axstp |= ~0x1fff; + } + break; + case 0x8ae9: + case 0xcae9: + if (len == 1) { + dev->accel.desty_axstp = (dev->accel.desty_axstp & 0xff) | ((val & 0x3f) << 8); + if (val & 0x20) + dev->accel.desty_axstp |= ~0x1fff; + } + break; + + case 0x8ee8: + case 0xcee8: + if (len == 1) + dev->accel.destx_distp = (dev->accel.destx_distp & 0x3f00) | val; + else { + dev->accel.destx_distp = val & 0x3fff; + if (val & 0x2000) + dev->accel.destx_distp |= ~0x1fff; + } + break; + case 0x8ee9: + case 0xcee9: + if (len == 1) { + dev->accel.destx_distp = (dev->accel.destx_distp & 0xff) | ((val & 0x3f) << 8); + if (val & 0x20) + dev->accel.destx_distp |= ~0x1fff; + } + break; + + case 0x92e8: + if (len != 1) + dev->test = val; + case 0xd2e8: + if (len == 1) + dev->accel.err_term = (dev->accel.err_term & 0x3f00) | val; + else { + dev->accel.err_term = val & 0x3fff; + if (val & 0x2000) + dev->accel.err_term |= ~0x3fff; + } + break; + case 0x92e9: + case 0xd2e9: + if (len == 1) { + dev->accel.err_term = (dev->accel.err_term & 0xff) | ((val & 0x3f) << 8); + if (val & 0x20) + dev->accel.err_term |= ~0x3fff; + } + break; + + case 0x96e8: + case 0xd6e8: + if (len == 1) + dev->accel.maj_axis_pcnt = (dev->accel.maj_axis_pcnt & 0x700) | val; + else { + dev->accel.maj_axis_pcnt = val & 0x7ff; + } + break; + case 0x96e9: + case 0xd6e9: + if (len == 1) { + dev->accel.maj_axis_pcnt = (dev->accel.maj_axis_pcnt & 0xff) | ((val & 0x07) << 8); + } + break; + + case 0x9ae8: + case 0xdae8: + dev->accel.ssv_state = 0; + if (len == 1) + dev->accel.cmd = (dev->accel.cmd & 0xff00) | val; + else { + dev->data_available = 0; + dev->data_available2 = 0; + dev->accel.cmd = val; + if (dev->accel.cmd & 0x100) + dev->accel.cmd_back = 0; + ibm8514_accel_start(-1, 0, -1, 0, dev, len); + } + break; + case 0x9ae9: + case 0xdae9: + if (len == 1) { + dev->data_available = 0; + dev->data_available2 = 0; + dev->accel.cmd = (dev->accel.cmd & 0xff) | (val << 8); + if (port == 0xdae9) { + if (dev->accel.cmd & 0x100) + dev->accel.cmd_back = 0; + } + ibm8514_accel_start(-1, 0, -1, 0, dev, len); + } + break; + + case 0x9ee8: + case 0xdee8: + dev->accel.ssv_state = 1; + if (len == 1) + dev->accel.short_stroke = (dev->accel.short_stroke & 0xff00) | val; + else { + dev->accel.short_stroke = val; + if (dev->accel.cur_x & 0x400) + dev->accel.cx |= ~0x3ff; + if (dev->accel.cur_y & 0x400) + dev->accel.cy |= ~0x3ff; + if (dev->accel.cmd & 0x1000) { + ibm8514_short_stroke_start(-1, 0, -1, 0, dev, dev->accel.short_stroke & 0xff, len); + ibm8514_short_stroke_start(-1, 0, -1, 0, dev, dev->accel.short_stroke >> 8, len); + } else { + ibm8514_short_stroke_start(-1, 0, -1, 0, dev, dev->accel.short_stroke >> 8, len); + ibm8514_short_stroke_start(-1, 0, -1, 0, dev, dev->accel.short_stroke & 0xff, len); + } + } + break; + case 0x9ee9: + case 0xdee9: + if (len == 1) { + dev->accel.short_stroke = (dev->accel.short_stroke & 0xff) | (val << 8); + dev->accel.cx = dev->accel.cur_x; + dev->accel.cy = dev->accel.cur_y; + if (dev->accel.cur_x & 0x400) + dev->accel.cx |= ~0x3ff; + if (dev->accel.cur_y & 0x400) + dev->accel.cy |= ~0x3ff; + + if (dev->accel.cmd & 0x1000) { + ibm8514_short_stroke_start(-1, 0, -1, 0, dev, dev->accel.short_stroke & 0xff, len); + ibm8514_short_stroke_start(-1, 0, -1, 0, dev, dev->accel.short_stroke >> 8, len); + } else { + ibm8514_short_stroke_start(-1, 0, -1, 0, dev, dev->accel.short_stroke >> 8, len); + ibm8514_short_stroke_start(-1, 0, -1, 0, dev, dev->accel.short_stroke & 0xff, len); + } + } + break; + + case 0xa2e8: + case 0xe2e8: + if (port == 0xe2e8) { + if (dev->accel.cmd_back) { + if (len == 1) + dev->accel.bkgd_color = (dev->accel.bkgd_color & 0x00ff) | val; + else + dev->accel.bkgd_color = val; + } else { + if (ibm8514_cpu_dest(dev)) + break; + ibm8514_accel_out_pixtrans(dev, port, val, len); + } + } else { + if (len == 1) + dev->accel.bkgd_color = (dev->accel.bkgd_color & 0x00ff) | val; + else + dev->accel.bkgd_color = val; + } + break; + case 0xa2e9: + case 0xe2e9: + if (len == 1) + dev->accel.bkgd_color = (dev->accel.bkgd_color & 0xff00) | (val << 8); + break; + + case 0xa6e8: + case 0xe6e8: + if (port == 0xe6e8) { + if (dev->accel.cmd_back) { + if (len == 1) + dev->accel.frgd_color = (dev->accel.frgd_color & 0x00ff) | val; + else + dev->accel.frgd_color = val; + } else { + if (ibm8514_cpu_dest(dev)) + break; + ibm8514_accel_out_pixtrans(dev, port, val, len); + } + } else { + if (len == 1) + dev->accel.frgd_color = (dev->accel.frgd_color & 0x00ff) | val; + else + dev->accel.frgd_color = val; + } + break; + case 0xa6e9: + case 0xe6e9: + if (len == 1) + dev->accel.frgd_color = (dev->accel.frgd_color & 0xff00) | (val << 8); + break; + + case 0xaae8: + case 0xeae8: + if (len == 1) + dev->accel.wrt_mask = (dev->accel.wrt_mask & 0x00ff) | val; + else + dev->accel.wrt_mask = val; + break; + case 0xaae9: + case 0xeae9: + if (len == 1) + dev->accel.wrt_mask = (dev->accel.wrt_mask & 0xff00) | (val << 8); + break; + + case 0xaee8: + case 0xeee8: + if (len == 1) + dev->accel.rd_mask = (dev->accel.rd_mask & 0x00ff) | val; + else + dev->accel.rd_mask = val; + break; + case 0xaee9: + case 0xeee9: + if (len == 1) + dev->accel.rd_mask = (dev->accel.rd_mask & 0xff00) | (val << 8); + break; + + case 0xb2e8: + case 0xf2e8: + if (len == 1) + dev->accel.color_cmp = (dev->accel.color_cmp & 0x00ff) | val; + else + dev->accel.color_cmp = val; + break; + case 0xb2e9: + case 0xf2e9: + if (len == 1) + dev->accel.color_cmp = (dev->accel.color_cmp & 0xff00) | (val << 8); + break; + + case 0xb6e8: + case 0xf6e8: + dev->accel.bkgd_mix = val & 0xff; + break; + + case 0xbae8: + case 0xfae8: + dev->accel.frgd_mix = val & 0xff; + break; + + case 0xbee8: + case 0xfee8: + if (len == 1) + dev->accel.multifunc_cntl = (dev->accel.multifunc_cntl & 0xff00) | val; + else { + dev->accel.multifunc_cntl = val; + dev->accel.multifunc[dev->accel.multifunc_cntl >> 12] = dev->accel.multifunc_cntl & 0xfff; + if ((dev->accel.multifunc_cntl >> 12) == 1) { + dev->accel.clip_top = val & 0x3ff; + } + if ((dev->accel.multifunc_cntl >> 12) == 2) { + dev->accel.clip_left = val & 0x3ff; + } + if (port == 0xfee8) + dev->accel.cmd_back = 1; + else + dev->accel.cmd_back = 0; + } + break; + case 0xbee9: + case 0xfee9: + if (len == 1) { + dev->accel.multifunc_cntl = (dev->accel.multifunc_cntl & 0xff) | (val << 8); + dev->accel.multifunc[dev->accel.multifunc_cntl >> 12] = dev->accel.multifunc_cntl & 0xfff; + if (port == 0xfee9) + dev->accel.cmd_back = 1; + else + dev->accel.cmd_back = 0; + } + break; + } +} + +static void +ibm8514_ramdac_out(uint16_t port, uint8_t val, void *p) +{ + svga_t *svga = (svga_t *)p; + uint8_t index; + + switch (port) { + case 0x2ea: + svga_out(0x3c6, val, svga); + break; + case 0x2eb: + svga_out(0x3c7, val, svga); + break; + case 0x2ec: + svga_out(0x3c8, val, svga); + break; + case 0x2ed: + svga_out(0x3c9, val, svga); + break; + } +} + +static uint8_t +ibm8514_ramdac_in(uint16_t port, void *p) +{ + svga_t *svga = (svga_t *)p; + uint8_t ret = 0xff; + uint8_t index; + + switch (port) { + case 0x2ea: + ret = svga_in(0x3c6, svga); + break; + case 0x2eb: + ret = svga_in(0x3c7, svga); + break; + case 0x2ec: + ret = svga_in(0x3c8, svga); + break; + case 0x2ed: + ret = svga_in(0x3c9, svga); + break; + + } + return ret; +} + +static void +ibm8514_io_remove(svga_t *svga) +{ + io_removehandler(0x2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x2ea, 0x0004, ibm8514_ramdac_in, NULL, NULL, ibm8514_ramdac_out, NULL, NULL, svga); + io_removehandler(0x6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x12e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x16e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x1ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x1ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x22e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x26e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x2ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x42e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x4ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x52e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x56e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x5ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x5ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x82e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x86e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x8ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x8ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x92e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x96e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x9ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0x9ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xa2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xa6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xaae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xaee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xb2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xb6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xbae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xbee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xe2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + + io_removehandler(0xc2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xc6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xcae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xcee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xd2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xd6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xdae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xdee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xe6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xeae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xeee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xf2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xf6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xfae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_removehandler(0xfee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); +} + +static void +ibm8514_io_set(svga_t *svga) +{ + io_sethandler(0x2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x2ea, 0x0004, ibm8514_ramdac_in, NULL, NULL, ibm8514_ramdac_out, NULL, NULL, svga); + io_sethandler(0x6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x12e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x16e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x1ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x1ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x22e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x26e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x2ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x42e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x4ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x52e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x56e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x5ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x5ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x82e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x86e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x8ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x8ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x92e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x96e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x9ae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0x9ee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xa2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xa6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xaae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xaee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xb2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xb6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xbae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xbee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xe2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + + io_sethandler(0xc2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xc6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xcae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xcee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xd2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xd6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xdae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xdee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xe6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xeae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xeee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xf2e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xf6e8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xfae8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); + io_sethandler(0xfee8, 0x0002, ibm8514_accel_inb, ibm8514_accel_inw, NULL, ibm8514_accel_outb, ibm8514_accel_outw, NULL, svga); +} + +static void +ibm8514_accel_out(uint16_t port, uint32_t val, svga_t *svga, int len) +{ + ibm8514_t *dev = &svga->dev8514; + + if (port & 0x8000) { + ibm8514_accel_out_fifo(dev, port, val, len); + } else { + switch (port) { + case 0x2e8: + if (len == 1) + dev->htotal = (dev->htotal & 0xff00) | val; + else { + dev->htotal = val; + svga_recalctimings(svga); + } + break; + case 0x2e9: + if (len != 1) { + dev->htotal = (dev->htotal & 0xff) | (val << 8); + //pclog("IBM 8514/A: H_TOTAL write 02E8 = %d\n", dev->htotal + 1); + svga_recalctimings(svga); + } + break; + + case 0x6e8: + dev->hdisp = val; + //pclog("IBM 8514/A: H_DISP write 06E8 = %d\n", dev->hdisp + 1); + svga_recalctimings(svga); + break; + + case 0xae8: + //pclog("IBM 8514/A: H_SYNC_STRT write 0AE8 = %d\n", val + 1); + svga_recalctimings(svga); + break; + + case 0xee8: + //pclog("IBM 8514/A: H_SYNC_WID write 0EE8 = %d\n", val + 1); + svga_recalctimings(svga); + break; + + case 0x12e8: + if (len == 1) + dev->vtotal = (dev->vtotal & 0x1f00) | val; + else { + dev->vtotal = val & 0x1fff; + svga_recalctimings(svga); + } + break; + case 0x12e9: + if (len == 1) { + dev->vtotal = (dev->vtotal & 0xff) | ((val & 0x1f) << 8); + //pclog("IBM 8514/A: V_TOTAL write 12E8 = %d\n", dev->vtotal); + svga_recalctimings(svga); + } + break; + + case 0x16e8: + if (len == 1) + dev->vdisp = (dev->vdisp & 0x1f00) | val; + else { + dev->vdisp = val & 0x1fff; + svga_recalctimings(svga); + } + break; + case 0x16e9: + if (len == 1) { + dev->vdisp = (dev->vdisp & 0xff) | ((val & 0x1f) << 8); + //pclog("IBM 8514/A: V_DISP write 16E8 = %d\n", dev->vdisp); + svga_recalctimings(svga); + } + break; + + case 0x1ae8: + if (len == 1) + dev->vsyncstart = (dev->vsyncstart & 0x1f00) | val; + else { + dev->vsyncstart = val & 0x1fff; + svga_recalctimings(svga); + } + break; + case 0x1ae9: + if (len == 1) { + dev->vsyncstart = (dev->vsyncstart & 0xff) | ((val & 0x1f) << 8); + //pclog("IBM 8514/A: V_SYNC_STRT write 1AE8 = %d\n", dev->vsyncstart); + svga_recalctimings(svga); + } + break; + + case 0x1ee8: + dev->vsyncwidth = val; + //pclog("IBM 8514/A: V_SYNC_WID write 1EE8 = %02x\n", val); + svga_recalctimings(svga); + break; + + case 0x22e8: + dev->disp_cntl = val & 0x7e; + dev->interlace = !!(val & 0x10); + //pclog("IBM 8514/A: DISP_CNTL write 22E8 = %02x, SCANMODULOS = %d\n", dev->disp_cntl, dev->scanmodulos); + svga_recalctimings(svga); + break; + + case 0x42e8: + if (len == 1) { + dev->subsys_stat &= ~val; + } else { + dev->subsys_stat &= ~(val & 0xff); + dev->subsys_cntl = (val >> 8); + } + break; + case 0x42e9: + if (len == 1) { + dev->subsys_cntl = val; + } + break; + + case 0x4ae8: + dev->accel.advfunc_cntl = val & 7; + vga_on = ((dev->accel.advfunc_cntl & 1) == 0) ? 1 : 0; + //pclog("IBM 8514/A: VGA ON = %i, val = %02x\n", vga_on, val); + svga_recalctimings(svga); + break; + } + } +} + +static void +ibm8514_accel_outb(uint16_t port, uint8_t val, void *p) +{ + svga_t *svga = (svga_t *)p; + ibm8514_accel_out(port, val, svga, 1); +} + +static void +ibm8514_accel_outw(uint16_t port, uint16_t val, void *p) +{ + svga_t *svga = (svga_t *)p; + ibm8514_accel_out(port, val, svga, 2); +} + +static uint32_t +ibm8514_accel_in(uint16_t port, svga_t *svga, int len) +{ + ibm8514_t *dev = &svga->dev8514; + uint32_t temp = 0; + int cmd; + + switch (port) { + case 0x6e8: + temp = dev->hdisp; + break; + + case 0x22e8: + temp = dev->disp_cntl; + break; + + case 0x26e8: + if (len == 1) + temp = dev->htotal & 0xff; + else + temp = dev->htotal; + break; + case 0x26e9: + if (len == 1) + temp = dev->htotal >> 8; + break; + + case 0x2ee8: + temp = dev->subsys_cntl; + break; + + case 0x42e8: + if (len != 1) { + temp = dev->subsys_stat | 0xa0 | 0x8000; + } else + temp = dev->subsys_stat | 0xa0; + break; + + case 0x42e9: + if (len == 1) + temp |= 0x80; + break; + + case 0x92e8: + if (len != 1) { + temp = dev->test; + } + break; + + case 0x9ae8: + case 0xdae8: + temp = 0; + if (len != 1) { + if (dev->force_busy) + temp |= 0x200; /*Hardware busy*/ + dev->force_busy = 0; + if (dev->data_available) { + temp |= 0x100; /*Read Data available*/ + dev->data_available = 0; + } + } + break; + case 0x9ae9: + case 0xdae9: + temp = 0; + if (len == 1) { + if (dev->force_busy2) + temp |= 2; /*Hardware busy*/ + dev->force_busy2 = 0; + if (dev->data_available2) { + temp |= 1; /*Read Data available*/ + dev->data_available2 = 0; + } + } + break; + + case 0xe2e8: + case 0xe6e8: + if (ibm8514_cpu_dest(dev)) { + if (len == 1) { + ;//READ_PIXTRANS_BYTE_IO(0) + } else { + cmd = (dev->accel.cmd >> 13); + READ_PIXTRANS_WORD(dev->accel.cx, 0) + if (dev->accel.input && !dev->accel.odd_in && !dev->accel.sx) { + temp &= ~0xff00; + temp |= (dev->vram[(dev->accel.newdest_in + dev->accel.cur_x) & dev->vram_mask] << 8); + } + } + ibm8514_accel_out_pixtrans(dev, port, temp, len); + } + break; + case 0xe2e9: + case 0xe6e9: + if (ibm8514_cpu_dest(dev)) { + if (len == 1) { + ;//READ_PIXTRANS_BYTE_IO(1) + ibm8514_accel_out_pixtrans(dev, port, temp, len); + } + } + break; + } + return temp; +} + + +static uint8_t +ibm8514_accel_inb(uint16_t port, void *p) +{ + svga_t *svga = (svga_t *)p; + return ibm8514_accel_in(port, svga, 1); +} + +static uint16_t +ibm8514_accel_inw(uint16_t port, void *p) +{ + svga_t *svga = (svga_t *)p; + return ibm8514_accel_in(port, svga, 2); +} + + +static void +ibm8514_short_stroke_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, ibm8514_t *dev, uint8_t ssv, int len) +{ + if (!cpu_input) { + dev->accel.ssv_len = ssv & 0x0f; + dev->accel.ssv_dir = ssv & 0xe0; + dev->accel.ssv_draw = ssv & 0x10; + + if (ibm8514_cpu_src(dev)) { + return; /*Wait for data from CPU*/ + } + } + + ibm8514_accel_start(count, cpu_input, mix_dat, cpu_dat, dev, len); +} + +static void +ibm8514_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, ibm8514_t *dev, int len) +{ + uint8_t src_dat = 0, dest_dat, old_dest_dat; + int frgd_mix, bkgd_mix; + uint16_t clip_b = dev->accel.multifunc[3] & 0x7ff; + uint16_t clip_r = dev->accel.multifunc[4] & 0x7ff; + int pixcntl = (dev->accel.multifunc[0x0a] >> 6) & 3; + uint8_t mix_mask = 0x80; + uint8_t compare = dev->accel.color_cmp & 0xff; + int compare_mode = dev->accel.multifunc[0x0a] & 0x38; + int cmd = dev->accel.cmd >> 13; + uint8_t wrt_mask = dev->accel.wrt_mask & 0xff; + uint8_t rd_mask = ((dev->accel.rd_mask & 0x01) << 7) | ((dev->accel.rd_mask & 0xfe) >> 1); + uint8_t rd_mask_polygon = dev->accel.rd_mask & 0xff; + uint8_t frgd_color = dev->accel.frgd_color; + uint8_t bkgd_color = dev->accel.bkgd_color; + uint32_t old_mix_dat; + int and3 = dev->accel.cur_x & 3; + uint8_t poly_src = 0; + + if (dev->accel.cmd & 0x100) { + dev->force_busy = 1; + dev->force_busy2 = 1; + } + + frgd_mix = (dev->accel.frgd_mix >> 5) & 3; + bkgd_mix = (dev->accel.bkgd_mix >> 5) & 3; + + if (cpu_input) { + if ((dev->accel.cmd & 2) || (pixcntl == 2)) { + if ((frgd_mix == 2) || (bkgd_mix == 2)) { + count >>= 3; + } else if (pixcntl == 2) { + if (dev->accel.cmd & 2) { + count >>= 1; + } else + count >>= 3; + } + } else { + count >>= 3; + } + } + + if (pixcntl == 1) { + mix_dat = 0; + if (and3 == 3) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x10; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x08; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x04; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x02; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x01; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x80; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x20; + } + if (and3 == 2) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x20; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x10; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x08; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x04; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x02; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x01; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x80; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x40; + } + if (and3 == 1) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x40; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x20; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x10; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x08; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x04; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x02; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x01; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x80; + } + if (and3 == 0) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x80; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x40; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x20; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x10; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x08; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x04; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x02; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x01; + } + } + + old_mix_dat = mix_dat; + + /*Bit 4 of the Command register is the draw yes bit, which enables writing to memory/reading from memory when enabled. + When this bit is disabled, no writing to memory/reading from memory is allowed. (This bit is almost meaningless on + the NOP command)*/ + switch (cmd) { + case 0: /*NOP (Short Stroke Vectors)*/ + if (dev->accel.ssv_state == 0) + break; + + if (dev->accel.cmd & 8) { + while (count-- && dev->accel.ssv_len >= 0) { + if (dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b) { + switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + READ((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + MIX(mix_dat & mix_mask, dest_dat, src_dat); + + if (dev->accel.ssv_draw) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (!dev->accel.ssv_len) + break; + + switch (dev->accel.ssv_dir & 0xe0) { + case 0x00: dev->accel.cx++; break; + case 0x20: dev->accel.cx++; dev->accel.cy--; break; + case 0x40: dev->accel.cy--; break; + case 0x60: dev->accel.cx--; dev->accel.cy--; break; + case 0x80: dev->accel.cx--; break; + case 0xa0: dev->accel.cx--; dev->accel.cy++; break; + case 0xc0: dev->accel.cy++; break; + case 0xe0: dev->accel.cx++; dev->accel.cy++; break; + } + + dev->accel.ssv_len--; + } + + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + } + break; + + case 1: /*Draw line*/ + if (!cpu_input) { + dev->accel.cx = dev->accel.cur_x; + dev->accel.cy = dev->accel.cur_y; + + if (dev->accel.cur_x & 0x400) { + dev->accel.cx |= ~0x3ff; + } + if (dev->accel.cur_y & 0x400) { + dev->accel.cy |= ~0x3ff; + } + + dev->accel.sy = dev->accel.maj_axis_pcnt; + + if (ibm8514_cpu_src(dev)) { + dev->data_available = 0; + dev->data_available2 = 0; + return; /*Wait for data from CPU*/ + } else if (ibm8514_cpu_dest(dev)) { + dev->data_available = 1; + dev->data_available2 = 1; + return; + } + } + + if (dev->accel.cmd & 8) { /*Vector Line*/ + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev) && (pixcntl == 0)) { + mix_dat = mix_mask; /* Mix data = forced to foreground register. */ + } else if (ibm8514_cpu_dest(dev) && (pixcntl == 3)) { + /* Mix data = current video memory value. */ + READ((dev->accel.cy * 1024) + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + + if (ibm8514_cpu_dest(dev)) { + READ((dev->accel.cy * 1024) + dev->accel.cx, src_dat); + if (pixcntl == 3) + src_dat = ((src_dat & rd_mask) == rd_mask); + } else switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + if ((dev->accel.cmd & 4) && dev->accel.sy) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.sy == 0) { + break; + } + + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cx++; break; + case 0x20: dev->accel.cx++; dev->accel.cy--; break; + case 0x40: dev->accel.cy--; break; + case 0x60: dev->accel.cx--; dev->accel.cy--; break; + case 0x80: dev->accel.cx--; break; + case 0xa0: dev->accel.cx--; dev->accel.cy++; break; + case 0xc0: dev->accel.cy++; break; + case 0xe0: dev->accel.cx++; dev->accel.cy++; break; + } + + dev->accel.sy--; + } + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + } else { /*Bresenham*/ + if (pixcntl == 1) { + dev->accel.temp_cnt = 8; + while (count-- && (dev->accel.sy >= 0)) { + if (dev->accel.temp_cnt == 0) { + dev->accel.temp_cnt = 8; + mix_dat = old_mix_dat; + } + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev)) { + READ((dev->accel.cy * 1024) + dev->accel.cx, src_dat); + } else switch ((mix_dat & 1) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & 1, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + if ((dev->accel.cmd & 4) && dev->accel.sy) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } + } + } + + dev->accel.temp_cnt--; + mix_dat >>= 1; + cpu_dat >>= 8; + + if (dev->accel.sy == 0) { + break; + } + + if (dev->accel.err_term >= dev->accel.maj_axis_pcnt) { + dev->accel.err_term += dev->accel.destx_distp; + /*Step minor axis*/ + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cy--; break; + case 0x20: dev->accel.cy--; break; + case 0x40: dev->accel.cx--; break; + case 0x60: dev->accel.cx++; break; + case 0x80: dev->accel.cy++; break; + case 0xa0: dev->accel.cy++; break; + case 0xc0: dev->accel.cx--; break; + case 0xe0: dev->accel.cx++; break; + } + } else + dev->accel.err_term += dev->accel.desty_axstp; + + /*Step major axis*/ + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cx--; break; + case 0x20: dev->accel.cx++; break; + case 0x40: dev->accel.cy--; break; + case 0x60: dev->accel.cy--; break; + case 0x80: dev->accel.cx--; break; + case 0xa0: dev->accel.cx++; break; + case 0xc0: dev->accel.cy++; break; + case 0xe0: dev->accel.cy++; break; + } + + dev->accel.sy--; + } + } else { + while (count-- && (dev->accel.sy >= 0)) { + if (((dev->accel.cx) >= dev->accel.clip_left && (dev->accel.cx) <= clip_r && + (dev->accel.cy) >= dev->accel.clip_top && (dev->accel.cy) <= clip_b)) { + if (ibm8514_cpu_dest(dev) && (pixcntl == 0)) { + mix_dat = mix_mask; /* Mix data = forced to foreground register. */ + } else if (ibm8514_cpu_dest(dev) && (pixcntl == 3)) { + /* Mix data = current video memory value. */ + READ((dev->accel.cy * 1024) + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + + if (ibm8514_cpu_dest(dev)) { + READ((dev->accel.cy * 1024) + dev->accel.cx, src_dat); + if (pixcntl == 3) + src_dat = ((src_dat & rd_mask) == rd_mask); + } else switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + if ((dev->accel.cmd & 4) && dev->accel.sy) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.sy == 0) { + break; + } + + if (dev->accel.err_term >= dev->accel.maj_axis_pcnt) { + dev->accel.err_term += dev->accel.destx_distp; + /*Step minor axis*/ + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cy--; break; + case 0x20: dev->accel.cy--; break; + case 0x40: dev->accel.cx--; break; + case 0x60: dev->accel.cx++; break; + case 0x80: dev->accel.cy++; break; + case 0xa0: dev->accel.cy++; break; + case 0xc0: dev->accel.cx--; break; + case 0xe0: dev->accel.cx++; break; + } + } else + dev->accel.err_term += dev->accel.desty_axstp; + + /*Step major axis*/ + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cx--; break; + case 0x20: dev->accel.cx++; break; + case 0x40: dev->accel.cy--; break; + case 0x60: dev->accel.cy--; break; + case 0x80: dev->accel.cx--; break; + case 0xa0: dev->accel.cx++; break; + case 0xc0: dev->accel.cy++; break; + case 0xe0: dev->accel.cy++; break; + } + + dev->accel.sy--; + } + } + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + } + break; + + case 2: /*Rectangle fill (X direction)*/ + case 3: /*Rectangle fill (Y direction)*/ + case 4: /*Rectangle fill (Y direction using nibbles)*/ + if (!cpu_input) { + dev->accel.x_count = 0; + dev->accel.xx_count = 0; + dev->accel.odd_out = 0; + dev->accel.odd_in = 0; + dev->accel.input = 0; + dev->accel.output = 0; + dev->accel.newdest_out = 0; + dev->accel.newdest_in = 0; + + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + dev->accel.sy = dev->accel.multifunc[0] & 0x7ff; + + dev->accel.cx = dev->accel.cur_x & 0x3ff; + dev->accel.cy = dev->accel.cur_y & 0x3ff; + + if (dev->accel.cur_x & 0x400) + dev->accel.cx |= ~0x3ff; + if (dev->accel.cur_y & 0x400) + dev->accel.cy |= ~0x3ff; + + dev->accel.fill_state = 0; + dev->accel.dest = dev->accel.cy * 1024; + + if (cmd == 4) + dev->accel.cmd |= 2; + else if (cmd == 3) + dev->accel.cmd &= ~2; + + if (ibm8514_cpu_src(dev)) { + if (dev->accel.cmd & 2) { + if (!(dev->accel.cmd & 0x1000)) { + if (!(dev->accel.cmd & 8)) { + dev->accel.sx += and3; + dev->accel.nibbleset = (uint8_t *)calloc(1, (dev->accel.sx >> 3) + 1); + dev->accel.writemono = (uint8_t *)calloc(1, (dev->accel.sx >> 3) + 1); + dev->accel.sys_cnt = (dev->accel.sx >> 3) + 1; + } else { + if (and3 == 1) { + dev->accel.sx += 4; + switch (dev->accel.cmd & 0xe0) { + case 0x00: + case 0x20: + case 0xe0: + dev->accel.cx -= 4; + break; + case 0x60: + case 0x80: + case 0xa0: + dev->accel.cx += 4; + break; + } + } else if (and3 == 2) { + dev->accel.sx += 5; + switch (dev->accel.cmd & 0xe0) { + case 0x00: + case 0x20: + case 0xe0: + dev->accel.cx -= 5; + break; + case 0x60: + case 0x80: + case 0xa0: + dev->accel.cx += 5; + break; + } + } else if (and3 == 3) { + dev->accel.sx += 6; + switch (dev->accel.cmd & 0xe0) { + case 0x00: + case 0x20: + case 0xe0: + dev->accel.cx -= 6; + break; + case 0x60: + case 0x80: + case 0xa0: + dev->accel.cx += 6; + break; + } + } else { + dev->accel.sx += 3; + switch (dev->accel.cmd & 0xe0) { + case 0x00: + case 0x20: + case 0xe0: + dev->accel.cx -= 3; + break; + case 0x60: + case 0x80: + case 0xa0: + dev->accel.cx += 3; + break; + } + } + } + } + } else { + if (!(dev->accel.cmd & 0x40) && (frgd_mix == 2) && (bkgd_mix == 2) && (pixcntl == 0) && (cmd == 2)) { + if (!(dev->accel.sx & 1)) { + dev->accel.output = 1; + dev->accel.newdest_out = (dev->accel.cy + 1) * 1024; + } + } + } + dev->data_available = 0; + dev->data_available2 = 0; + return; /*Wait for data from CPU*/ + } else if (ibm8514_cpu_dest(dev)) { + if (!(dev->accel.cmd & 2) && (frgd_mix == 2) && (pixcntl == 0) && (cmd == 2)) { + if (!(dev->accel.sx & 1)) { + dev->accel.input = 1; + dev->accel.newdest_in = (dev->accel.cy + 1) * 1024; + } + } + dev->data_available = 1; + dev->data_available2 = 1; + return; /*Wait for data from CPU*/ + } + } + + if (dev->accel.cmd & 2) { + if (cpu_input) { +rect_fill_pix: + if (dev->accel.cmd & 8) { + dev->accel.xx_count++; + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev) && (pixcntl == 0)) { + mix_dat = mix_dat; /* Mix data = forced to foreground register. */ + } else if (ibm8514_cpu_dest(dev) && (pixcntl == 3)) { + /* Mix data = current video memory value. */ + READ(dev->accel.dest + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + + if (ibm8514_cpu_dest(dev)) { + READ(dev->accel.dest + dev->accel.cx, src_dat); + if (pixcntl == 3) + src_dat = ((src_dat & rd_mask) == rd_mask); + } else switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + if (and3 == 1) { + if (dev->accel.xx_count >= 2) { + if ((dev->accel.cmd & 4) && dev->accel.sx) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } else if (and3 == 2) { + if (dev->accel.xx_count == 2) { + if (count <= 2) { + if ((dev->accel.cmd & 4) && dev->accel.sx) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } else if (dev->accel.xx_count >= 3) { + if ((dev->accel.cmd & 4) && dev->accel.sx) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } else if (and3 == 3) { + if (dev->accel.xx_count == 2) { + if (count <= 1) { + if ((dev->accel.cmd & 4) && dev->accel.sx) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } else if (dev->accel.xx_count >= 3) { + if ((dev->accel.cmd & 4) && dev->accel.sx) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } else { + if (dev->accel.xx_count == 1) { + if (!count) { + if ((dev->accel.cmd & 4) && dev->accel.sx) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } else if (dev->accel.xx_count >= 2) { + if ((dev->accel.cmd & 4) && dev->accel.sx) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + switch (dev->accel.cmd & 0xe0) { + case 0x00: + case 0x20: + case 0xe0: + dev->accel.cx++; + break; + case 0x60: + case 0x80: + case 0xa0: + dev->accel.cx--; + break; + } + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + if (and3 == 1) { + dev->accel.sx += 4; + } else if (and3 == 2) { + dev->accel.sx += 5; + } else if (and3 == 3) { + dev->accel.sx += 6; + } else { + dev->accel.sx += 3; + } + + switch (dev->accel.cmd & 0xe0) { + case 0x00: + case 0x20: + case 0xe0: + dev->accel.cx -= (dev->accel.sx + 1); + break; + case 0x60: + case 0x80: + case 0xa0: + dev->accel.cx += (dev->accel.sx + 1); + break; + } + + switch (dev->accel.cmd & 0xe0) { + case 0x20: + case 0x40: + case 0x60: + dev->accel.cy--; + break; + case 0xa0: + case 0xc0: + case 0xe0: + dev->accel.cy++; + break; + } + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + return; + } + } + break; + } + if (count < 8) { + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev) && (pixcntl == 0)) { + mix_dat = mix_mask; /* Mix data = forced to foreground register. */ + } else if (ibm8514_cpu_dest(dev) && (pixcntl == 3)) { + /* Mix data = current video memory value. */ + READ(dev->accel.dest + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + + if (ibm8514_cpu_dest(dev)) { + READ(dev->accel.dest + dev->accel.cx, src_dat); + if (pixcntl == 3) + src_dat = ((src_dat & rd_mask) == rd_mask); + } else switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 2) { + dev->accel.sx += (dev->accel.cur_x & 3); + } + + if (dev->accel.cmd & 0x20) { + dev->accel.cx -= (dev->accel.sx) + 1; + } else + dev->accel.cx += (dev->accel.sx) + 1; + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + return; + } + } + } else { + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev) && (pixcntl == 0)) { + mix_dat = 1; /* Mix data = forced to foreground register. */ + } else if (ibm8514_cpu_dest(dev) && (pixcntl == 3)) { + /* Mix data = current video memory value. */ + READ(dev->accel.dest + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? 1 : 0; + } + + if (ibm8514_cpu_dest(dev)) { + READ(dev->accel.dest + dev->accel.cx, src_dat); + if (pixcntl == 3) + src_dat = ((src_dat & rd_mask) == rd_mask); + } else { + switch ((mix_dat & 1) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + } + + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & 1, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + mix_dat >>= 1; + cpu_dat >>= 8; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 2) { + if (!(dev->accel.cmd & 0x1000)) + dev->accel.sx += (dev->accel.cur_x & 3); + } + + if (dev->accel.cmd & 0x20) { + dev->accel.cx -= (dev->accel.sx) + 1; + } else + dev->accel.cx += (dev->accel.sx) + 1; + + if (dev->accel.cmd & 2) { + if (dev->accel.cmd & 0x1000) { + dev->accel.cx = dev->accel.cur_x & 0x3ff; + if (dev->accel.cur_x & 0x400) + dev->accel.cx |= ~0x3ff; + } + } + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + return; + } + } + } + } else { + goto rect_fill; + } + } else { + if (cpu_input) { + if (pixcntl == 2) { + goto rect_fill_pix; + } else { + if (dev->accel.input && !dev->accel.output) { + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + mix_dat = mix_mask; /* Mix data = forced to foreground register. */ + if (!dev->accel.odd_in && !dev->accel.sx) { + READ(dev->accel.newdest_in + dev->accel.cur_x, src_dat); + READ(dev->accel.newdest_in + dev->accel.cur_x, dest_dat); + } else { + READ(dev->accel.dest + dev->accel.cx, src_dat); + READ(dev->accel.dest + dev->accel.cx, dest_dat); + } + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + if (!dev->accel.odd_in && !dev->accel.sx) { + WRITE(dev->accel.newdest_in + dev->accel.cur_x, dest_dat); + } else { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.odd_in) { + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + dev->accel.odd_in = 0; + dev->accel.cx = dev->accel.cur_x; + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.newdest_in = (dev->accel.cy + 1) * 1024; + dev->accel.sy--; + return; + } + } else { + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + dev->accel.sx--; + dev->accel.cx = dev->accel.cur_x; + dev->accel.odd_in = 1; + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.newdest_in = (dev->accel.cy + 1) * 1024; + dev->accel.sy--; + return; + } + } + } + } else if (dev->accel.output && !dev->accel.input) { + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + src_dat = cpu_dat; + if (!dev->accel.odd_out && !dev->accel.sx) { + READ(dev->accel.newdest_out + dev->accel.cur_x, dest_dat); + } else { + READ(dev->accel.dest + dev->accel.cx, dest_dat); + } + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + if (!dev->accel.odd_out && !dev->accel.sx) { + WRITE(dev->accel.newdest_out + dev->accel.cur_x, dest_dat); + } else { + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.odd_out) { + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + dev->accel.odd_out = 0; + dev->accel.cx = dev->accel.cur_x; + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.newdest_out = (dev->accel.cy + 1) * 1024; + dev->accel.sy--; + return; + } + } else { + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + dev->accel.odd_out = 1; + dev->accel.sx--; + dev->accel.cx = dev->accel.cur_x; + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.newdest_out = (dev->accel.cy + 1) * 1024; + dev->accel.sy--; + return; + } + } + } + } else { + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev) && (pixcntl == 0)) { + mix_dat = mix_mask; /* Mix data = forced to foreground register. */ + } else if (ibm8514_cpu_dest(dev) && (pixcntl == 3)) { + /* Mix data = current video memory value. */ + READ(dev->accel.dest + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + + if (ibm8514_cpu_dest(dev)) { + READ(dev->accel.dest + dev->accel.cx, src_dat); + if (pixcntl == 3) { + src_dat = ((src_dat & rd_mask) == rd_mask); + } + } else switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + if (ibm8514_cpu_dest(dev)) { + if (pixcntl == 3) { + MIX(mix_dat & mix_mask, dest_dat, src_dat); + } + } else { + MIX(mix_dat & mix_mask, dest_dat, src_dat); + } + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.cx -= (dev->accel.sx) + 1; + } else + dev->accel.cx += (dev->accel.sx) + 1; + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + return; + } + } + } + } + } else { +rect_fill: + if (pixcntl == 1) { + if (dev->accel.cmd & 0x40) { + count = dev->accel.maj_axis_pcnt + 1; + dev->accel.temp_cnt = 8; + while (count-- && dev->accel.sy >= 0) { + if (dev->accel.temp_cnt == 0) { + mix_dat >>= 8; + dev->accel.temp_cnt = 8; + } + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = 0; break; + case 3: src_dat = 0; break; + } + + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + + if (dev->accel.temp_cnt > 0) { + dev->accel.temp_cnt--; + mix_dat <<= 1; + mix_dat |= 1; + } + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.cx -= (dev->accel.sx) + 1; + } else + dev->accel.cx += (dev->accel.sx) + 1; + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + return; + } + } + } else { + dev->accel.temp_cnt = 8; + while (count-- && dev->accel.sy >= 0) { + if (dev->accel.temp_cnt == 0) { + dev->accel.temp_cnt = 8; + mix_dat = old_mix_dat; + } + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + switch ((mix_dat & 1) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = 0; break; + case 3: src_dat = 0; break; + } + + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & 1, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + + dev->accel.temp_cnt--; + mix_dat >>= 1; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.cx -= (dev->accel.sx) + 1; + } else + dev->accel.cx += (dev->accel.sx) + 1; + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + + if (dev->accel.sy < 0) { + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + return; + } + } + } + } + } else { + if (dev->accel.multifunc[0x0a] & 4) { + while (count-- && dev->accel.sy >= 0) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = 0; break; + case 3: src_dat = 0; break; + } + + READ(dev->accel.dest + dev->accel.cx, poly_src); + if (dev->accel.multifunc[0x0a] & 2) { + poly_src = ((poly_src & wrt_mask) == wrt_mask); + } else { + poly_src = ((poly_src & rd_mask_polygon) == rd_mask_polygon); + } + + if (poly_src) + dev->accel.fill_state = !dev->accel.fill_state; + + if (dev->accel.fill_state) { + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.fill_state = 0; + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.cx -= (dev->accel.sx) + 1; + } else + dev->accel.cx += (dev->accel.sx) + 1; + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + + if (dev->accel.sy < 0) { + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + return; + } + } + } + } else { + while (count-- && dev->accel.sy >= 0) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = 0; break; + case 3: src_dat = 0; break; + } + + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + + mix_dat <<= 1; + mix_dat |= 1; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.cx -= (dev->accel.sx) + 1; + } else + dev->accel.cx += (dev->accel.sx) + 1; + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + + if (dev->accel.sy < 0) { + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + return; + } + } + } + } + } + } + } + break; + + case 5: /*Draw Polygon Boundary Line*/ + if (!cpu_input) { + dev->accel.cx = dev->accel.cur_x; + dev->accel.cy = dev->accel.cur_y; + + if (dev->accel.cur_x & 0x400) { + if (dev->accel.cx >= 1024) { + dev->accel.cx = 0; + } else + dev->accel.cx |= ~0x3ff; + } + + if (dev->accel.cur_y & 0x400) { + if (dev->accel.cy >= 1024) + dev->accel.cy = 1; + else + dev->accel.cy |= ~0x3ff; + } + + dev->accel.sy = dev->accel.maj_axis_pcnt; + + if (ibm8514_cpu_src(dev)) { + dev->data_available = 0; + dev->data_available2 = 0; + return; /*Wait for data from CPU*/ + } else if (ibm8514_cpu_dest(dev)) { + dev->data_available = 1; + dev->data_available2 = 1; + return; + } + } + + if (dev->accel.cmd & 8) { /*Vector Line*/ + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev) && (pixcntl == 0)) { + mix_dat = mix_mask; /* Mix data = forced to foreground register. */ + } else if (ibm8514_cpu_dest(dev) && (pixcntl == 3)) { + /* Mix data = current video memory value. */ + READ((dev->accel.cy * 1024) + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + + if (ibm8514_cpu_dest(dev)) { + READ((dev->accel.cy * 1024) + dev->accel.cx, src_dat); + if (pixcntl == 3) + src_dat = ((src_dat & rd_mask) == rd_mask); + } else switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + + if ((dev->accel.cmd & 4) && dev->accel.sy) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.sy == 0) { + break; + } + + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cx++; break; + case 0x20: dev->accel.cx++; dev->accel.cy--; break; + case 0x40: dev->accel.cy--; break; + case 0x60: dev->accel.cx--; dev->accel.cy--; break; + case 0x80: dev->accel.cx--; break; + case 0xa0: dev->accel.cx--; dev->accel.cy++; break; + case 0xc0: dev->accel.cy++; break; + case 0xe0: dev->accel.cx++; dev->accel.cy++; break; + } + + dev->accel.sy--; + } + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + } else { /*Bresenham*/ + if (pixcntl == 1) { + dev->accel.temp_cnt = 8; + while (count-- && (dev->accel.sy >= 0)) { + if (dev->accel.temp_cnt == 0) { + dev->accel.temp_cnt = 8; + mix_dat = old_mix_dat; + } + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev)) { + READ((dev->accel.cy * 1024) + dev->accel.cx, src_dat); + } else switch ((mix_dat & 1) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & 1, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + if ((dev->accel.cmd & 4) && dev->accel.sy) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } + } + } + + dev->accel.temp_cnt--; + mix_dat >>= 1; + cpu_dat >>= 8; + + if (dev->accel.sy == 0) { + break; + } + + if (dev->accel.err_term >= dev->accel.maj_axis_pcnt) { + dev->accel.err_term += dev->accel.destx_distp; + /*Step minor axis*/ + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cy--; break; + case 0x20: dev->accel.cy--; break; + case 0x40: dev->accel.cx--; break; + case 0x60: dev->accel.cx++; break; + case 0x80: dev->accel.cy++; break; + case 0xa0: dev->accel.cy++; break; + case 0xc0: dev->accel.cx--; break; + case 0xe0: dev->accel.cx++; break; + } + } else + dev->accel.err_term += dev->accel.desty_axstp; + + /*Step major axis*/ + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cx--; break; + case 0x20: dev->accel.cx++; break; + case 0x40: dev->accel.cy--; break; + case 0x60: dev->accel.cy--; break; + case 0x80: dev->accel.cx--; break; + case 0xa0: dev->accel.cx++; break; + case 0xc0: dev->accel.cy++; break; + case 0xe0: dev->accel.cy++; break; + } + + dev->accel.sy--; + } + } else { + while (count-- && (dev->accel.sy >= 0)) { + if (dev->accel.cur_x >= 1024) { + dev->accel.cx = 0; + } + if (dev->accel.cur_y >= 1024) { + dev->accel.cy = 1; + } + + if ((dev->accel.cx >= dev->accel.clip_left && dev->accel.cx <= clip_r && + dev->accel.cy >= dev->accel.clip_top && dev->accel.cy <= clip_b)) { + if (ibm8514_cpu_dest(dev) && (pixcntl == 0)) { + mix_dat = mix_mask; /* Mix data = forced to foreground register. */ + } else if (ibm8514_cpu_dest(dev) && (pixcntl == 3)) { + /* Mix data = current video memory value. */ + READ((dev->accel.cy * 1024) + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + + if (ibm8514_cpu_dest(dev)) { + READ((dev->accel.cy * 1024) + dev->accel.cx, src_dat); + if (pixcntl == 3) + src_dat = ((src_dat & rd_mask) == rd_mask); + } else switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: src_dat = 0; break; + } + + READ((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + if ((dev->accel.cmd & 4) && dev->accel.sy) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } else if (!(dev->accel.cmd & 4)) { + WRITE((dev->accel.cy * 1024) + dev->accel.cx, dest_dat); + } + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.sy == 0) { + break; + } + + if (dev->accel.err_term >= dev->accel.maj_axis_pcnt) { + dev->accel.err_term += dev->accel.destx_distp; + /*Step minor axis*/ + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cy--; break; + case 0x20: dev->accel.cy--; break; + case 0x40: dev->accel.cx--; break; + case 0x60: dev->accel.cx++; break; + case 0x80: dev->accel.cy++; break; + case 0xa0: dev->accel.cy++; break; + case 0xc0: dev->accel.cx--; break; + case 0xe0: dev->accel.cx++; break; + } + } else + dev->accel.err_term += dev->accel.desty_axstp; + + /*Step major axis*/ + switch (dev->accel.cmd & 0xe0) { + case 0x00: dev->accel.cx--; break; + case 0x20: dev->accel.cx++; break; + case 0x40: dev->accel.cy--; break; + case 0x60: dev->accel.cy--; break; + case 0x80: dev->accel.cx--; break; + case 0xa0: dev->accel.cx++; break; + case 0xc0: dev->accel.cy++; break; + case 0xe0: dev->accel.cy++; break; + } + + dev->accel.sy--; + } + } + dev->accel.cur_x = dev->accel.cx; + dev->accel.cur_y = dev->accel.cy; + } + break; + + case 6: /*BitBlt*/ + if (!cpu_input) /*!cpu_input is trigger to start operation*/ + { + dev->accel.x_count = 0; + dev->accel.output = 0; + + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + dev->accel.sy = dev->accel.multifunc[0] & 0x7ff; + + dev->accel.dx = dev->accel.destx_distp & 0x3ff; + dev->accel.dy = dev->accel.desty_axstp & 0x3ff; + + if (dev->accel.destx_distp & 0x400) + dev->accel.dx |= ~0x3ff; + if (dev->accel.desty_axstp & 0x400) + dev->accel.dy |= ~0x3ff; + + dev->accel.cx = dev->accel.cur_x & 0x3ff; + dev->accel.cy = dev->accel.cur_y & 0x3ff; + + if (dev->accel.cur_x & 0x400) + dev->accel.cx |= ~0x3ff; + if (dev->accel.cur_y & 0x400) + dev->accel.cy |= ~0x3ff; + + dev->accel.src = dev->accel.cy * 1024; + dev->accel.dest = dev->accel.dy * 1024; + + if (ibm8514_cpu_src(dev)) { + if (dev->accel.cmd & 2) { + if (!(dev->accel.cmd & 0x1000)) { + dev->accel.sx += (dev->accel.cur_x & 3); + dev->accel.nibbleset = (uint8_t *)calloc(1, (dev->accel.sx >> 3) + 1); + dev->accel.writemono = (uint8_t *)calloc(1, (dev->accel.sx >> 3) + 1); + dev->accel.sys_cnt = (dev->accel.sx >> 3) + 1; + } + } + dev->data_available = 0; + dev->data_available2 = 0; + return; /*Wait for data from CPU*/ + } else if (ibm8514_cpu_dest(dev)) { + dev->data_available = 1; + dev->data_available2 = 1; + return; /*Wait for data from CPU*/ + } + } + + if (dev->accel.cmd & 2) { + if (cpu_input) { +bitblt_pix: + if (count < 8) { + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.dx >= dev->accel.clip_left && dev->accel.dx <= clip_r && + dev->accel.dy >= dev->accel.clip_top && dev->accel.dy <= clip_b)) { + if (pixcntl == 3) { + if (!(dev->accel.cmd & 0x10) && ((frgd_mix != 3) || (bkgd_mix != 3))) { + READ(dev->accel.src + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } else if (dev->accel.cmd & 0x10) { + READ(dev->accel.src + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + } + switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: READ(dev->accel.src + dev->accel.cx, src_dat); + if (pixcntl == 3) { + if (dev->accel.cmd & 0x10) { + src_dat = ((src_dat & rd_mask) == rd_mask); + } + } + break; + } + + READ(dev->accel.dest + dev->accel.cx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + WRITE(dev->accel.dest + dev->accel.cx, dest_dat); + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.cmd & 0x20) + dev->accel.cx++; + else + dev->accel.cx--; + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 2) { + dev->accel.sx += (dev->accel.cur_x & 3); + } + + if (dev->accel.cmd & 0x20) { + dev->accel.cx -= (dev->accel.sx) + 1; + } else + dev->accel.cx += (dev->accel.sx) + 1; + + if (dev->accel.cmd & 0x80) + dev->accel.cy++; + else + dev->accel.cy--; + + dev->accel.dest = dev->accel.cy * 1024; + dev->accel.sy--; + return; + } + } + } else { + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.dx >= dev->accel.clip_left && dev->accel.dx <= clip_r && + dev->accel.dy >= dev->accel.clip_top && dev->accel.dy <= clip_b)) { + if (pixcntl == 3) { + if (!(dev->accel.cmd & 0x10) && ((frgd_mix != 3) || (bkgd_mix != 3))) { + READ(dev->accel.src + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? 1 : 0; + } else if (dev->accel.cmd & 0x10) { + READ(dev->accel.src + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? 1 : 0; + } + } + switch ((mix_dat & 1) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: READ(dev->accel.src + dev->accel.cx, src_dat); + if (pixcntl == 3) { + if (dev->accel.cmd & 0x10) { + src_dat = ((src_dat & rd_mask) == rd_mask); + } + } + break; + } + + READ(dev->accel.dest + dev->accel.dx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & 1, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + WRITE(dev->accel.dest + dev->accel.dx, dest_dat); + } + } + mix_dat >>= 1; + cpu_dat >>= 8; + + if (dev->accel.cmd & 0x20) { + dev->accel.dx++; + dev->accel.cx++; + } else { + dev->accel.dx--; + dev->accel.cx--; + } + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 2) { + if (!(dev->accel.cmd & 0x1000)) + dev->accel.sx += (dev->accel.cur_x & 3); + } + + if (dev->accel.cmd & 0x20) { + dev->accel.dx -= (dev->accel.sx) + 1; + dev->accel.cx -= (dev->accel.sx) + 1; + } else { + dev->accel.dx += (dev->accel.sx) + 1; + dev->accel.cx += (dev->accel.sx) + 1; + } + + if (dev->accel.cmd & 2) { + if (dev->accel.cmd & 0x1000) { + dev->accel.cx = dev->accel.cur_x & 0x3ff; + if (dev->accel.cur_x & 0x400) + dev->accel.cx |= ~0x3ff; + dev->accel.dx = dev->accel.destx_distp & 0x3ff; + if (dev->accel.destx_distp & 0x400) + dev->accel.dx |= ~0x3ff; + } + } + + if (dev->accel.cmd & 0x80) { + dev->accel.dy++; + dev->accel.cy++; + } else { + dev->accel.dy--; + dev->accel.cy--; + } + + dev->accel.dest = dev->accel.dy * 1024; + dev->accel.src = dev->accel.cy * 1024; + dev->accel.sy--; + return; + } + } + } + } else { + goto bitblt; + } + } else { + if (cpu_input) { + if (pixcntl == 2) { + goto bitblt_pix; + } else { + while (count-- && (dev->accel.sy >= 0)) { + if ((dev->accel.dx >= dev->accel.clip_left && dev->accel.dx <= clip_r && + dev->accel.dy >= dev->accel.clip_top && dev->accel.dy <= clip_b)) { + if (pixcntl == 3) { + if (!(dev->accel.cmd & 0x10) && ((frgd_mix != 3) || (bkgd_mix != 3))) { + READ(dev->accel.src + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } else if (dev->accel.cmd & 0x10) { + READ(dev->accel.src + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + } + switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = cpu_dat & 0xff; break; + case 3: READ(dev->accel.src + dev->accel.cx, src_dat); + if (pixcntl == 3) { + if (dev->accel.cmd & 0x10) { + src_dat = ((src_dat & rd_mask) == rd_mask); + } + } + break; + } + + READ(dev->accel.dest + dev->accel.dx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + WRITE(dev->accel.dest + dev->accel.dx, dest_dat); + } + } + + mix_dat <<= 1; + mix_dat |= 1; + cpu_dat >>= 8; + + if (dev->accel.cmd & 0x20) { + dev->accel.dx++; + dev->accel.cx++; + } else { + dev->accel.dx--; + dev->accel.cx--; + } + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.dx -= (dev->accel.sx) + 1; + dev->accel.cx -= (dev->accel.sx) + 1; + } else { + dev->accel.dx += (dev->accel.sx) + 1; + dev->accel.cx += (dev->accel.sx) + 1; + } + + if (dev->accel.cmd & 0x80) { + dev->accel.dy++; + dev->accel.cy++; + } else { + dev->accel.dy--; + dev->accel.cy--; + } + + dev->accel.dest = dev->accel.dy * 1024; + dev->accel.src = dev->accel.cy * 1024; + dev->accel.sy--; + return; + } + } + } + } else { +bitblt: + if (pixcntl == 1) { + if (dev->accel.cmd & 0x40) { + count = dev->accel.maj_axis_pcnt + 1; + dev->accel.temp_cnt = 8; + while (count-- && dev->accel.sy >= 0) { + if (dev->accel.temp_cnt == 0) { + mix_dat >>= 8; + dev->accel.temp_cnt = 8; + } + if ((dev->accel.dx >= dev->accel.clip_left && dev->accel.dx <= clip_r && + dev->accel.dy >= dev->accel.clip_top && dev->accel.dy <= clip_b)) { + switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = 0; break; + case 3: READ(dev->accel.src + dev->accel.cx, src_dat); + break; + } + + READ(dev->accel.dest + dev->accel.dx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + + WRITE(dev->accel.dest + dev->accel.dx, dest_dat); + } + } + + if (dev->accel.temp_cnt > 0) { + dev->accel.temp_cnt--; + mix_dat <<= 1; + mix_dat |= 1; + } + + if (dev->accel.cmd & 0x20) { + dev->accel.dx++; + dev->accel.cx++; + } else { + dev->accel.dx--; + dev->accel.cx--; + } + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.dx -= (dev->accel.sx) + 1; + dev->accel.cx -= (dev->accel.sx) + 1; + } else { + dev->accel.dx += (dev->accel.sx) + 1; + dev->accel.cx += (dev->accel.sx) + 1; + } + + if (dev->accel.cmd & 0x80) { + dev->accel.dy++; + dev->accel.cy++; + } else { + dev->accel.dy--; + dev->accel.cy--; + } + + dev->accel.dest = dev->accel.dy * 1024; + dev->accel.src = dev->accel.cy * 1024; + dev->accel.sy--; + return; + } + } + } else { + dev->accel.temp_cnt = 8; + while (count-- && dev->accel.sy >= 0) { + if (dev->accel.temp_cnt == 0) { + dev->accel.temp_cnt = 8; + mix_dat = old_mix_dat; + } + if ((dev->accel.dx >= dev->accel.clip_left && dev->accel.dx <= clip_r && + dev->accel.dy >= dev->accel.clip_top && dev->accel.dy <= clip_b)) { + switch ((mix_dat & 1) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = 0; break; + case 3: READ(dev->accel.src + dev->accel.cx, src_dat); + break; + } + + READ(dev->accel.dest + dev->accel.dx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & 1, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + + WRITE(dev->accel.dest + dev->accel.dx, dest_dat); + } + } + + dev->accel.temp_cnt--; + mix_dat >>= 1; + + if (dev->accel.cmd & 0x20) { + dev->accel.dx++; + dev->accel.cx++; + } else { + dev->accel.dx--; + dev->accel.cx--; + } + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.dx -= (dev->accel.sx) + 1; + dev->accel.cx -= (dev->accel.sx) + 1; + } else { + dev->accel.dx += (dev->accel.sx) + 1; + dev->accel.cx += (dev->accel.sx) + 1; + } + + if (dev->accel.cmd & 0x80) { + dev->accel.dy++; + dev->accel.cy++; + } else { + dev->accel.dy--; + dev->accel.cy--; + } + + dev->accel.dest = dev->accel.dy * 1024; + dev->accel.src = dev->accel.cy * 1024; + dev->accel.sy--; + + if (dev->accel.sy < 0) { + return; + } + } + } + } + } else { + while (count-- && dev->accel.sy >= 0) { + if ((dev->accel.dx >= dev->accel.clip_left && dev->accel.dx <= clip_r && + dev->accel.dy >= dev->accel.clip_top && dev->accel.dy <= clip_b)) { + if (pixcntl == 3) { + if (!(dev->accel.cmd & 0x10) && ((frgd_mix != 3) || (bkgd_mix != 3))) { + READ(dev->accel.src + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } else if (dev->accel.cmd & 0x10) { + READ(dev->accel.src + dev->accel.cx, mix_dat); + mix_dat = ((mix_dat & rd_mask) == rd_mask); + mix_dat = mix_dat ? mix_mask : 0; + } + } + switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { + case 0: src_dat = bkgd_color; break; + case 1: src_dat = frgd_color; break; + case 2: src_dat = 0; break; + case 3: READ(dev->accel.src + dev->accel.cx, src_dat); + if (pixcntl == 3) { + if (dev->accel.cmd & 0x10) { + src_dat = ((src_dat & rd_mask) == rd_mask); + } + } + break; + } + + READ(dev->accel.dest + dev->accel.dx, dest_dat); + + if ((compare_mode == 0) || + ((compare_mode == 0x10) && (dest_dat >= compare)) || + ((compare_mode == 0x18) && (dest_dat < compare)) || + ((compare_mode == 0x20) && (dest_dat != compare)) || + ((compare_mode == 0x28) && (dest_dat == compare)) || + ((compare_mode == 0x30) && (dest_dat <= compare)) || + ((compare_mode == 0x38) && (dest_dat > compare))) { + old_dest_dat = dest_dat; + MIX(mix_dat & mix_mask, dest_dat, src_dat); + dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); + + WRITE(dev->accel.dest + dev->accel.dx, dest_dat); + } + } + + mix_dat <<= 1; + mix_dat |= 1; + + if (dev->accel.cmd & 0x20) { + dev->accel.dx++; + dev->accel.cx++; + } else { + dev->accel.dx--; + dev->accel.cx--; + } + + dev->accel.sx--; + if (dev->accel.sx < 0) { + dev->accel.sx = dev->accel.maj_axis_pcnt & 0x7ff; + + if (dev->accel.cmd & 0x20) { + dev->accel.dx -= (dev->accel.sx) + 1; + dev->accel.cx -= (dev->accel.sx) + 1; + } else { + dev->accel.dx += (dev->accel.sx) + 1; + dev->accel.cx += (dev->accel.sx) + 1; + } + + if (dev->accel.cmd & 0x80) { + dev->accel.dy++; + dev->accel.cy++; + } else { + dev->accel.dy--; + dev->accel.cy--; + } + + dev->accel.dest = dev->accel.dy * 1024; + dev->accel.src = dev->accel.cy * 1024; + dev->accel.sy--; + + if (dev->accel.sy < 0) { + return; + } + } + } + } + } + } + break; + } +} + +static void +ibm8514_render_8bpp(svga_t *svga) +{ + ibm8514_t *dev = &svga->dev8514; + int x; + uint32_t *p; + uint32_t dat; + + if ((dev->displine + svga->y_add) < 0) { + return; + } + + if (dev->changedvram[dev->ma >> 12] || dev->changedvram[(dev->ma >> 12) + 1] || svga->fullchange) { + p = &buffer32->line[dev->displine + svga->y_add][svga->x_add]; + + if (dev->firstline_draw == 2000) + dev->firstline_draw = dev->displine; + dev->lastline_draw = dev->displine; + + for (x = 0; x <= dev->h_disp; x += 8) { + dat = *(uint32_t *)(&dev->vram[dev->ma & dev->vram_mask]); + p[0] = dev->map8[dat & 0xff]; + p[1] = dev->map8[(dat >> 8) & 0xff]; + p[2] = dev->map8[(dat >> 16) & 0xff]; + p[3] = dev->map8[(dat >> 24) & 0xff]; + + dat = *(uint32_t *)(&dev->vram[(dev->ma + 4) & dev->vram_mask]); + p[4] = dev->map8[dat & 0xff]; + p[5] = dev->map8[(dat >> 8) & 0xff]; + p[6] = dev->map8[(dat >> 16) & 0xff]; + p[7] = dev->map8[(dat >> 24) & 0xff]; + + dev->ma += 8; + p += 8; + } + dev->ma &= dev->vram_mask; + } +} + +static void +ibm8514_render_overscan_left(ibm8514_t *dev, svga_t *svga) +{ + int i; + + if ((dev->displine + svga->y_add) < 0) + return; + + if (svga->scrblank || (dev->h_disp == 0)) + return; + + for (i = 0; i < svga->x_add; i++) + buffer32->line[dev->displine + svga->y_add][i] = svga->overscan_color; +} + + +static void +ibm8514_render_overscan_right(ibm8514_t *dev, svga_t *svga) +{ + int i, right; + + if ((dev->displine + svga->y_add) < 0) + return; + + if (svga->scrblank || (dev->h_disp == 0)) + return; + + right = (overscan_x >> 1); + for (i = 0; i < right; i++) + buffer32->line[dev->displine + svga->y_add][svga->x_add + dev->h_disp + i] = svga->overscan_color; +} + +static void +ibm8514_doblit(int wx, int wy, ibm8514_t *dev, svga_t *svga) +{ + int y_add, x_add, y_start, x_start, bottom; + uint32_t *p; + int i, j; + int xs_temp, ys_temp; + + y_add = (enable_overscan) ? overscan_y : 0; + x_add = (enable_overscan) ? overscan_x : 0; + y_start = (enable_overscan) ? 0 : (overscan_y >> 1); + x_start = (enable_overscan) ? 0 : (overscan_x >> 1); + bottom = (overscan_y >> 1) + (svga->crtc[8] & 0x1f); + + if ((wx <= 0) || (wy <= 0)) + return; + + xs_temp = wx; + ys_temp = wy + 1; + if (xs_temp < 64) + xs_temp = 640; + if (ys_temp < 32) + ys_temp = 200; + + if ((svga->crtc[0x17] & 0x80) && ((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get())) { + /* Screen res has changed.. fix up, and let them know. */ + xsize = xs_temp; + ysize = ys_temp; + + if ((xsize > 1984) || (ysize > 2016)) { + /* 2048x2048 is the biggest safe render texture, to account for overscan, + we suppress overscan starting from x 1984 and y 2016. */ + x_add = 0; + y_add = 0; + suppress_overscan = 1; + } else + suppress_overscan = 0; + + /* Block resolution changes while in DPMS mode to avoid getting a bogus + screen width (320). We're already rendering a blank screen anyway. */ + set_screen_size(xsize + x_add, ysize + y_add); + + if (video_force_resize_get()) + video_force_resize_set(0); + } + + if ((wx >= 160) && ((wy + 1) >= 120)) { + /* Draw (overscan_size - scroll size) lines of overscan on top and bottom. */ + for (i = 0; i < svga->y_add; i++) { + p = &buffer32->line[i & 0x7ff][0]; + + for (j = 0; j < (xsize + x_add); j++) + p[j] = svga->overscan_color; + } + + for (i = 0; i < bottom; i++) { + p = &buffer32->line[(ysize + svga->y_add + i) & 0x7ff][0]; + + for (j = 0; j < (xsize + x_add); j++) + p[j] = svga->overscan_color; + } + } + video_blit_memtoscreen(x_start, y_start, xsize + x_add, ysize + y_add); +} + +void +ibm8514_poll(ibm8514_t *dev, svga_t *svga) +{ + uint32_t x; + int wx, wy; + + if (!dev->linepos) { + timer_advance_u64(&svga->timer, svga->dispofftime); + dev->linepos = 1; + + if (dev->dispon) { + dev->hdisp_on = 1; + + dev->ma &= dev->vram_mask; + + if (dev->firstline == 2000) { + dev->firstline = dev->displine; + video_wait_for_buffer(); + } + + ibm8514_render_8bpp(svga); + + svga->x_add = (overscan_x >> 1); + ibm8514_render_overscan_left(dev, svga); + ibm8514_render_overscan_right(dev, svga); + svga->x_add = (overscan_x >> 1); + + if (dev->lastline < dev->displine) + dev->lastline = dev->displine; + } + + dev->displine++; + if (dev->interlace) + dev->displine++; + if (dev->displine > 1500) + dev->displine = 0; + } else { + timer_advance_u64(&svga->timer, svga->dispontime); + dev->hdisp_on = 0; + + dev->linepos = 0; + if (dev->dispon) { + if (dev->sc == dev->rowcount) { + dev->linecountff = 0; + dev->sc = 0; + + dev->maback += (dev->rowoffset << 3); + if (dev->interlace) + dev->maback += (dev->rowoffset << 3); + dev->maback &= dev->vram_mask; + dev->ma = dev->maback; + } else { + dev->linecountff = 0; + dev->sc++; + dev->sc &= 31; + dev->ma = dev->maback; + } + } + + dev->vc++; + dev->vc &= 2047; + + if (dev->vc == dev->dispend) { + dev->dispon = 0; + + for (x = 0; x < ((dev->vram_mask + 1) >> 12); x++) { + if (dev->changedvram[x]) + dev->changedvram[x]--; + } + + if (svga->fullchange) + svga->fullchange--; + } + if (dev->vc == dev->v_syncstart) { + dev->dispon = 0; + x = dev->h_disp; + + if (dev->interlace && !dev->oddeven) + dev->lastline++; + if (dev->interlace && dev->oddeven) + dev->firstline--; + + wx = x; + + wy = dev->lastline - dev->firstline; + ibm8514_doblit(wx, wy, dev, svga); + + dev->firstline = 2000; + dev->lastline = 0; + + dev->firstline_draw = 2000; + dev->lastline_draw = 0; + + dev->oddeven ^= 1; + + changeframecount = dev->interlace ? 3 : 2; + + if (dev->interlace && dev->oddeven) + dev->ma = dev->maback = 0 + (dev->rowoffset << 1); + else + dev->ma = dev->maback = 0; + + dev->ma = (dev->ma << 2); + dev->maback = (dev->maback << 2); + } + if (dev->vc == dev->v_total) { + dev->vc = 0; + dev->sc = 0; + dev->dispon = 1; + dev->displine = (dev->interlace && dev->oddeven) ? 1 : 0; + + svga->x_add = (overscan_x >> 1); + + dev->linecountff = 0; + } + } +} + +void +ibm8514_recalctimings(svga_t *svga) +{ + ibm8514_t *dev = &svga->dev8514; + + dev->h_disp_time = dev->h_disp = (dev->hdisp + 1) << 3; + dev->rowoffset = (dev->hdisp + 1); + dev->h_total = (dev->htotal + 1); + dev->v_total = (dev->vtotal + 1); + dev->v_syncstart = (dev->vsyncstart + 1); + dev->rowcount = !!(dev->disp_cntl & 0x08); + + if (((dev->disp_cntl & 0x60) == 0) || ((dev->disp_cntl & 0x60) >= 0x40)) + return; + + if (dev->accel.advfunc_cntl & 4) { + if (dev->hdisp == 0) { + dev->rowoffset = 128; + dev->h_disp = 1024; + } + + if (dev->vtotal == 0) + dev->v_total = 1632; + + if (dev->vsyncstart == 0) + dev->v_syncstart = 1536; + + if (dev->interlace) { + dev->dispend = 384; /*Interlaced*/ + dev->v_total >>= 2; + dev->v_syncstart >>= 2; + } else { + dev->dispend = 768; + dev->v_total >>= 1; + dev->v_syncstart >>= 1; + } + //pclog("1024x768 clock mode, hdisp = %d, htotal = %d, vtotal = %d, vsyncstart = %d, interlace = %02x\n", dev->h_disp, dev->h_total, dev->v_total, dev->v_syncstart, dev->interlace); + svga->clock = (cpuclock * (double)(1ull << 32)) / 44900000.0; + } else { + //pclog("640x480 clock mode\n"); + dev->dispend = 480; + dev->v_total >>= 1; + dev->v_syncstart >>= 1; + svga->clock = (cpuclock * (double)(1ull << 32)) / 25175000.0; + } + //pclog("8514 enabled, hdisp=%d, vtotal=%d, htotal=%d, dispend=%d, rowoffset=%d, split=%d, vsyncstart=%d, split=%08x\n", dev->hdisp, dev->vtotal, dev->htotal, dev->dispend, dev->rowoffset, dev->split, dev->vsyncstart, dev->split); +} + +static uint8_t +ibm8514_mca_read(int port, void *priv) +{ + svga_t *svga = (svga_t *)priv; + ibm8514_t *dev = &svga->dev8514; + + return(dev->pos_regs[port & 7]); +} + + +static void +ibm8514_mca_write(int port, uint8_t val, void *priv) +{ + svga_t *svga = (svga_t *)priv; + ibm8514_t *dev = &svga->dev8514; + + /* MCA does not write registers below 0x0100. */ + if (port < 0x0102) return; + + /* Save the MCA register value. */ + dev->pos_regs[port & 7] = val; +} + + +static uint8_t +ibm8514_mca_feedb(void *priv) +{ + return 1; +} + + +static void +*ibm8514_init(const device_t *info) +{ + svga_t *svga = svga_8514; + ibm8514_t *dev = &svga->dev8514; + + dev->vram_size = 1024 << 10; + dev->vram = calloc(dev->vram_size, 1); + dev->changedvram = calloc(dev->vram_size >> 12, 1); + dev->vram_mask = dev->vram_size - 1; + dev->map8 = svga->pallook; + + dev->type = info->flags; + + ibm8514_io_remove(svga); + ibm8514_io_set(svga); + + if (info->flags & DEVICE_MCA) { + dev->pos_regs[0] = 0x7f; + dev->pos_regs[1] = 0xef; + mca_add(ibm8514_mca_read, ibm8514_mca_write, ibm8514_mca_feedb, NULL, svga); + } + + return svga; +} + +static void +ibm8514_close(void *p) +{ + svga_t *svga = (svga_t *)p; + ibm8514_t *dev = &svga->dev8514; + + if (dev) { + free(dev->vram); + free(dev->changedvram); + } +} + +static void +ibm8514_speed_changed(void *p) +{ + svga_t *svga = (svga_t *)p; + ibm8514_t *dev = &svga->dev8514; + + svga_recalctimings(svga); +} + +static void +ibm8514_force_redraw(void *p) +{ + svga_t *svga = (svga_t *)p; + ibm8514_t *dev = &svga->dev8514; + + dev->fullchange = changeframecount; +} + +// clang-format off +const device_t gen8514_isa_device = { + .name = "Generic 8514/A clone (ISA)", + .internal_name = "8514_isa", + .flags = DEVICE_AT | DEVICE_ISA, + .local = 0, + .init = ibm8514_init, + .close = ibm8514_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = ibm8514_speed_changed, + .force_redraw = ibm8514_force_redraw, + .config = NULL +}; + +const device_t ibm8514_mca_device = { + .name = "IBM 8514/A (MCA)", + .internal_name = "8514_mca", + .flags = DEVICE_MCA, + .local = 0, + .init = ibm8514_init, + .close = ibm8514_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = ibm8514_speed_changed, + .force_redraw = ibm8514_force_redraw, + .config = NULL +}; + + +void +ibm8514_device_add(void) +{ + if (!ibm8514_enabled) + return; + + if (machine_has_bus(machine, MACHINE_BUS_MCA)) + device_add(&ibm8514_mca_device); + else + device_add(&gen8514_isa_device); +} diff --git a/src/video/vid_s3.c b/src/video/vid_s3.c index 406f6a20e..371d81ce4 100644 --- a/src/video/vid_s3.c +++ b/src/video/vid_s3.c @@ -2984,6 +2984,9 @@ static void s3_recalctimings(svga_t *svga) (s3->chip != S3_TRIO64) && (s3->chip != S3_VISION964) && (s3->chip != S3_VISION968)) { if (s3->width == 1280 || s3->width == 1600) svga->hdisp <<= 1; + } else if ((s3->card_type == S3_ELSAWIN2KPROX_964) || (s3->card_type == S3_ELSAWIN2KPROX)) { + if (s3->width == 1280 || s3->width == 1600) + svga->hdisp <<= 1; } else if (s3->card_type == S3_SPEA_MERCURY_P64V) { if (s3->width == 1280 || s3->width == 1600) svga->hdisp <<= 1; diff --git a/src/video/vid_svga.c b/src/video/vid_svga.c index 20c4d6dd6..dde271df9 100644 --- a/src/video/vid_svga.c +++ b/src/video/vid_svga.c @@ -43,6 +43,8 @@ void svga_doblit(int wx, int wy, svga_t *svga); +svga_t *svga_8514; + extern int cyc_total; extern uint8_t edatlookup[4][4]; @@ -51,7 +53,7 @@ uint8_t svga_rotate[8][256]; /*Primary SVGA device. As multiple video cards are not yet supported this is the only SVGA device.*/ static svga_t *svga_pri; - +int vga_on; svga_t *svga_get_pri() @@ -552,8 +554,13 @@ svga_recalctimings(svga_t *svga) } else overscan_x = 16; - if (svga->recalctimings_ex) - svga->recalctimings_ex(svga); + if (vga_on) { + if (svga->recalctimings_ex) { + svga->recalctimings_ex(svga); + } + } else { + ibm8514_recalctimings(svga); + } svga->y_add = (overscan_y >> 1) - (svga->crtc[8] & 0x1f); svga->x_add = (overscan_x >> 1); @@ -650,6 +657,11 @@ svga_poll(void *p) int wx, wy; int ret, old_ma; + if (!vga_on) { + ibm8514_poll(&svga->dev8514, svga); + return; + } + if (!svga->linepos) { if (svga->displine == svga->hwcursor_latch.y && svga->hwcursor_latch.ena) { svga->hwcursor_on = svga->hwcursor.ysize - svga->hwcursor_latch.yoff; @@ -956,6 +968,8 @@ svga_init(const device_t *info, svga_t *svga, void *p, int memsize, svga->translate_address = NULL; svga->ksc5601_english_font_type = 0; + vga_on = 1; + if ((info->flags & DEVICE_PCI) || (info->flags & DEVICE_VLB) || (info->flags & DEVICE_MCA)) { mem_mapping_add(&svga->mapping, 0xa0000, 0x20000, svga_read, svga_readw, svga_readl, @@ -977,6 +991,11 @@ svga_init(const device_t *info, svga_t *svga, void *p, int memsize, svga_pri = svga; + if (ibm8514_enabled) + svga_8514 = svga; + else + svga_8514 = NULL; + svga->ramdac_type = RAMDAC_6BIT; svga->map8 = svga->pallook; diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index c9726d37f..6f3463b4b 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -689,6 +689,7 @@ VIDOBJ := agpgart.o video.o \ vid_wy700.o \ vid_ega.o vid_ega_render.o \ vid_svga.o vid_svga_render.o \ + vid_8514a.o \ vid_ddc.o \ vid_vga.o \ vid_ati_eeprom.o \ diff --git a/src/win/languages/cs-CZ.rc b/src/win/languages/cs-CZ.rc index 5e89d3376..751d69b53 100644 --- a/src/win/languages/cs-CZ.rc +++ b/src/win/languages/cs-CZ.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Grafika:" #define STR_VOODOO "Použít grafický akcelerátor Voodoo" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Myš:" #define STR_JOYSTICK "Joystick:" diff --git a/src/win/languages/de-DE.rc b/src/win/languages/de-DE.rc index d9a21fa12..430b517f0 100644 --- a/src/win/languages/de-DE.rc +++ b/src/win/languages/de-DE.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Videokarte:" #define STR_VOODOO "Voodoo-Grafik" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Maus:" #define STR_JOYSTICK "Joystick:" diff --git a/src/win/languages/dialogs.rc b/src/win/languages/dialogs.rc index 4a4c132fc..9d01e4cdf 100644 --- a/src/win/languages/dialogs.rc +++ b/src/win/languages/dialogs.rc @@ -265,6 +265,10 @@ BEGIN 7, 27, 199, CFG_CHECKBOX_HEIGHT PUSHBUTTON STR_CONFIGURE, IDC_BUTTON_VOODOO, CFG_COMBO_BTN_LEFT, 25, CFG_BTN_WIDTH, CFG_BTN_HEIGHT + + CONTROL STR_IBM8514, IDC_CHECK_IBM8514, + "Button", BS_AUTOCHECKBOX | WS_TABSTOP, + 7, 46, 199, CFG_CHECKBOX_HEIGHT END DLG_CFG_INPUT DIALOG DISCARDABLE CFG_PANE_LEFT, CFG_PANE_TOP, CFG_PANE_WIDTH, CFG_PANE_HEIGHT diff --git a/src/win/languages/en-GB.rc b/src/win/languages/en-GB.rc index bfb92015c..5d3ad5c1c 100644 --- a/src/win/languages/en-GB.rc +++ b/src/win/languages/en-GB.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Video:" #define STR_VOODOO "Voodoo Graphics" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Mouse:" #define STR_JOYSTICK "Joystick:" diff --git a/src/win/languages/en-US.rc b/src/win/languages/en-US.rc index 50abbba44..7e7e26e4c 100644 --- a/src/win/languages/en-US.rc +++ b/src/win/languages/en-US.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Video:" #define STR_VOODOO "Voodoo Graphics" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Mouse:" #define STR_JOYSTICK "Joystick:" diff --git a/src/win/languages/es-ES.rc b/src/win/languages/es-ES.rc index e9fbea09f..6191b7d70 100644 --- a/src/win/languages/es-ES.rc +++ b/src/win/languages/es-ES.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Vídeo:" #define STR_VOODOO "Voodoo Graphics" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Ratón:" #define STR_JOYSTICK "Mando:" diff --git a/src/win/languages/fi-FI.rc b/src/win/languages/fi-FI.rc index 4d45c4639..dd08c9a67 100644 --- a/src/win/languages/fi-FI.rc +++ b/src/win/languages/fi-FI.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Näytönohjain:" #define STR_VOODOO "Voodoo-grafiikkasuoritin" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Hiiri:" #define STR_JOYSTICK "Peliohjain:" diff --git a/src/win/languages/fr-FR.rc b/src/win/languages/fr-FR.rc index b6aa48089..182ab6463 100644 --- a/src/win/languages/fr-FR.rc +++ b/src/win/languages/fr-FR.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Vidéo:" #define STR_VOODOO "Graphique Voodoo" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Souris:" #define STR_JOYSTICK "Manette de commande:" diff --git a/src/win/languages/hr-HR.rc b/src/win/languages/hr-HR.rc index f8230e25e..cc0a10c52 100644 --- a/src/win/languages/hr-HR.rc +++ b/src/win/languages/hr-HR.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Video:" #define STR_VOODOO "Voodoo grafika" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Miš:" #define STR_JOYSTICK "Palica za igru:" diff --git a/src/win/languages/hu-HU.rc b/src/win/languages/hu-HU.rc index 89d66f18d..d74f31741 100644 --- a/src/win/languages/hu-HU.rc +++ b/src/win/languages/hu-HU.rc @@ -277,6 +277,7 @@ END #define STR_VIDEO "Videokártya:" #define STR_VOODOO "Voodoo-gyorsítókártya" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Egér:" #define STR_JOYSTICK "Játékvezérlő:" diff --git a/src/win/languages/it-IT.rc b/src/win/languages/it-IT.rc index 17bb217dd..1b75f0674 100644 --- a/src/win/languages/it-IT.rc +++ b/src/win/languages/it-IT.rc @@ -273,6 +273,7 @@ END #define STR_VIDEO "Video:" #define STR_VOODOO "Grafica Voodoo" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Mouse:" #define STR_JOYSTICK "Joystick:" diff --git a/src/win/languages/ja-JP.rc b/src/win/languages/ja-JP.rc index 1dc3c2987..a79950469 100644 --- a/src/win/languages/ja-JP.rc +++ b/src/win/languages/ja-JP.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "ビデオカード:" #define STR_VOODOO "Voodooグラフィック" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "マウス:" #define STR_JOYSTICK "ジョイスティック:" diff --git a/src/win/languages/ko-KR.rc b/src/win/languages/ko-KR.rc index b461b2cbd..19a557e95 100644 --- a/src/win/languages/ko-KR.rc +++ b/src/win/languages/ko-KR.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "비디오 카드:" #define STR_VOODOO "Voodoo 그래픽" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "마우스:" #define STR_JOYSTICK "조이스틱:" diff --git a/src/win/languages/pl-PL.rc b/src/win/languages/pl-PL.rc index 4aa55b154..6fd61661c 100644 --- a/src/win/languages/pl-PL.rc +++ b/src/win/languages/pl-PL.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Wideo:" #define STR_VOODOO "Grafika Voodoo" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Mysz:" #define STR_JOYSTICK "Joystick:" diff --git a/src/win/languages/pt-BR.rc b/src/win/languages/pt-BR.rc index bf3c37f7b..69aa25010 100644 --- a/src/win/languages/pt-BR.rc +++ b/src/win/languages/pt-BR.rc @@ -275,6 +275,7 @@ END #define STR_VIDEO "Vídeo:" #define STR_VOODOO "3DFX Voodoo" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Mouse:" #define STR_JOYSTICK "Joystick:" diff --git a/src/win/languages/pt-PT.rc b/src/win/languages/pt-PT.rc index 86625ab3b..3b377d2c8 100644 --- a/src/win/languages/pt-PT.rc +++ b/src/win/languages/pt-PT.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Vídeo:" #define STR_VOODOO "Gráficos Voodoo" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Rato:" #define STR_JOYSTICK "Joystick:" diff --git a/src/win/languages/ru-RU.rc b/src/win/languages/ru-RU.rc index 217b2754e..c2e3ca6d9 100644 --- a/src/win/languages/ru-RU.rc +++ b/src/win/languages/ru-RU.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Видеокарта:" #define STR_VOODOO "Ускоритель Voodoo" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Мышь:" #define STR_JOYSTICK "Джойстик:" diff --git a/src/win/languages/sl-SI.rc b/src/win/languages/sl-SI.rc index 18fd22d4c..8e9bd2532 100644 --- a/src/win/languages/sl-SI.rc +++ b/src/win/languages/sl-SI.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Video:" #define STR_VOODOO "Voodoo grafika" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Miška:" #define STR_JOYSTICK "Igralna palica:" diff --git a/src/win/languages/tr-TR.rc b/src/win/languages/tr-TR.rc index 251ae6233..9f9b3ab12 100644 --- a/src/win/languages/tr-TR.rc +++ b/src/win/languages/tr-TR.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Ekran kartı:" #define STR_VOODOO "Voodoo Grafikleri" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Fare:" #define STR_JOYSTICK "Oyun kolu:" diff --git a/src/win/languages/uk-UA.rc b/src/win/languages/uk-UA.rc index b0887df7a..c54ac2668 100644 --- a/src/win/languages/uk-UA.rc +++ b/src/win/languages/uk-UA.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "Відеокарта:" #define STR_VOODOO "Прискорювач Voodoo" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "Миша:" #define STR_JOYSTICK "Джойстик:" diff --git a/src/win/languages/zh-CN.rc b/src/win/languages/zh-CN.rc index c852222a4..0a8e405ef 100644 --- a/src/win/languages/zh-CN.rc +++ b/src/win/languages/zh-CN.rc @@ -272,6 +272,7 @@ END #define STR_VIDEO "显卡:" #define STR_VOODOO "Voodoo Graphics" +#define STR_IBM8514 "IBM 8514/a Graphics" #define STR_MOUSE "鼠标:" #define STR_JOYSTICK "操纵杆:" diff --git a/src/win/win_mouse.c b/src/win/win_mouse.c index e5d1c229a..b69646a6e 100644 --- a/src/win/win_mouse.c +++ b/src/win/win_mouse.c @@ -28,6 +28,7 @@ #include <86box/win.h> int mouse_capture; +double mouse_sensitivity = 1.0; /* Unused. */ typedef struct { int buttons; diff --git a/src/win/win_opendir.c b/src/win/win_opendir.c index 5339f0713..24fefc8c2 100644 --- a/src/win/win_opendir.c +++ b/src/win/win_opendir.c @@ -28,26 +28,15 @@ #include <86box/plat_dir.h> -#ifdef UNICODE -# define SUFFIX L"\\*" -# define FINDATA struct _wfinddata_t -# define FINDFIRST _wfindfirst -# define FINDNEXT _wfindnext -#else -# define SUFFIX "\\*" -# define FINDATA struct _finddata_t -# define FINDFIRST _findfirst -# define FINDNEXT _findnext -#endif +#define SUFFIX "\\*" +#define FINDATA struct _finddata_t +#define FINDFIRST _findfirst +#define FINDNEXT _findnext /* Open a directory. */ DIR * -#ifdef UNICODE -opendirw(const wchar_t *name) -#else opendir(const char *name) -#endif { DIR *p; @@ -69,20 +58,11 @@ opendir(const char *name) memset(p->dta, 0x00, sizeof(struct _finddata_t)); /* Add search filespec. */ -#ifdef UNICODE - wcscpy(p->dir, name); - wcscat(p->dir, SUFFIX); -#else strcpy(p->dir, name); strcat(p->dir, SUFFIX); -#endif /* Special case: flag if we are in the root directory. */ -#ifdef UNICODE - if (wcslen(p->dir) == 3) -#else if (strlen(p->dir) == 3) -#endif p->flags |= DIR_F_ISROOT; /* Start the searching by doing a FindFirst. */ @@ -136,31 +116,18 @@ readdir(DIR *p) p->dent.d_off = p->offset++; switch(p->offset) { case 1: /* . */ -#ifdef UNICODE - wcsncpy(p->dent.d_name, L".", MAXNAMLEN+1); -#else strncpy(p->dent.d_name, ".", MAXNAMLEN+1); -#endif p->dent.d_reclen = 1; break; case 2: /* .. */ -#ifdef UNICODE - wcsncpy(p->dent.d_name, L"..", MAXNAMLEN+1); -#else strncpy(p->dent.d_name, "..", MAXNAMLEN+1); -#endif p->dent.d_reclen = 2; break; default: /* regular entry. */ -#ifdef UNICODE - wcsncpy(p->dent.d_name, ffp->name, MAXNAMLEN+1); - p->dent.d_reclen = (char)wcslen(p->dent.d_name); -#else strncpy(p->dent.d_name, ffp->name, MAXNAMLEN+1); p->dent.d_reclen = (char)strlen(p->dent.d_name); -#endif } /* Read next entry. */ diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 5d9bdea38..a77efc8fc 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -85,7 +85,7 @@ static int temp_dynarec; #endif /* Video category */ -static int temp_gfxcard, temp_voodoo; +static int temp_gfxcard, temp_ibm8514, temp_voodoo; /* Input devices category */ static int temp_mouse, temp_joystick; @@ -332,6 +332,7 @@ win_settings_init(void) /* Video category */ temp_gfxcard = gfxcard; temp_voodoo = voodoo_enabled; + temp_ibm8514 = ibm8514_enabled; /* Input devices category */ temp_mouse = mouse_type; @@ -456,6 +457,7 @@ win_settings_changed(void) /* Video category */ i = i || (gfxcard != temp_gfxcard); i = i || (voodoo_enabled != temp_voodoo); + i = i || (ibm8514_enabled != temp_ibm8514); /* Input devices category */ i = i || (mouse_type != temp_mouse); @@ -546,6 +548,7 @@ win_settings_save(void) /* Video category */ gfxcard = temp_gfxcard; voodoo_enabled = temp_voodoo; + ibm8514_enabled = temp_ibm8514; /* Input devices category */ mouse_type = temp_mouse; @@ -1106,9 +1109,13 @@ win_settings_video_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) settings_enable_window(hdlg, IDC_COMBO_VIDEO, !machine_has_flags(temp_machine, MACHINE_VIDEO_ONLY)); e = settings_list_to_device[0][settings_get_cur_sel(hdlg, IDC_COMBO_VIDEO)]; settings_enable_window(hdlg, IDC_CONFIGURE_VID, video_card_has_config(e)); + settings_enable_window(hdlg, IDC_CHECK_VOODOO, machine_has_bus(temp_machine, MACHINE_BUS_PCI)); settings_set_check(hdlg, IDC_CHECK_VOODOO, temp_voodoo); settings_enable_window(hdlg, IDC_BUTTON_VOODOO, machine_has_bus(temp_machine, MACHINE_BUS_PCI) && temp_voodoo); + + settings_enable_window(hdlg, IDC_CHECK_IBM8514, machine_has_bus(temp_machine, MACHINE_BUS_ISA16) || machine_has_bus(temp_machine, MACHINE_BUS_MCA)); + settings_set_check(hdlg, IDC_CHECK_IBM8514, temp_ibm8514); return TRUE; case WM_COMMAND: @@ -1123,6 +1130,10 @@ win_settings_video_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) settings_enable_window(hdlg, IDC_BUTTON_VOODOO, temp_voodoo); break; + case IDC_CHECK_IBM8514: + temp_ibm8514 = settings_get_check(hdlg, IDC_CHECK_IBM8514); + break; + case IDC_BUTTON_VOODOO: temp_deviceconfig |= deviceconfig_open(hdlg, (void *)&voodoo_device); break; @@ -1137,6 +1148,7 @@ win_settings_video_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) case WM_SAVESETTINGS: temp_gfxcard = settings_list_to_device[0][settings_get_cur_sel(hdlg, IDC_COMBO_VIDEO)]; temp_voodoo = settings_get_check(hdlg, IDC_CHECK_VOODOO); + temp_ibm8514 = settings_get_check(hdlg, IDC_CHECK_IBM8514); default: return FALSE; diff --git a/src/win/win_thread.c b/src/win/win_thread.c index 1ad0fc469..f97c5a3db 100644 --- a/src/win/win_thread.c +++ b/src/win/win_thread.c @@ -45,6 +45,16 @@ thread_create(void (*func)(void *param), void *param) } +int +thread_test_mutex(thread_t *arg) +{ + if (arg == NULL) return(0); + + return (WaitForSingleObject(arg, 0) == WAIT_OBJECT_0) ? 1 : 0; +} + + + int thread_wait(thread_t *arg) { diff --git a/vcpkg.json b/vcpkg.json index 495ac2eac..d64fc9108 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "86box", - "version-string": "3.4", + "version-string": "3.5", "homepage": "https://86box.net/", "documentation": "http://86box.readthedocs.io/", "license": "GPL-2.0-or-later", @@ -14,8 +14,28 @@ "qt-ui": { "description": "Qt User Interface", "dependencies": [ - "qt5-base", - "qt5-translations" + { + "name": "qtbase", + "default-features": false, + "features": [ + "concurrent", + "default-features", + "gui", + "harfbuzz", + "network", + "vulkan", + "widgets", + "zstd" + ] + }, + { + "name": "qttools", + "default-features": false, + "features": [ + "linguist" + ], + "host": true + } ] }, "munt": {