diff --git a/.ci/AppImageBuilder.yml b/.ci/AppImageBuilder.yml index 64f8f91ed..7f312baae 100644 --- a/.ci/AppImageBuilder.yml +++ b/.ci/AppImageBuilder.yml @@ -72,6 +72,7 @@ AppDir: - libxkbcommon-x11-0 # if QT:BOOL=ON - qtwayland5 # if QT:BOOL=ON - zlib1g + - libserialport0 files: exclude: - etc diff --git a/.ci/build.sh b/.ci/build.sh index 2a48d564b..326952186 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -525,6 +525,9 @@ then cmake_flags_extra="$cmake_flags_extra -D MOLTENVK=ON -D \"MOLTENVK_INCLUDE_DIR=$macports\"" fi + # Enable Libserialport + cmake_flags_extra="$cmake_flags_extra -D \"LIBSERIALPORT_ROOT=$macports\"" + # Install dependencies only if we're in a new build and/or MacPorts prefix. if check_buildtag "$(basename "$macports")" then @@ -539,6 +542,12 @@ then sudo sed -i -e 's/configure.env-append MAKE=/configure.env-append VULKAN_SDK=${prefix} MAKE=/g' "$qt5_portfile" fi + # Patch openal-soft to use 1.23.1 on all targets instead of 1.24.1 on >=10.15 only, + # to prevent a symlink mismatch from having different versions on x86_64 and arm64. + # See: https://github.com/macports/macports-ports/commit/9b4903fc9c76769d476079e404c9a3b8a225f8aa + openal_portfile="$macports/var/macports/sources/rsync.macports.org/macports/release/tarballs/ports/audio/openal-soft/Portfile" + sudo sed -i -e 's/if {${os.platform} ne "darwin" || ${os.major} >= 19}/if {0}/g' "$openal_portfile" + # Patch wget to remove libproxy support, as it depends on shared-mime-info which # fails to build for a 10.13 target, which we have to do despite wget only being # a host dependency. MacPorts issue 69406 strongly implies this will not be fixed. @@ -616,7 +625,7 @@ else # ...and the ones we do want listed. Non-dev packages fill missing spots on the list. libpkgs="" longest_libpkg=0 - for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libxkbcommon-x11-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev libvdeplug-dev libfluidsynth-dev libsndfile1-dev + for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libxkbcommon-x11-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev libvdeplug-dev libfluidsynth-dev libsndfile1-dev libserialport-dev do libpkgs="$libpkgs $pkg:$arch_deb" length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c) diff --git a/.ci/dependencies_macports.txt b/.ci/dependencies_macports.txt index b23ac441d..e530871ae 100644 --- a/.ci/dependencies_macports.txt +++ b/.ci/dependencies_macports.txt @@ -16,3 +16,4 @@ ghostscript libslirp vde2 libsndfile +libserialport diff --git a/.ci/dependencies_msys.txt b/.ci/dependencies_msys.txt index eacdb8b36..b6c0979e2 100644 --- a/.ci/dependencies_msys.txt +++ b/.ci/dependencies_msys.txt @@ -14,3 +14,4 @@ qt5-static qt5-translations vulkan-headers libsndfile +libserialport diff --git a/.github/workflows/cmake_linux.yml b/.github/workflows/cmake_linux.yml index 859a38899..73ae94969 100644 --- a/.github/workflows/cmake_linux.yml +++ b/.github/workflows/cmake_linux.yml @@ -81,6 +81,8 @@ jobs: libopenal-dev libslirp-dev libfluidsynth-dev + libvdeplug-dev + libserialport-dev ${{ matrix.ui.packages }} - name: Checkout repository @@ -89,7 +91,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install sonar-scanner and build-wrapper - uses: SonarSource/sonarcloud-github-c-cpp@5c3c39143e381909307f6903f13774b275ed956d + uses: SonarSource/sonarcloud-github-c-cpp@v3 - name: Configure CMake run: >- diff --git a/.github/workflows/cmake_macos.yml b/.github/workflows/cmake_macos.yml index f482c7b69..5a0454385 100644 --- a/.github/workflows/cmake_macos.yml +++ b/.github/workflows/cmake_macos.yml @@ -81,6 +81,8 @@ jobs: openal-soft fluidsynth libslirp + vde + libserialport ${{ matrix.ui.packages }} - name: Checkout repository @@ -89,7 +91,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install sonar-scanner and build-wrapper - uses: SonarSource/sonarcloud-github-c-cpp@5c3c39143e381909307f6903f13774b275ed956d + uses: SonarSource/sonarcloud-github-c-cpp@v3 - name: Configure CMake run: >- @@ -101,6 +103,7 @@ jobs: -D Qt5_ROOT=$(brew --prefix qt@5) -D Qt5LinguistTools_ROOT=$(brew --prefix qt@5) -D OpenAL_ROOT=$(brew --prefix openal-soft) + -D LIBSERIALPORT_ROOT=$(brew --prefix libserialport) - name: Build run: | @@ -180,6 +183,7 @@ jobs: openal-soft fluidsynth libslirp + libserialport ${{ matrix.ui.packages }} - name: Checkout repository @@ -188,7 +192,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis # - name: Install sonar-scanner and build-wrapper -# uses: SonarSource/sonarcloud-github-c-cpp@5c3c39143e381909307f6903f13774b275ed956d +# uses: SonarSource/sonarcloud-github-c-cpp@v3 - name: Configure CMake run: >- @@ -200,6 +204,7 @@ jobs: -D Qt5_ROOT=$(brew --prefix qt@5) -D Qt5LinguistTools_ROOT=$(brew --prefix qt@5) -D OpenAL_ROOT=$(brew --prefix openal-soft) + -D LIBSERIALPORT_ROOT=$(brew --prefix libserialport) - name: Build run: | diff --git a/.github/workflows/cmake_windows_msys2.yml b/.github/workflows/cmake_windows_msys2.yml index 91442eafd..abe25b949 100644 --- a/.github/workflows/cmake_windows_msys2.yml +++ b/.github/workflows/cmake_windows_msys2.yml @@ -67,15 +67,9 @@ jobs: environment: # - msystem: MSYS # toolchain: ./cmake/flags-gcc-x86_64.cmake -# - msystem: MINGW32 -# prefix: mingw-w64-i686 -# toolchain: ./cmake/flags-gcc-i686.cmake - msystem: MINGW64 prefix: mingw-w64-x86_64 toolchain: ./cmake/flags-gcc-x86_64.cmake -# - msystem: CLANG32 -# prefix: mingw-w64-clang-i686 -# toolchain: ./cmake/llvm-win32-i686.cmake # - msystem: CLANG64 # prefix: mingw-w64-clang-x86_64 # toolchain: ./cmake/llvm-win32-x86_64.cmake @@ -103,6 +97,7 @@ jobs: rtmidi:p libslirp:p fluidsynth:p + libserialport:p ${{ matrix.ui.packages }} - name: Checkout repository @@ -111,7 +106,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install sonar-scanner and build-wrapper - uses: SonarSource/sonarcloud-github-c-cpp@5c3c39143e381909307f6903f13774b275ed956d + uses: SonarSource/sonarcloud-github-c-cpp@v3 - name: Configure CMake run: >- diff --git a/.github/workflows/codeql_linux.yml b/.github/workflows/codeql_linux.yml index 396ccd4fe..fc0397703 100644 --- a/.github/workflows/codeql_linux.yml +++ b/.github/workflows/codeql_linux.yml @@ -84,6 +84,8 @@ jobs: libopenal-dev libslirp-dev libfluidsynth-dev + libvdeplug-dev + libserialport-dev ${{ matrix.ui.packages }} - name: Checkout repository diff --git a/.github/workflows/codeql_macos.yml b/.github/workflows/codeql_macos.yml index 6947ab6ef..841ca98f5 100644 --- a/.github/workflows/codeql_macos.yml +++ b/.github/workflows/codeql_macos.yml @@ -75,6 +75,8 @@ jobs: openal-soft fluidsynth libslirp + vde + libserialport ${{ matrix.ui.packages }} - name: Checkout repository @@ -96,6 +98,7 @@ jobs: -D Qt5_ROOT=$(brew --prefix qt@5) -D Qt5LinguistTools_ROOT=$(brew --prefix qt@5) -D OpenAL_ROOT=$(brew --prefix openal-soft) + -D LIBSERIALPORT_ROOT=$(brew --prefix libserialport) - name: Build run: cmake --build build diff --git a/.github/workflows/codeql_windows_msys2.yml b/.github/workflows/codeql_windows_msys2.yml index 652a1986a..7a0055910 100644 --- a/.github/workflows/codeql_windows_msys2.yml +++ b/.github/workflows/codeql_windows_msys2.yml @@ -72,15 +72,9 @@ jobs: environment: # - msystem: MSYS # toolchain: ./cmake/flags-gcc-x86_64.cmake - - msystem: MINGW32 - prefix: mingw-w64-i686 - toolchain: ./cmake/flags-gcc-i686.cmake - msystem: MINGW64 prefix: mingw-w64-x86_64 toolchain: ./cmake/flags-gcc-x86_64.cmake -# - msystem: CLANG32 -# prefix: mingw-w64-clang-i686 -# toolchain: ./cmake/llvm-win32-i686.cmake # - msystem: CLANG64 # prefix: mingw-w64-clang-x86_64 # toolchain: ./cmake/llvm-win32-x86_64.cmake @@ -108,6 +102,7 @@ jobs: rtmidi:p libslirp:p fluidsynth:p + libserialport:p ${{ matrix.ui.packages }} - name: Checkout repository diff --git a/CMakePresets.json b/CMakePresets.json index c19a7abc0..d4af8e6cb 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -68,7 +68,8 @@ "Qt5_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5", "MOLTENVK_DIR": "/opt/homebrew/opt/molten-vk", "Qt5LinguistTools_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5LinguistTools", - "OpenAL_ROOT": "/opt/homebrew/opt/openal-soft" + "OpenAL_ROOT": "/opt/homebrew/opt/openal-soft", + "LIBSERIALPORT_ROOT": "/opt/homebrew/opt/libserialport" }, "inherits": "regular" }, @@ -86,6 +87,7 @@ "MOLTENVK_DIR": "/opt/homebrew/opt/molten-vk", "Qt5LinguistTools_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5LinguistTools", "OpenAL_ROOT": "/opt/homebrew/opt/openal-soft", + "LIBSERIALPORT_ROOT": "/opt/homebrew/opt/libserialport", "CMAKE_CXX_FLAGS_DEBUG": "-g -O0 -DENABLE_VDE_LOG", "CMAKE_C_FLAGS_DEBUG": "-g -O0 -DENABLE_VDE_LOG" }, diff --git a/debian/control b/debian/control index a718aee33..a11e2af48 100644 --- a/debian/control +++ b/debian/control @@ -16,7 +16,8 @@ Build-Depends: cmake (>= 3.21), libsndfile-dev, ninja-build, qttools5-dev, - qtbase5-private-dev + qtbase5-private-dev, + libserialport-dev Standards-Version: 4.6.0 Homepage: https://86box.net/ #Vcs-Browser: https://salsa.debian.org/debian/86box diff --git a/src/cdrom/cdrom.c b/src/cdrom/cdrom.c index ff83b0a07..0bdcb5fb8 100644 --- a/src/cdrom/cdrom.c +++ b/src/cdrom/cdrom.c @@ -1167,27 +1167,81 @@ read_toc_session(cdrom_t *dev, unsigned char *b, int msf) static int read_toc_raw(cdrom_t *dev, unsigned char *b) { - track_info_t ti; - int len = 4; - int first_track; - int last_track; - - cdrom_log("read_toc_raw(%08X, %08X)\n", dev, b); - - dev->ops->get_tracks(dev, &first_track, &last_track); + track_info_t ti; + raw_track_info_t rti[256] = { 0 }; + int num = 0; + int len = 4; + int first_track; + int last_track; /* Bytes 2 and 3 = Number of first and last sessions */ b[2] = b[3] = 1; - for (int i = 0; i <= last_track; i++) { - dev->ops->get_track_info(dev, i + 1, 0, &ti); + if (dev->ops->get_raw_track_info != NULL) { + cdrom_log("read_toc_raw(%08X, %08X): Raw tracks\n", dev, b); + pclog("read_toc_raw(%016" PRIXPTR ", %016" PRIXPTR "): Raw tracks\n", + (uintptr_t) dev, (uintptr_t) b); + + dev->ops->get_raw_track_info(dev, &num, (raw_track_info_t *) rti); + + if (num != 0) for (int i = 0; i < num; i++) { + b[len++] = rti[i].session; + b[len++] = rti[i].adr_ctl; + b[len++] = rti[i].tno; + b[len++] = rti[i].point; + b[len++] = rti[i].m; + b[len++] = rti[i].s; + b[len++] = rti[i].f; + b[len++] = rti[i].zero; + b[len++] = rti[i].pm; + b[len++] = rti[i].ps; + b[len++] = rti[i].pf; + } + } else { + cdrom_log("read_toc_raw(%08X, %08X): Cooked tracks\n", dev, b); + pclog("read_toc_raw(%016" PRIXPTR ", %016" PRIXPTR "): Cooked tracks\n", + (uintptr_t) dev, (uintptr_t) b); + + dev->ops->get_tracks(dev, &first_track, &last_track); + + dev->ops->get_track_info(dev, 1, 0, &ti); + + b[len++] = 1; /* Session number */ + b[len++] = ti.attr; /* Track ADR and Control */ + b[len++] = 0; /* TNO (always 0) */ + b[len++] = 0xA0; /* Point (for track points - track number) */ + /* Yes, this is correct - MSF followed by PMSF, the actual position is in PMSF. */ + b[len++] = 0; + b[len++] = 0; + b[len++] = 0; + b[len++] = 0; + b[len++] = ti.number; /* First track number */ + b[len++] = 0; + b[len++] = 0; + + dev->ops->get_track_info(dev, last_track, 0, &ti); + + b[len++] = 1; /* Session number */ + b[len++] = ti.attr; /* Track ADR and Control */ + b[len++] = 0; /* TNO (always 0) */ + b[len++] = 0xA1; /* Point (for track points - track number) */ + /* Yes, this is correct - MSF followed by PMSF, the actual position is in PMSF. */ + b[len++] = 0; + b[len++] = 0; + b[len++] = 0; + b[len++] = 0; + b[len++] = ti.number; /* First track number */ + b[len++] = 0; + b[len++] = 0; + + dev->ops->get_track_info(dev, last_track + 1, 0, &ti); cdrom_log(" tracks(%i) = %02X, %02X, %i:%02i.%02i\n", i, ti.attr, ti.number, ti.m, ti.s, ti.f); b[len++] = 1; /* Session number */ b[len++] = ti.attr; /* Track ADR and Control */ b[len++] = 0; /* TNO (always 0) */ - b[len++] = ti.number; /* Point (for track points - track number) */ + b[len++] = 0xA2; /* Point (for track points - track number) */ /* Yes, this is correct - MSF followed by PMSF, the actual position is in PMSF. */ b[len++] = 0; b[len++] = 0; @@ -1196,6 +1250,25 @@ read_toc_raw(cdrom_t *dev, unsigned char *b) b[len++] = ti.m; /* PM */ b[len++] = ti.s; /* PS */ b[len++] = ti.f; /* PF */ + + for (int i = 0; i < last_track; i++) { + dev->ops->get_track_info(dev, i + 1, 0, &ti); + + cdrom_log(" tracks(%i) = %02X, %02X, %i:%02i.%02i\n", i, ti.attr, ti.number, ti.m, ti.s, ti.f); + + b[len++] = 1; /* Session number */ + b[len++] = ti.attr; /* Track ADR and Control */ + b[len++] = 0; /* TNO (always 0) */ + b[len++] = ti.number; /* Point (for track points - track number) */ + /* Yes, this is correct - MSF followed by PMSF, the actual position is in PMSF. */ + b[len++] = 0; + b[len++] = 0; + b[len++] = 0; + b[len++] = 0; + b[len++] = ti.m; /* PM */ + b[len++] = ti.s; /* PS */ + b[len++] = ti.f; /* PF */ + } } return len; @@ -1954,6 +2027,22 @@ cdrom_drive_reset(cdrom_t *dev) dev->get_channel = NULL; } +/* Will be removed later. */ +#ifdef ENABLE_CDROM_LOG +static void +cdrom_toc_dump(cdrom_t *dev) +{ + uint8_t b[65536] = { 0 }; + int len = cdrom_read_toc(dev, b, CD_TOC_RAW, 0, 0, 65536); + const char *fn2 = "d:\\86boxnew\\toc_cue.dmp"; + FILE * f = fopen(fn2, "wb"); + fwrite(b, 1, len, f); + fflush(f); + fclose(f); + pclog("Written TOC of %i bytes to %s\n", len, fn2); +} +#endif + void cdrom_hard_reset(void) { @@ -1994,6 +2083,10 @@ cdrom_hard_reset(void) cdrom_ioctl_open(dev, dev->image_path); else cdrom_image_open(dev, dev->image_path); + +#ifdef ENABLE_CDROM_LOG + cdrom_toc_dump(dev); +#endif } } } @@ -2095,15 +2188,13 @@ cdrom_reload(uint8_t id) strcpy(dev->image_path, dev->prev_image_path); #ifdef _WIN32 - if (strlen(dev->prev_image_path) > 0) { - if ((strlen(dev->image_path) >= 1) && (dev->image_path[strlen(dev->image_path) - 1] == '/')) - dev->image_path[strlen(dev->image_path) - 1] = '\\'; - } + if ((strlen(dev->prev_image_path) > 0) && (strlen(dev->image_path) >= 1) && + (dev->image_path[strlen(dev->image_path) - 1] == '/')) + dev->image_path[strlen(dev->image_path) - 1] = '\\'; #else - if (strlen(dev->prev_image_path) > 0) { - if ((strlen(dev->image_path) >= 1) && (dev->image_path[strlen(dev->image_path) - 1] == '\\')) - dev->image_path[strlen(dev->image_path) - 1] = '/'; - } + if ((strlen(dev->prev_image_path) > 0) && (strlen(dev->image_path) >= 1) && + (dev->image_path[strlen(dev->image_path) - 1] == '\\')) + dev->image_path[strlen(dev->image_path) - 1] = '/'; #endif if ((strlen(dev->image_path) != 0) && (strstr(dev->image_path, "ioctl://") == dev->image_path)) @@ -2111,6 +2202,10 @@ cdrom_reload(uint8_t id) else cdrom_image_open(dev, dev->image_path); +#ifdef ENABLE_CDROM_LOG + cdrom_toc_dump(dev); +#endif + cdrom_insert(id); } diff --git a/src/cdrom/cdrom_image.c b/src/cdrom/cdrom_image.c index 113d426c6..0ff914795 100644 --- a/src/cdrom/cdrom_image.c +++ b/src/cdrom/cdrom_image.c @@ -61,7 +61,7 @@ cdrom_image_log(const char *fmt, ...) static void image_get_tracks(cdrom_t *dev, int *first, int *last) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; TMSF tmsf; cdi_get_audio_tracks(img, first, last, &tmsf); @@ -70,7 +70,7 @@ image_get_tracks(cdrom_t *dev, int *first, int *last) static void image_get_track_info(cdrom_t *dev, uint32_t track, int end, track_info_t *ti) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; TMSF tmsf; cdi_get_audio_track_info(img, end, track, &ti->number, &tmsf, &ti->attr); @@ -83,7 +83,7 @@ image_get_track_info(cdrom_t *dev, uint32_t track, int end, track_info_t *ti) static void image_get_subchannel(cdrom_t *dev, uint32_t lba, subchannel_t *subc) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; TMSF rel_pos; TMSF abs_pos; @@ -105,7 +105,7 @@ image_get_subchannel(cdrom_t *dev, uint32_t lba, subchannel_t *subc) static int image_get_capacity(cdrom_t *dev) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; int first_track; int last_track; int number; @@ -130,7 +130,7 @@ image_get_capacity(cdrom_t *dev) static int image_is_track_audio(cdrom_t *dev, uint32_t pos, int ismsf) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; uint8_t attr; TMSF tmsf; int m; @@ -162,7 +162,7 @@ image_is_track_audio(cdrom_t *dev, uint32_t pos, int ismsf) static int image_is_track_pre(cdrom_t *dev, uint32_t lba) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; int track; /* GetTrack requires LBA. */ @@ -177,7 +177,7 @@ image_is_track_pre(cdrom_t *dev, uint32_t lba) static int image_sector_size(struct cdrom *dev, uint32_t lba) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; return cdi_get_sector_size(img, lba); } @@ -185,7 +185,7 @@ image_sector_size(struct cdrom *dev, uint32_t lba) static int image_read_sector(struct cdrom *dev, int type, uint8_t *b, uint32_t lba) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; switch (type) { case CD_READ_DATA: @@ -206,7 +206,7 @@ image_read_sector(struct cdrom *dev, int type, uint8_t *b, uint32_t lba) static int image_track_type(cdrom_t *dev, uint32_t lba) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; if (img) { if (image_is_track_audio(dev, lba, 0)) @@ -229,14 +229,14 @@ image_ext_medium_changed(cdrom_t *dev) static void image_exit(cdrom_t *dev) { - cd_img_t *img = (cd_img_t *) dev->image; + cd_img_t *img = (cd_img_t *) dev->local; cdrom_image_log("CDROM: image_exit(%s)\n", dev->image_path); dev->cd_status = CD_STATUS_EMPTY; if (img) { cdi_close(img); - dev->image = NULL; + dev->local = NULL; } dev->ops = NULL; @@ -245,6 +245,7 @@ image_exit(cdrom_t *dev) static const cdrom_ops_t cdrom_image_ops = { image_get_tracks, image_get_track_info, + NULL, image_get_subchannel, image_is_track_pre, image_sector_size, @@ -282,7 +283,7 @@ cdrom_image_open(cdrom_t *dev, const char *fn) return image_open_abort(dev); memset(img, 0, sizeof(cd_img_t)); - dev->image = img; + dev->local = img; /* Open the image. */ int i = cdi_set_device(img, fn); @@ -298,6 +299,12 @@ cdrom_image_open(cdrom_t *dev, const char *fn) dev->cd_buflen = 0; dev->cdrom_capacity = image_get_capacity(dev); cdrom_image_log("CD-ROM capacity: %i sectors (%" PRIi64 " bytes)\n", dev->cdrom_capacity, ((uint64_t) dev->cdrom_capacity) << 11ULL); + int cm, cs, cf; + cf = dev->cdrom_capacity % 75; + cs = (dev->cdrom_capacity / 75) % 60; + cm = (dev->cdrom_capacity / 75) / 60; + pclog("CD-ROM capacity: %i sectors (%" PRIi64 " bytes) (time: %02i:%02i:%02i)\n", + dev->cdrom_capacity, ((uint64_t) dev->cdrom_capacity - 150ULL) * 2352ULL, cm, cs, cf); /* Attach this handler to the drive. */ dev->ops = &cdrom_image_ops; diff --git a/src/cdrom/cdrom_image_backend.c b/src/cdrom/cdrom_image_backend.c index f1d5ed1f9..2578e9e33 100644 --- a/src/cdrom/cdrom_image_backend.c +++ b/src/cdrom/cdrom_image_backend.c @@ -1189,7 +1189,6 @@ cdi_load_cue(cd_img_t *cdi, const char *cuefile) /* Add lead out track. */ trk.number++; trk.track_number = 0xAA; - trk.attr = 0x16; /* Was 0x00 but I believe 0x16 is appropriate. */ trk.start = 0; trk.length = 0; trk.file = NULL; diff --git a/src/cdrom/cdrom_ioctl.c b/src/cdrom/cdrom_ioctl.c index 13df2d965..f25988bb9 100644 --- a/src/cdrom/cdrom_ioctl.c +++ b/src/cdrom/cdrom_ioctl.c @@ -28,9 +28,9 @@ #include <86box/config.h> #include <86box/path.h> #include <86box/plat.h> -#include <86box/plat_cdrom.h> #include <86box/scsi_device.h> #include <86box/cdrom.h> +#include <86box/plat_cdrom.h> #ifdef ENABLE_CDROM_IOCTL_LOG int cdrom_ioctl_do_log = ENABLE_CDROM_IOCTL_LOG; @@ -56,19 +56,19 @@ cdrom_ioctl_log(const char *fmt, ...) #define MSFtoLBA(m, s, f) ((((m * 60) + s) * 75) + f) static void -ioctl_get_tracks(UNUSED(cdrom_t *dev), int *first, int *last) +ioctl_get_tracks(cdrom_t *dev, int *first, int *last) { TMSF tmsf; - plat_cdrom_get_audio_tracks(first, last, &tmsf); + plat_cdrom_get_audio_tracks(dev->local, first, last, &tmsf); } static void -ioctl_get_track_info(UNUSED(cdrom_t *dev), uint32_t track, int end, track_info_t *ti) +ioctl_get_track_info(cdrom_t *dev, uint32_t track, int end, track_info_t *ti) { TMSF tmsf; - plat_cdrom_get_audio_track_info(end, track, &ti->number, &tmsf, &ti->attr); + plat_cdrom_get_audio_track_info(dev->local, end, track, &ti->number, &tmsf, &ti->attr); ti->m = tmsf.min; ti->s = tmsf.sec; @@ -76,13 +76,19 @@ ioctl_get_track_info(UNUSED(cdrom_t *dev), uint32_t track, int end, track_info_t } static void -ioctl_get_subchannel(UNUSED(cdrom_t *dev), uint32_t lba, subchannel_t *subc) +ioctl_get_raw_track_info(cdrom_t *dev, int *num, raw_track_info_t *rti) +{ + plat_cdrom_get_raw_track_info(dev->local, num, rti); +} + +static void +ioctl_get_subchannel(cdrom_t *dev, uint32_t lba, subchannel_t *subc) { TMSF rel_pos; TMSF abs_pos; if ((dev->cd_status == CD_STATUS_PLAYING) || (dev->cd_status == CD_STATUS_PAUSED)) { - const uint32_t trk = plat_cdrom_get_track_start(lba, &subc->attr, &subc->track); + const uint32_t trk = plat_cdrom_get_track_start(dev->local, lba, &subc->attr, &subc->track); FRAMES_TO_MSF(lba + 150, &abs_pos.min, &abs_pos.sec, &abs_pos.fr); @@ -91,7 +97,7 @@ ioctl_get_subchannel(UNUSED(cdrom_t *dev), uint32_t lba, subchannel_t *subc) subc->index = 1; } else - plat_cdrom_get_audio_sub(lba, &subc->attr, &subc->track, &subc->index, + plat_cdrom_get_audio_sub(dev->local, lba, &subc->attr, &subc->track, &subc->index, &rel_pos, &abs_pos); subc->abs_m = abs_pos.min; @@ -107,11 +113,11 @@ ioctl_get_subchannel(UNUSED(cdrom_t *dev), uint32_t lba, subchannel_t *subc) } static int -ioctl_get_capacity(UNUSED(cdrom_t *dev)) +ioctl_get_capacity(cdrom_t *dev) { int ret; - ret = plat_cdrom_get_last_block(); + ret = plat_cdrom_get_last_block(dev->local); cdrom_ioctl_log("GetCapacity=%x.\n", ret); return ret; } @@ -134,35 +140,35 @@ ioctl_is_track_audio(cdrom_t *dev, uint32_t pos, int ismsf) } /* GetTrack requires LBA. */ - return plat_cdrom_is_track_audio(pos); + return plat_cdrom_is_track_audio(dev->local, pos); } static int -ioctl_is_track_pre(UNUSED(cdrom_t *dev), uint32_t lba) +ioctl_is_track_pre(cdrom_t *dev, uint32_t lba) { - return plat_cdrom_is_track_pre(lba); + return plat_cdrom_is_track_pre(dev->local, lba); } static int -ioctl_sector_size(UNUSED(cdrom_t *dev), uint32_t lba) +ioctl_sector_size(cdrom_t *dev, uint32_t lba) { cdrom_ioctl_log("LBA=%x.\n", lba); - return plat_cdrom_get_sector_size(lba); + return plat_cdrom_get_sector_size(dev->local, lba); } static int -ioctl_read_sector(UNUSED(cdrom_t *dev), int type, uint8_t *b, uint32_t lba) +ioctl_read_sector(cdrom_t *dev, int type, uint8_t *b, uint32_t lba) { switch (type) { case CD_READ_DATA: cdrom_ioctl_log("cdrom_ioctl_read_sector(): Data.\n"); - return plat_cdrom_read_sector(b, 0, lba); + return plat_cdrom_read_sector(dev->local, b, 0, lba); case CD_READ_AUDIO: cdrom_ioctl_log("cdrom_ioctl_read_sector(): Audio.\n"); - return plat_cdrom_read_sector(b, 1, lba); + return plat_cdrom_read_sector(dev->local, b, 1, lba); case CD_READ_RAW: cdrom_ioctl_log("cdrom_ioctl_read_sector(): Raw.\n"); - return plat_cdrom_read_sector(b, 1, lba); + return plat_cdrom_read_sector(dev->local, b, 1, lba); default: cdrom_ioctl_log("cdrom_ioctl_read_sector(): Unknown CD read type.\n"); break; @@ -191,7 +197,7 @@ ioctl_ext_medium_changed(cdrom_t *dev) if ((dev->cd_status == CD_STATUS_PLAYING) || (dev->cd_status == CD_STATUS_PAUSED)) ret = 0; else - ret = plat_cdrom_ext_medium_changed(); + ret = plat_cdrom_ext_medium_changed(dev->local); if (ret == 1) { dev->cd_status = CD_STATUS_STOPPED; @@ -208,7 +214,8 @@ ioctl_exit(cdrom_t *dev) cdrom_ioctl_log("CDROM: ioctl_exit(%s)\n", dev->image_path); dev->cd_status = CD_STATUS_EMPTY; - plat_cdrom_close(); + plat_cdrom_close(dev->local); + dev->local = NULL; dev->ops = NULL; } @@ -216,6 +223,7 @@ ioctl_exit(cdrom_t *dev) static const cdrom_ops_t cdrom_ioctl_ops = { ioctl_get_tracks, ioctl_get_track_info, + ioctl_get_raw_track_info, ioctl_get_subchannel, ioctl_is_track_pre, ioctl_sector_size, @@ -238,9 +246,10 @@ int cdrom_ioctl_open(cdrom_t *dev, const char *drv) { const char *actual_drv = &(drv[8]); + int local_size = plat_cdrom_get_local_size(); /* Make sure to not STRCPY if the two are pointing - at the same place. */ + at the same place. */ if (drv != dev->image_path) strcpy(dev->image_path, drv); @@ -248,7 +257,9 @@ cdrom_ioctl_open(cdrom_t *dev, const char *drv) if (strstr(drv, "ioctl://") != drv) return cdrom_ioctl_open_abort(dev); cdrom_ioctl_log("actual_drv = %s\n", actual_drv); - int i = plat_cdrom_set_drive(actual_drv); + if (dev->local == NULL) + dev->local = calloc(1, local_size); + int i = plat_cdrom_set_drive(dev->local, actual_drv); if (!i) return cdrom_ioctl_open_abort(dev); diff --git a/src/chipset/scat.c b/src/chipset/scat.c index 02610efa2..d168b643f 100644 --- a/src/chipset/scat.c +++ b/src/chipset/scat.c @@ -1140,14 +1140,21 @@ scat_out(uint16_t port, uint8_t val, void *priv) if (indx >= 24) base_addr += 0x30000; + if ((base_addr >= 0x000a0000) && (base_addr < 0x00100000)) + mem_set_mem_state(base_addr, 0x00004000, MEM_READ_EXTANY | MEM_WRITE_EXTANY); + if ((dev->regs[SCAT_EMS_CONTROL] & 0x80) && (dev->page[indx].regs_2x9 & 0x80)) { virt_addr = get_addr(dev, base_addr, &dev->page[indx]); if (virt_addr < ((uint32_t) mem_size << 10)) mem_mapping_set_exec(&dev->ems_mapping[indx], ram + virt_addr); else mem_mapping_set_exec(&dev->ems_mapping[indx], NULL); - flushmmucache(); + + if ((base_addr >= 0x000a0000) && (base_addr < 0x00100000)) + mem_set_mem_state(base_addr, 0x00004000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); } + + flushmmucache(); } break; @@ -1163,6 +1170,9 @@ scat_out(uint16_t port, uint8_t val, void *priv) if (indx >= 24) base_addr += 0x30000; + if ((base_addr >= 0x000a0000) && (base_addr < 0x00100000)) + mem_set_mem_state(base_addr, 0x00004000, MEM_READ_EXTANY | MEM_WRITE_EXTANY); + if (dev->regs[SCAT_EMS_CONTROL] & 0x80) { if (val & 0x80) { virt_addr = get_addr(dev, base_addr, &dev->page[indx]); @@ -1175,6 +1185,9 @@ scat_out(uint16_t port, uint8_t val, void *priv) else mem_mapping_set_exec(&dev->ems_mapping[indx], NULL); mem_mapping_enable(&dev->ems_mapping[indx]); + + if ((base_addr >= 0x000a0000) && (base_addr < 0x00100000)) + mem_set_mem_state(base_addr, 0x00004000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); } else { mem_mapping_set_exec(&dev->ems_mapping[indx], ram + base_addr); mem_mapping_disable(&dev->ems_mapping[indx]); @@ -1495,7 +1508,7 @@ scat_init(const device_t *info) mem_mapping_add(&dev->ems_mapping[i], (i + 28) << 14, 0x04000, mem_read_scatb, mem_read_scatw, mem_read_scatl, mem_write_scatb, mem_write_scatw, mem_write_scatl, - ram + ((i + 28) << 14), 0, &dev->page[i]); + ram + ((i + 28) << 14), MEM_MAPPING_INTERNAL, &dev->page[i]); mem_mapping_disable(&dev->ems_mapping[i]); } } else { @@ -1508,7 +1521,7 @@ scat_init(const device_t *info) mem_read_scatb, mem_read_scatw, mem_read_scatl, mem_write_scatb, mem_write_scatw, mem_write_scatl, ram + ((i + (i >= 24 ? 28 : 16)) << 14), - 0, &dev->page[i]); + MEM_MAPPING_INTERNAL, &dev->page[i]); } } diff --git a/src/config.c b/src/config.c index 5a3f507c1..2f1561f18 100644 --- a/src/config.c +++ b/src/config.c @@ -895,9 +895,9 @@ load_storage_controllers(void) fatal("load_storage_controllers(): strlen(p) > 2047 " "(cassette_image_history[%i])\n", i); else - snprintf(cassette_image_history[i], (MAX_IMAGE_PATH_LEN - 1), "%s", p); + snprintf(cassette_image_history[i], MAX_IMAGE_PATH_LEN, "%s", p); } else - snprintf(cassette_image_history[i], (MAX_IMAGE_PATH_LEN - 1), "%s%s%s", usr_path, + snprintf(cassette_image_history[i], MAX_IMAGE_PATH_LEN, "%s%s%s", usr_path, path_get_slash(usr_path), p); path_normalize(cassette_image_history[i]); } @@ -960,9 +960,9 @@ load_storage_controllers(void) fatal("load_storage_controllers(): strlen(p) > 2047 " "(cart_image_history[%i][%i])\n", c, i); else - snprintf(cart_image_history[c][i], (MAX_IMAGE_PATH_LEN - 1), "%s", p); + snprintf(cart_image_history[c][i], MAX_IMAGE_PATH_LEN, "%s", p); } else - snprintf(cart_image_history[c][i], (MAX_IMAGE_PATH_LEN - 1), "%s%s%s", usr_path, + snprintf(cart_image_history[c][i], MAX_IMAGE_PATH_LEN, "%s%s%s", usr_path, path_get_slash(usr_path), p); path_normalize(cart_image_history[c][i]); } @@ -1259,9 +1259,9 @@ load_floppy_and_cdrom_drives(void) fatal("load_floppy_and_cdrom_drives(): strlen(p) > 2047 " "(fdd_image_history[%i][%i])\n", c, i); else - snprintf(fdd_image_history[c][i], (MAX_IMAGE_PATH_LEN - 1), "%s", p); + snprintf(fdd_image_history[c][i], MAX_IMAGE_PATH_LEN, "%s", p); } else - snprintf(fdd_image_history[c][i], (MAX_IMAGE_PATH_LEN - 1), "%s%s%s", usr_path, + snprintf(fdd_image_history[c][i], MAX_IMAGE_PATH_LEN, "%s%s%s", usr_path, path_get_slash(usr_path), p); path_normalize(fdd_image_history[c][i]); } @@ -1370,9 +1370,9 @@ load_floppy_and_cdrom_drives(void) fatal("load_floppy_and_cdrom_drives(): strlen(p) > 2047 " "(cdrom[%i].image_history[%i])\n", c, i); else - snprintf(cdrom[c].image_history[i], (MAX_IMAGE_PATH_LEN - 1), "%s", p); + snprintf(cdrom[c].image_history[i], MAX_IMAGE_PATH_LEN, "%s", p); } else - snprintf(cdrom[c].image_history[i], (MAX_IMAGE_PATH_LEN - 1), "%s%s%s", usr_path, + snprintf(cdrom[c].image_history[i], MAX_IMAGE_PATH_LEN, "%s%s%s", usr_path, path_get_slash(usr_path), p); path_normalize(cdrom[c].image_history[i]); } @@ -1500,9 +1500,9 @@ load_other_removable_devices(void) fatal("load_other_removable_devices(): strlen(p) > 2047 " "(zip_drives[%i].image_history[%i])\n", c, i); else - snprintf(zip_drives[c].image_history[i], (MAX_IMAGE_PATH_LEN - 1), "%s", p); + snprintf(zip_drives[c].image_history[i], MAX_IMAGE_PATH_LEN, "%s", p); } else - snprintf(zip_drives[c].image_history[i], (MAX_IMAGE_PATH_LEN - 1), "%s%s%s", usr_path, + snprintf(zip_drives[c].image_history[i], MAX_IMAGE_PATH_LEN, "%s%s%s", usr_path, path_get_slash(usr_path), p); path_normalize(zip_drives[c].image_history[i]); } @@ -1613,9 +1613,9 @@ load_other_removable_devices(void) fatal("load_other_removable_devices(): strlen(p) > 2047 " "(mo_drives[%i].image_history[%i])\n", c, i); else - snprintf(mo_drives[c].image_history[i], (MAX_IMAGE_PATH_LEN - 1), "%s", p); + snprintf(mo_drives[c].image_history[i], MAX_IMAGE_PATH_LEN, "%s", p); } else - snprintf(mo_drives[c].image_history[i], (MAX_IMAGE_PATH_LEN - 1), "%s%s%s", usr_path, + snprintf(mo_drives[c].image_history[i], MAX_IMAGE_PATH_LEN, "%s%s%s", usr_path, path_get_slash(usr_path), p); path_normalize(mo_drives[c].image_history[i]); } diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 8ff204855..351fe2fc2 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -2612,7 +2612,7 @@ cpu_ven_reset(void) void cpu_RDMSR(void) { - if (CPL) + if ((CPL || (cpu_state.eflags & VM_FLAG)) && (cr0 & 1)) x86gpf(NULL, 0); else switch (cpu_s->cpu_type) { case CPU_IBM386SLC: @@ -3469,7 +3469,7 @@ cpu_WRMSR(void) cpu_log("WRMSR %08X %08X%08X\n", ECX, EDX, EAX); - if (CPL) + if ((CPL || (cpu_state.eflags & VM_FLAG)) && (cr0 & 1)) x86gpf(NULL, 0); else switch (cpu_s->cpu_type) { case CPU_IBM386SLC: diff --git a/src/cpu/x86_ops_misc.h b/src/cpu/x86_ops_misc.h index f3e4bb353..c0b9de437 100644 --- a/src/cpu/x86_ops_misc.h +++ b/src/cpu/x86_ops_misc.h @@ -878,7 +878,7 @@ opINVD(uint32_t fetchdat) static int opWBINVD(uint32_t fetchdat) { - if (CPL) { + if ((CPL || (cpu_state.eflags & VM_FLAG)) && (cr0 & 1)) { x86gpf(NULL, 0); return 1; } diff --git a/src/device.c b/src/device.c index 46125f58e..2f5a3cdad 100644 --- a/src/device.c +++ b/src/device.c @@ -111,6 +111,14 @@ device_set_context(device_context_t *c, const device_t *dev, int inst) void * old_sec = config_find_section("Standard PS/2 Mouse"); if ((sec == NULL) && (old_sec != NULL)) config_rename_section(old_sec, c->name); + } else if (!strcmp(dev->name, "Microsoft RAMCard")) { + sprintf(c->name, "%s", dev->name); + + /* Migrate the old "Standard PS/2 Mouse" section */ + const void *sec = config_find_section(c->name); + void * old_sec = config_find_section("Microsoft RAMCard for IBM PC"); + if ((sec == NULL) && (old_sec != NULL)) + config_rename_section(old_sec, c->name); } else sprintf(c->name, "%s", dev->name); } diff --git a/src/device/isamem.c b/src/device/isamem.c index 4ff819d67..a3baf466d 100644 --- a/src/device/isamem.c +++ b/src/device/isamem.c @@ -1080,7 +1080,7 @@ static const device_config_t msramcard_config[] = { }; static const device_t msramcard_device = { - .name = "Microsoft RAMCard for IBM PC", + .name = "Microsoft RAMCard", .internal_name = "msramcard", .flags = DEVICE_ISA, .local = ISAMEM_RAMCARD_CARD, diff --git a/src/device/serial.c b/src/device/serial.c index 2b832f750..dcaff0f7f 100644 --- a/src/device/serial.c +++ b/src/device/serial.c @@ -197,6 +197,8 @@ serial_receive_timer(void *priv) /* Raise Data Ready interrupt. */ dev->lsr |= 0x01; dev->int_status |= SERIAL_INT_RECEIVE; + if (dev->lsr & 0x02) + dev->int_status |= SERIAL_INT_LSR; serial_update_ints(dev); } @@ -266,10 +268,6 @@ serial_move_to_txsr(serial_t *dev) /* Update interrupts to signal THRE and that TXSR is no longer empty. */ serial_update_ints(dev); } - if (dev->transmit_enabled & 2) - dev->baud_cycles++; - else - dev->baud_cycles = 0; /* If not moving while transmitting, reset BAUDOUT cycle count. */ if (!dev->fifo_enabled || (fifo_get_count(dev->xmit_fifo) == 0x0)) dev->transmit_enabled &= ~1; /* Stop moving. */ dev->transmit_enabled |= 2; /* Start transmitting. */ @@ -303,16 +301,26 @@ static void serial_transmit_timer(void *priv) { serial_t *dev = (serial_t *) priv; - int delay = 8; /* STOP to THRE delay is 8 BAUDOUT cycles. */ + /* + Norton Diagnostics waits for up to 2 bit periods, this is + confirmed by the NS16550A timings graph, which shows operation + as follows after write: 1 bit of delay, then start bit, and at + the end of the start bit, move from THR to TXSR. + */ + int delay = 1; if (dev->transmit_enabled & 3) { + /* + If already transmitting, move from THR to TXSR at the end of + the last data bit. + */ if ((dev->transmit_enabled & 1) && (dev->transmit_enabled & 2)) - delay = dev->data_bits; /* Delay by less if already transmitting. */ + delay = dev->data_bits + 1; dev->baud_cycles++; - /* We have processed (total bits) BAUDOUT cycles, transmit the byte. */ - if ((dev->baud_cycles == dev->bits) && (dev->transmit_enabled & 2)) + /* We have processed (delay + total bits) BAUDOUT cycles, transmit the byte. */ + if ((dev->baud_cycles == (dev->bits + 1)) && (dev->transmit_enabled & 2)) serial_process_txsr(dev); /* We have processed (data bits) BAUDOUT cycles. */ @@ -614,6 +622,11 @@ serial_write(uint16_t addr, uint8_t val, void *priv) dev->msr = new_msr; + if (dev->msr & 0x0f) { + dev->int_status |= SERIAL_INT_MSR; + serial_update_ints(dev); + } + /* TODO: Why reset the FIFO's here?! */ fifo_reset(dev->xmit_fifo); fifo_reset(dev->rcvr_fifo); diff --git a/src/disk/hdc_ide.c b/src/disk/hdc_ide.c index 660093a8e..b868f51a0 100644 --- a/src/disk/hdc_ide.c +++ b/src/disk/hdc_ide.c @@ -558,7 +558,10 @@ ide_hd_identify(const ide_t *ide) /* Firmware */ ide_padstr((char *) (ide->buffer + 23), EMU_VERSION_EX, 8); /* Model */ - ide_padstr((char *) (ide->buffer + 27), device_identify, 40); + if (hdd[ide->hdd_num].model) + ide_padstr((char *) (ide->buffer + 27), hdd[ide->hdd_num].model, 40); + else + ide_padstr((char *) (ide->buffer + 27), device_identify, 40); /* Fixed drive */ ide->buffer[0] = (1 << 6); /* Buffer type */ diff --git a/src/disk/hdd.c b/src/disk/hdd.c index 7acfc82be..ea38602d2 100644 --- a/src/disk/hdd.c +++ b/src/disk/hdd.c @@ -415,7 +415,10 @@ static hdd_preset_t hdd_speed_presets[] = { { .name = "[1997] 5400 RPM", .internal_name = "1997_5400rpm", .zones = 16, .avg_spt = 185, .heads = 6, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2.5, .rcache_num_seg = 8, .rcache_seg_size = 64, .max_multiple = 32 }, { .name = "[1998] 5400 RPM", .internal_name = "1998_5400rpm", .zones = 16, .avg_spt = 300, .heads = 8, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 32 }, { .name = "[2000] 7200 RPM", .internal_name = "2000_7200rpm", .zones = 16, .avg_spt = 350, .heads = 6, .rpm = 7200, .full_stroke_ms = 15, .track_seek_ms = 2, .rcache_num_seg = 16, .rcache_seg_size = 128, .max_multiple = 32 }, - // clang-format on + { .name = "Conner CP3024", .internal_name = "CP3024", .model = "Conner Peripherals 20MB - CP3024", .zones = 1, .avg_spt = 33, .heads = 2, .rpm = 3500, .full_stroke_ms = 50, .track_seek_ms = 8, .rcache_num_seg = 1, .rcache_seg_size = 8, .max_multiple = 8 }, + { .name = "Conner CP3044", .internal_name = "CP3044", .model = "Conner Peripherals 40MB - CP3044", .zones = 1, .avg_spt = 40, .heads = 2, .rpm = 3500, .full_stroke_ms = 50, .track_seek_ms = 8, .rcache_num_seg = 1, .rcache_seg_size = 8, .max_multiple = 8 }, + { .name = "Conner CP3104", .internal_name = "CP3104", .model = "Conner Peripherals 104MB - CP3104", .zones = 1, .avg_spt = 33, .heads = 8, .rpm = 3500, .full_stroke_ms = 45, .track_seek_ms = 8, .rcache_num_seg = 4, .rcache_seg_size = 8, .max_multiple = 8 }, + // clang-format on }; int @@ -472,6 +475,8 @@ hdd_preset_apply(int hdd_id) hd->cache.num_segments = preset->rcache_num_seg; hd->cache.segment_size = preset->rcache_seg_size; hd->max_multiple_block = preset->max_multiple; + if (preset->model) + hd->model = preset->model; if (!hd->speed_preset) return; diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index 0c18f2f30..546174c57 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -177,15 +177,13 @@ fdc_ctrl_reset(void *priv) { fdc_t *fdc = (fdc_t *) priv; - fdc->stat = 0x80; + fdc->stat = 0x80; fdc->pnum = fdc->ptot = 0; fdc->st0 = 0; fdc->lock = 0; fdc->head = 0; fdc->step = 0; fdc->power_down = 0; - if (!(fdc->flags & FDC_FLAG_AT)) - fdc->rate = 2; } sector_id_t @@ -654,13 +652,31 @@ fdc_sis(fdc_t *fdc) fdc->paramstogo = 2; } +static void +fdc_soft_reset(fdc_t *fdc) +{ + if (fdc->power_down) { + timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); + fdc->interrupt = -5; + } else { + timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); + fdc->interrupt = -1; + + fdc->perp &= 0xfc; + + for (int i = 0; i < FDD_NUM; i++) + ui_sb_update_icon(SB_FLOPPY | i, 0); + + fdc_ctrl_reset(fdc); + } +} + static void fdc_write(uint16_t addr, uint8_t val, void *priv) { fdc_t *fdc = (fdc_t *) priv; int drive; - int i; int drive_num; fdc_log("Write FDC %04X %02X\n", addr, val); @@ -684,7 +700,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) fdc->interrupt = -1; ui_sb_update_icon(SB_FLOPPY | 0, 0); fdc_ctrl_reset(fdc); - fdd_changed[0] = 1; } if (!fdd_get_flags(0)) val &= 0xfe; @@ -701,24 +716,10 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) fdc->stat = 0x00; fdc->pnum = fdc->ptot = 0; } - if ((val & 4) && !(fdc->dor & 4)) { - if (fdc->power_down) { - timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); - fdc->interrupt = -5; - } else { - timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); - fdc->interrupt = -1; - - fdc->perp &= 0xfc; - - for (i = 0; i < FDD_NUM; i++) - ui_sb_update_icon(SB_FLOPPY | i, 0); - - fdc_ctrl_reset(fdc); - } - } + if ((val & 4) && !(fdc->dor & 4)) + fdc_soft_reset(fdc); /* We can now simplify this since each motor now spins separately. */ - for (i = 0; i < FDD_NUM; i++) { + for (int i = 0; i < FDD_NUM; i++) { drive_num = real_drive(fdc, i); if ((!fdd_get_flags(drive_num)) || (drive_num >= FDD_NUM)) val &= ~(0x10 << drive_num); @@ -737,28 +738,14 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) fdc_update_rwc(fdc, drive, (val & 0x30) >> 4); } return; - case 4: + case 4: /* DSR */ if (!(fdc->flags & FDC_FLAG_NO_DSR_RESET)) { if (!(val & 0x80)) { timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); fdc->interrupt = -6; } - if (fdc->power_down || ((val & 0x80) && !(fdc->dsr & 0x80))) { - if (fdc->power_down) { - timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); - fdc->interrupt = -5; - } else { - timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); - fdc->interrupt = -1; - - fdc->perp &= 0xfc; - - for (i = 0; i < FDD_NUM; i++) - ui_sb_update_icon(SB_FLOPPY | i, 0); - - fdc_ctrl_reset(fdc); - } - } + if (fdc->power_down || ((val & 0x80) && !(fdc->dsr & 0x80))) + fdc_soft_reset(fdc); } fdc->dsr = val; return; @@ -1211,7 +1198,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) if (!(fdc->flags & FDC_FLAG_TOSHIBA) && !(fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_UMC)) return; fdc->rate = val & 0x03; - if (fdc->flags & FDC_FLAG_PS1) + if (fdc->flags & FDC_FLAG_PS2) fdc->noprec = !!(val & 0x04); return; @@ -1231,23 +1218,43 @@ fdc_read(uint16_t addr, void *priv) if (!fdc->power_down || ((addr & 7) == 2)) switch (addr & 7) { case 0: /* STA */ - if (fdc->flags & FDC_FLAG_PS1) { + if (fdc->flags & FDC_FLAG_PS2) { drive = real_drive(fdc, fdc->dor & 3); ret = 0x00; /* TODO: Bit 2: INDEX (best return always 0 as it goes by very fast) */ - if (fdc->seek_dir) /* nDIRECTION */ + if (fdc->seek_dir) /* nDIRECTION */ ret |= 0x01; - if (writeprot[drive]) /* WRITEPROT */ + if (writeprot[drive]) /* WRITEPROT */ ret |= 0x02; - if (!fdd_get_head(drive)) /* nHDSEL */ + if (!fdd_get_head(drive)) /* nHDSEL */ ret |= 0x08; - if (fdd_track0(drive)) /* TRK0 */ + if (fdd_track0(drive)) /* TRK0 */ ret |= 0x10; - if (fdc->step) /* STEP */ + if (fdc->step) /* STEP */ ret |= 0x20; - if (dma_get_drq(fdc->dma_ch)) /* DRQ */ + if (dma_get_drq(fdc->dma_ch)) /* DRQ */ + ret |= 0x40; + if (fdc->fintr || fdc->reset_stat) /* INTR */ + ret |= 0x80; + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x04; + /* TODO: + Bit 2: nINDEX (best return always 1 as it goes by very fast) + */ + if (!fdc->seek_dir) /* DIRECTION */ + ret |= 0x01; + if (!writeprot[drive]) /* nWRITEPROT */ + ret |= 0x02; + if (fdd_get_head(drive)) /* HDSEL */ + ret |= 0x08; + if (!fdd_track0(drive)) /* nTRK0 */ + ret |= 0x10; + if (fdc->step) /* STEP */ + ret |= 0x20; + if (!fdd_get_type(1)) /* -Drive 2 Installed */ ret |= 0x40; if (fdc->fintr || fdc->reset_stat) /* INTR */ ret |= 0x80; @@ -1255,14 +1262,12 @@ fdc_read(uint16_t addr, void *priv) ret = 0xff; break; case 1: /* STB */ - if (fdc->flags & FDC_FLAG_PS1) { + if (fdc->flags & FDC_FLAG_PS2) { drive = real_drive(fdc, fdc->dor & 3); ret = 0x00; - /* -Drive 2 Installed */ - if (!fdd_get_type(1)) + if (!fdd_get_type(1)) /* -Drive 2 Installed */ ret |= 0x80; - /* -Drive Select 1,0 */ - switch (drive) { + switch (drive) { /* -Drive Select 1,0 */ case 0: ret |= 0x43; break; @@ -1279,6 +1284,11 @@ fdc_read(uint16_t addr, void *priv) default: break; } + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0xc0; + ret |= (fdc->dor & 0x01) << 5; /* Drive Select 0 */ + ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ } else { if (is486 || !fdc->enable_3f1) ret = 0xff; @@ -1287,19 +1297,12 @@ fdc_read(uint16_t addr, void *priv) drive = real_drive(fdc, fdc->dor & 1); ret = !fdd_is_dd(drive) ? ((fdc->dor & 1) ? 2 : 1) : 0; } else { - ret = 0x70; - + /* TODO: What is this and what is it used for? + It's almost identical to the PS/2 MCA mode. */ drive = real_drive(fdc, fdc->dor & 3); - - if (drive) - ret &= ~0x40; - else - ret &= ~0x20; - - if (fdc->dor & 0x10) - ret |= 1; - if (fdc->dor & 0x20) - ret |= 2; + ret = 0x70; + ret &= ~(drive ? 0x40 : 0x20); + ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ } } } @@ -1309,7 +1312,8 @@ fdc_read(uint16_t addr, void *priv) break; case 3: drive = real_drive(fdc, fdc->dor & 3); - if (fdc->flags & FDC_FLAG_PS1) { + /* TODO: FDC_FLAG_PS2_TDR? */ + if ((fdc->flags & FDC_FLAG_PS2) || (fdc->flags & FDC_FLAG_PS2_MCA)) { /* PS/1 Model 2121 seems return drive type in port * 0x3f3, despite the 82077AA fdc_t not implementing * this. This is presumably implemented outside the @@ -1371,7 +1375,7 @@ fdc_read(uint16_t addr, void *priv) case 7: /*Disk change*/ drive = real_drive(fdc, fdc->dor & 3); - if (fdc->flags & FDC_FLAG_PS1) { + if (fdc->flags & FDC_FLAG_PS2) { if (fdc->dor & (0x10 << drive)) { ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x00 : 0x80; ret |= (fdc->dor & 0x08); @@ -1379,6 +1383,14 @@ fdc_read(uint16_t addr, void *priv) ret |= (fdc->rate & 0x03); } else ret = 0x00; + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + if (fdc->dor & (0x10 << drive)) { + ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; + ret |= ((fdc->rate & 0x03) << 1); + ret |= fdc_get_densel(fdc, drive); + ret |= 0x78; + } else + ret = 0xf9; } else { if (fdc->dor & (0x10 << drive)) { if ((drive == 1) && (fdc->flags & FDC_FLAG_TOSHIBA)) @@ -1411,7 +1423,7 @@ static void fdc_poll_common_finish(fdc_t *fdc, int compare, int st5) { fdc_int(fdc, 1); - if (!(fdc->flags & FDC_FLAG_PS1)) + if (!(fdc->flags & FDC_FLAG_FINTR)) fdc->fintr = 0; fdc->stat = 0xD0; fdc->st0 = fdc->res[4] = (fdd_get_head(real_drive(fdc, fdc->drive)) ? 4 : 0) | fdc->rw_drive; @@ -1712,7 +1724,7 @@ fdc_callback(void *priv) } else { fdc->interrupt = -2; fdc_int(fdc, 1); - if (!(fdc->flags & FDC_FLAG_PS1)) + if (!(fdc->flags & FDC_FLAG_FINTR)) fdc->fintr = 0; fdc->stat = 0xD0; fdc->st0 = fdc->res[4] = (fdd_get_head(real_drive(fdc, fdc->drive)) ? 4 : 0) | fdc->drive; @@ -1804,7 +1816,7 @@ fdc_error(fdc_t *fdc, int st5, int st6) timer_disable(&fdc->timer); fdc_int(fdc, 1); - if (!(fdc->flags & FDC_FLAG_PS1)) + if (!(fdc->flags & FDC_FLAG_FINTR)) fdc->fintr = 0; fdc->stat = 0xD0; fdc->st0 = fdc->res[4] = 0x40 | (fdd_get_head(real_drive(fdc, fdc->drive)) ? 4 : 0) | fdc->rw_drive; @@ -2213,7 +2225,7 @@ fdc_reset(void *priv) fdc->enable_3f1 = 1; fdc_update_enh_mode(fdc, 0); - if (fdc->flags & FDC_FLAG_PS1) + if (fdc->flags & FDC_FLAG_DENSEL_INVERT) fdc_update_densel_polarity(fdc, 0); else fdc_update_densel_polarity(fdc, 1); @@ -2259,6 +2271,9 @@ fdc_reset(void *priv) fdc_ctrl_reset(fdc); + if (!(fdc->flags & FDC_FLAG_AT)) + fdc->rate = 2; + fdc->max_track = (fdc->flags & FDC_FLAG_MORE_TRACKS) ? 85 : 79; fdc_remove(fdc); @@ -2443,6 +2458,20 @@ const device_t fdc_xt_tandy_device = { .config = NULL }; +const device_t fdc_xt_umc_um8398_device = { + .name = "PC/XT Floppy Drive Controller (UMC UM8398)", + .internal_name = "fdc_xt_umc_um8398", + .flags = 0, + .local = FDC_FLAG_UMC, + .init = fdc_init, + .close = fdc_close, + .reset = fdc_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + const device_t fdc_pcjr_device = { .name = "PCjr Floppy Drive Controller", .internal_name = "fdc_pcjr", @@ -2527,34 +2556,6 @@ const device_t fdc_at_actlow_device = { .config = NULL }; -const device_t fdc_at_ps1_device = { - .name = "PC/AT Floppy Drive Controller (PS/1, PS/2 ISA)", - .internal_name = "fdc_at_ps1", - .flags = 0, - .local = FDC_FLAG_DISKCHG_ACTLOW | FDC_FLAG_AT | FDC_FLAG_PS1, - .init = fdc_init, - .close = fdc_close, - .reset = fdc_reset, - { .available = NULL }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = NULL -}; - -const device_t fdc_at_ps1_2121_device = { - .name = "PC/AT Floppy Drive Controller (PS/1, PS/2 ISA)", - .internal_name = "fdc_at_ps1", - .flags = 0, - .local = FDC_FLAG_NO_DSR_RESET | FDC_FLAG_DISKCHG_ACTLOW | FDC_FLAG_AT | FDC_FLAG_PS1, - .init = fdc_init, - .close = fdc_close, - .reset = fdc_reset, - { .available = NULL }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = NULL -}; - const device_t fdc_at_smc_device = { .name = "PC/AT Floppy Drive Controller (SM(s)C FDC37Cxxx)", .internal_name = "fdc_at_smc", @@ -2611,9 +2612,9 @@ const device_t fdc_at_nsc_device = { .config = NULL }; -const device_t fdc_dp8473_device = { - .name = "NS DP8473 Floppy Drive Controller", - .internal_name = "fdc_dp8473", +const device_t fdc_at_nsc_dp8473_device = { + .name = "PC/AT Floppy Drive Controller (NSC DP8473)", + .internal_name = "fdc_at_nsc_dp8473", .flags = 0, .local = FDC_FLAG_AT | FDC_FLAG_NEC | FDC_FLAG_NO_DSR_RESET, .init = fdc_init, @@ -2625,11 +2626,27 @@ const device_t fdc_dp8473_device = { .config = NULL }; -const device_t fdc_um8398_device = { - .name = "UMC UM8398 Floppy Drive Controller", - .internal_name = "fdc_um8398", +const device_t fdc_ps2_device = { + .name = "PS/2 Model 25/30 Floppy Drive Controller", + .internal_name = "fdc_ps2", .flags = 0, - .local = FDC_FLAG_UMC, + .local = FDC_FLAG_FINTR | FDC_FLAG_DENSEL_INVERT | FDC_FLAG_NO_DSR_RESET | FDC_FLAG_DISKCHG_ACTLOW | + FDC_FLAG_AT | FDC_FLAG_PS2, + .init = fdc_init, + .close = fdc_close, + .reset = fdc_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const device_t fdc_ps2_mca_device = { + .name = "PS/2 MCA Floppy Drive Controller", + .internal_name = "fdc_ps2_mca", + .flags = 0, + .local = FDC_FLAG_FINTR | FDC_FLAG_DENSEL_INVERT | FDC_FLAG_NO_DSR_RESET | FDC_FLAG_AT | + FDC_FLAG_PS2_MCA, .init = fdc_init, .close = fdc_close, .reset = fdc_reset, diff --git a/src/floppy/fdc_magitronic.c b/src/floppy/fdc_magitronic.c index f607267ef..306440b9c 100644 --- a/src/floppy/fdc_magitronic.c +++ b/src/floppy/fdc_magitronic.c @@ -95,7 +95,7 @@ b215_init(UNUSED(const device_t *info)) rom_init(&dev->rom, ROM_B215, ROM_ADDR, 0x2000, 0x1fff, 0, MEM_MAPPING_EXTERNAL); - dev->fdc_controller = device_add(&fdc_um8398_device); + dev->fdc_controller = device_add(&fdc_xt_umc_um8398_device); io_sethandler(FDC_PRIMARY_ADDR, 1, b215_read, NULL, NULL, NULL, NULL, NULL, dev); return dev; diff --git a/src/floppy/fdc_pii15xb.c b/src/floppy/fdc_pii15xb.c index cc998e8e1..013d6d39b 100644 --- a/src/floppy/fdc_pii15xb.c +++ b/src/floppy/fdc_pii15xb.c @@ -75,7 +75,7 @@ MiniMicro 4 also won't work with the XT FDC which the Zilog claims to be. #include <86box/fdc_ext.h> #define DTK_VARIANT ((info->local == 158) ? ROM_PII_158B : ROM_PII_151B) -#define DTK_CHIP ((info->local == 158) ? &fdc_xt_device : &fdc_dp8473_device) +#define DTK_CHIP ((info->local == 158) ? &fdc_xt_device : &fdc_at_nsc_dp8473_device) #define BIOS_ADDR (uint32_t)(device_get_config_hex20("bios_addr") & 0x000fffff) #define ROM_PII_151B "roms/floppy/dtk/pii-151b.rom" #define ROM_PII_158B "roms/floppy/dtk/pii-158b.rom" diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index 81edcc8b8..3a0b03a9b 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -26,6 +26,7 @@ #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/timer.h> +#include <86box/machine.h> #include <86box/path.h> #include <86box/plat.h> #include <86box/ui.h> @@ -153,20 +154,16 @@ static const struct { { 43, FLAG_RPM_300 | FLAG_525 | FLAG_DS | FLAG_HOLE0, "5.25\" 360k", "525_2dd" }, /* 5.25" QD */ { 86, FLAG_RPM_300 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "5.25\" 720k", "525_2qd" }, - /* 5.25" HD PS/2 */ - { 86, FLAG_RPM_360 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP | FLAG_INVERT_DENSEL | FLAG_PS2, "5.25\" 1.2M PS/2", "525_2hd_ps2" }, /* 5.25" HD */ - { 86, FLAG_RPM_360 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "5.25\" 1.2M", "525_2hd" }, + { 86, FLAG_RPM_360 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP | FLAG_PS2, "5.25\" 1.2M", "525_2hd" }, /* 5.25" HD Dual RPM */ { 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "5.25\" 1.2M 300/360 RPM", "525_2hd_dualrpm" }, /* 3.5" 1DD */ { 86, FLAG_RPM_300 | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "3.5\" 360k", "35_1dd" }, /* 3.5" DD, Equivalent to TEAC FD-235F */ { 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "3.5\" 720k", "35_2dd" }, - /* 3.5" HD PS/2 */ - { 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP | FLAG_INVERT_DENSEL | FLAG_PS2, "3.5\" 1.44M PS/2", "35_2hd_ps2" }, /* 3.5" HD, Equivalent to TEAC FD-235HF */ - { 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "3.5\" 1.44M", "35_2hd" }, + { 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP | FLAG_PS2, "3.5\" 1.44M", "35_2hd" }, /* TODO: 3.5" DD, Equivalent to TEAC FD-235GF */ // { 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "3.5\" 1.25M", "35_2hd_2mode" }, /* 3.5" HD PC-98 */ @@ -214,10 +211,19 @@ fdd_get_internal_name(int type) int fdd_get_from_internal_name(char *s) { - int c = 0; + int c = 0; + char *n; + + /* TODO: Remove this once the migration period is over. */ + if (!strcmp(s, "525_2hd_ps2")) + n = "525_2hd"; + else if (!strcmp(s, "35_2hd_ps2")) + n = "35_2hd"; + else + n = s; while (strlen(drive_types[c].internal_name)) { - if (!strcmp((char *) drive_types[c].internal_name, s)) + if (!strcmp((char *) drive_types[c].internal_name, n)) return c; c++; } @@ -282,11 +288,32 @@ fdd_current_track(int drive) return fdd[drive].track; } +static int +fdd_type_invert_densel(int type) +{ + int ret; + + if (drive_types[type].flags & FLAG_PS2) + ret = !!strstr(machine_getname(), "PS/"); + else + ret = drive_types[type].flags & FLAG_INVERT_DENSEL; + + return ret; +} + +static int +fdd_invert_densel(int drive) +{ + int ret = fdd_type_invert_densel(fdd[drive].type); + + return ret; +} + void fdd_set_densel(int densel) { for (uint8_t i = 0; i < FDD_NUM; i++) { - if (drive_types[fdd[i].type].flags & FLAG_INVERT_DENSEL) + if (fdd_invert_densel(i)) fdd[i].densel = densel ^ 1; else fdd[i].densel = densel; @@ -302,7 +329,7 @@ fdd_getrpm(int drive) hole = fdd_hole(drive); densel = fdd[drive].densel; - if (drive_types[fdd[drive].type].flags & FLAG_INVERT_DENSEL) + if (fdd_invert_densel(drive)) densel ^= 1; if (!(drive_types[fdd[drive].type].flags & FLAG_RPM_360)) @@ -340,10 +367,9 @@ fdd_doublestep_40(int drive) void fdd_set_type(int drive, int type) { - int old_type = fdd[drive].type; - fdd[drive].type = type; - if ((drive_types[old_type].flags ^ drive_types[type].flags) & FLAG_INVERT_DENSEL) + if (fdd_type_invert_densel(fdd[drive].type) != fdd_type_invert_densel(type)) fdd[drive].densel ^= 1; + fdd[drive].type = type; } int diff --git a/src/floppy/fdd_86f.c b/src/floppy/fdd_86f.c index cf9e83694..0e8a6590a 100644 --- a/src/floppy/fdd_86f.c +++ b/src/floppy/fdd_86f.c @@ -2911,6 +2911,7 @@ d86f_decompose_encoded_buffer(int drive, int side) uint16_t temp2; uint32_t len; const uint16_t *dst = dev->track_encoded_data[side]; + const uint16_t *dst_s = dev->track_surface_data[side]; uint16_t *src1 = dev->thin_track_encoded_data[0][side]; uint16_t *src1_s = dev->thin_track_surface_data[0][side]; uint16_t *src2 = dev->thin_track_encoded_data[1][side]; @@ -2922,12 +2923,13 @@ d86f_decompose_encoded_buffer(int drive, int side) if (d86f_has_surface_desc(drive)) { /* Source image has surface description data, so we have some more handling to do. We need hole masks for both buffers. Holes have data bit clear and surface bit set. */ - temp = ~src1[i] & src1_s[i]; - temp2 = ~src2[i] & src2_s[i]; - src1[i] = dst[i] & ~temp; - src1_s[i] = temp; - src2[i] = dst[i] & ~temp2; - src2_s[i] = temp2; + src1_s[i] = src2_s[i] = dst_s[i]; /* Write the new holes and weak bits. */ + temp = ~src1[i] & src1_s[i]; /* Bits that are clear in data and set in surface are holes. */ + temp2 = ~src2[i] & src2_s[i]; /* Bits that are clear in data and set in surface are holes. */ + src1[i] = dst[i] & ~temp; /* Make sure the holes' bits are cleared in the decomposed buffer. */ + src1_s[i] |= temp; /* Make sure the holes' bits are set in the decomposed surface. */ + src2[i] = dst[i] & ~temp2; /* Make sure the holes' bits are cleared in the decomposed buffer. */ + src2_s[i] |= temp2; /* Make sure the holes' bits are set in the decomposed surface. */ } else src1[i] = src2[i] = dst[i]; } diff --git a/src/game/gameport.c b/src/game/gameport.c index 58d9a446f..e7495e365 100644 --- a/src/game/gameport.c +++ b/src/game/gameport.c @@ -13,10 +13,12 @@ * Authors: Miran Grca, * Sarah Walker, * RichardG, + * Jasmine Iwanek, * - * Copyright 2016-2018 Miran Grca. + * Copyright 2016-2022 Miran Grca. * Copyright 2008-2018 Sarah Walker. * Copyright 2021 RichardG. + * Copyright 2021-2024 Jasmine Iwanek. */ #include #include @@ -468,7 +470,7 @@ const device_t gameport_device = { .name = "Game port", .internal_name = "gameport", .flags = 0, - .local = 0x080200, + .local = GAMEPORT_8ADDR | 0x0200, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -482,7 +484,7 @@ const device_t gameport_201_device = { .name = "Game port (Port 201h only)", .internal_name = "gameport_201", .flags = 0, - .local = 0x010201, + .local = GAMEPORT_1ADDR | 0x0201, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -496,7 +498,7 @@ const device_t gameport_203_device = { .name = "Game port (Port 203h only)", .internal_name = "gameport_203", .flags = 0, - .local = 0x010203, + .local = GAMEPORT_1ADDR | 0x0203, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -510,7 +512,7 @@ const device_t gameport_205_device = { .name = "Game port (Port 205h only)", .internal_name = "gameport_205", .flags = 0, - .local = 0x010205, + .local = GAMEPORT_1ADDR | 0x0205, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -524,7 +526,7 @@ const device_t gameport_207_device = { .name = "Game port (Port 207h only)", .internal_name = "gameport_207", .flags = 0, - .local = 0x010207, + .local = GAMEPORT_1ADDR | 0x0207, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -538,7 +540,7 @@ const device_t gameport_208_device = { .name = "Game port (Port 208h-20fh)", .internal_name = "gameport_208", .flags = 0, - .local = 0x080208, + .local = GAMEPORT_8ADDR | 0x0208, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -552,7 +554,7 @@ const device_t gameport_209_device = { .name = "Game port (Port 209h only)", .internal_name = "gameport_209", .flags = 0, - .local = 0x010209, + .local = GAMEPORT_1ADDR | 0x0209, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -566,7 +568,7 @@ const device_t gameport_20b_device = { .name = "Game port (Port 20Bh only)", .internal_name = "gameport_20b", .flags = 0, - .local = 0x01020B, + .local = GAMEPORT_1ADDR | 0x020B, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -580,7 +582,7 @@ const device_t gameport_20d_device = { .name = "Game port (Port 20Dh only)", .internal_name = "gameport_20d", .flags = 0, - .local = 0x01020D, + .local = GAMEPORT_1ADDR | 0x020D, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -594,7 +596,7 @@ const device_t gameport_20f_device = { .name = "Game port (Port 20Fh only)", .internal_name = "gameport_20f", .flags = 0, - .local = 0x01020F, + .local = GAMEPORT_1ADDR | 0x020F, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -662,7 +664,7 @@ const device_t gameport_pnp_device = { .name = "Game port (Plug and Play only)", .internal_name = "gameport_pnp", .flags = 0, - .local = 0x080000, + .local = GAMEPORT_8ADDR, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -676,7 +678,7 @@ const device_t gameport_pnp_6io_device = { .name = "Game port (Plug and Play only, 6 I/O ports)", .internal_name = "gameport_pnp_6io", .flags = 0, - .local = 0x060000, + .local = GAMEPORT_6ADDR, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -690,7 +692,7 @@ const device_t gameport_sio_device = { .name = "Game port (Super I/O)", .internal_name = "gameport_sio", .flags = 0, - .local = 0x1080000, + .local = GAMEPORT_SIO | GAMEPORT_8ADDR, .init = gameport_init, .close = gameport_close, .reset = NULL, @@ -704,7 +706,7 @@ const device_t gameport_sio_1io_device = { .name = "Game port (Super I/O, 1 I/O port)", .internal_name = "gameport_sio", .flags = 0, - .local = 0x1010000, + .local = GAMEPORT_SIO | GAMEPORT_1ADDR, .init = gameport_init, .close = gameport_close, .reset = NULL, diff --git a/src/include/86box/cdrom.h b/src/include/86box/cdrom.h index 636c151ce..0a28e630d 100644 --- a/src/include/86box/cdrom.h +++ b/src/include/86box/cdrom.h @@ -195,10 +195,25 @@ typedef struct track_info_t { uint8_t f; } track_info_t; +typedef struct raw_track_info_t { + uint8_t session; + uint8_t adr_ctl; + uint8_t tno; + uint8_t point; + uint8_t m; + uint8_t s; + uint8_t f; + uint8_t zero; + uint8_t pm; + uint8_t ps; + uint8_t pf; +} raw_track_info_t; + /* Define the various CD-ROM drive operations (ops). */ typedef struct cdrom_ops_t { void (*get_tracks)(struct cdrom *dev, int *first, int *last); void (*get_track_info)(struct cdrom *dev, uint32_t track, int end, track_info_t *ti); + void (*get_raw_track_info)(struct cdrom *dev, int *num, raw_track_info_t *rti); void (*get_subchannel)(struct cdrom *dev, uint32_t lba, subchannel_t *subc); int (*is_track_pre)(struct cdrom *dev, uint32_t lba); int (*sector_size)(struct cdrom *dev, uint32_t lba); @@ -248,7 +263,7 @@ typedef struct cdrom { const cdrom_ops_t *ops; - void *image; + void *local; void (*insert)(void *priv); void (*close)(void *priv); diff --git a/src/include/86box/cdrom_image_backend.h b/src/include/86box/cdrom_image_backend.h index cf4fe95c3..c6b2d9fc7 100644 --- a/src/include/86box/cdrom_image_backend.h +++ b/src/include/86box/cdrom_image_backend.h @@ -57,7 +57,31 @@ typedef struct track_file_t { int motorola; } track_file_t; +#define BLOCK_EMPTY 0 /* Empty block. */ +#define BLOCK_ZERO 1 /* Block not in the file, return all 0x00's. */ +#define BLOCK_NORMAL 2 /* Block in the file. */ + +#define BLOCK_NONE ((uint64_t) -1LL) + +typedef struct track_block_t { + /* Is the current block in the file? If not, return all 0x00's. */ + int type; + /* The amount of bytes to skip at the beginning of each sector. */ + int skip; + /* Starting and ending sector LBA - negative in order to accomodate LBA -150 to -1 + to read the pregap of track 1. */ + int64_t start_sector; + int64_t end_sector; + /* Starting and ending offset in the file. */ + uint64_t start_offs; + uint64_t end_offs; +} track_block_t; + typedef struct track_t { + int pregap_len; /* Pre-gap - not in file. */ + int index0_len; /* Pre-gap - in file. */ + int postgap_len; /* Post-gap - not in file. */ + int blocks_num; /* Number of blocks. */ int number; int track_number; int attr; @@ -65,10 +89,11 @@ typedef struct track_t { int mode2; int form; int pre; - int noskip; /* Do not skip by 8 bytes.*/ + int noskip; /* Do not skip by 8 bytes.*/ uint64_t start; uint64_t length; uint64_t skip; + track_block_t blocks[256]; track_file_t *file; } track_t; diff --git a/src/include/86box/fdc.h b/src/include/86box/fdc.h index ef78239cd..9e8006650 100644 --- a/src/include/86box/fdc.h +++ b/src/include/86box/fdc.h @@ -41,20 +41,23 @@ #define FDC_FLAG_PCJR 0x01 /* PCjr */ #define FDC_FLAG_DISKCHG_ACTLOW 0x02 /* Amstrad, PS/1, PS/2 ISA */ #define FDC_FLAG_AT 0x04 /* AT+, PS/x */ -#define FDC_FLAG_PS1 0x08 /* PS/1, PS/2 ISA */ -#define FDC_FLAG_SUPERIO 0x10 /* Super I/O chips */ -#define FDC_FLAG_START_RWC_1 0x20 /* W83877F, W83977F */ -#define FDC_FLAG_MORE_TRACKS 0x40 /* W83877F, W83977F, PC87306, PC87309 */ -#define FDC_FLAG_NSC 0x80 /* PC87306, PC87309 */ -#define FDC_FLAG_TOSHIBA 0x100 /* T1000, T1200 */ -#define FDC_FLAG_AMSTRAD 0x200 /* Non-AT Amstrad machines */ -#define FDC_FLAG_UMC 0x400 /* UMC UM8398 */ -#define FDC_FLAG_ALI 0x800 /* ALi M512x / M1543C */ -#define FDC_FLAG_NO_DSR_RESET 0x1000 /* Has no DSR reset */ -#define FDC_FLAG_NEC 0x2000 /* Is NEC upd765-compatible */ -#define FDC_FLAG_SEC 0x10000 /* Is Secondary */ -#define FDC_FLAG_TER 0x20000 /* Is Tertiary */ -#define FDC_FLAG_QUA 0x40000 /* Is Quaternary */ +#define FDC_FLAG_PS2 0x08 /* PS/1, PS/2 ISA */ +#define FDC_FLAG_PS2_MCA 0x10 /* PS/2 MCA */ +#define FDC_FLAG_SUPERIO 0x20 /* Super I/O chips */ +#define FDC_FLAG_START_RWC_1 0x40 /* W83877F, W83977F */ +#define FDC_FLAG_MORE_TRACKS 0x80 /* W83877F, W83977F, PC87306, PC87309 */ +#define FDC_FLAG_NSC 0x100 /* PC87306, PC87309 */ +#define FDC_FLAG_TOSHIBA 0x200 /* T1000, T1200 */ +#define FDC_FLAG_AMSTRAD 0x400 /* Non-AT Amstrad machines */ +#define FDC_FLAG_UMC 0x800 /* UMC UM8398 */ +#define FDC_FLAG_ALI 0x1000 /* ALi M512x / M1543C */ +#define FDC_FLAG_NO_DSR_RESET 0x2000 /* Has no DSR reset */ +#define FDC_FLAG_DENSEL_INVERT 0x4000 /* Invert DENSEL polarity */ +#define FDC_FLAG_FINTR 0x8000 /* Raise FINTR on data command finish */ +#define FDC_FLAG_NEC 0x10000 /* Is NEC upd765-compatible */ +#define FDC_FLAG_SEC 0x20000 /* Is Secondary */ +#define FDC_FLAG_TER 0x40000 /* Is Tertiary */ +#define FDC_FLAG_QUA 0x80000 /* Is Quaternary */ typedef struct fdc_t { uint8_t dor; @@ -244,20 +247,20 @@ extern const device_t fdc_xt_qua_device; extern const device_t fdc_xt_t1x00_device; extern const device_t fdc_xt_tandy_device; extern const device_t fdc_xt_amstrad_device; +extern const device_t fdc_xt_umc_um8398_device; extern const device_t fdc_pcjr_device; extern const device_t fdc_at_device; extern const device_t fdc_at_sec_device; extern const device_t fdc_at_ter_device; extern const device_t fdc_at_qua_device; extern const device_t fdc_at_actlow_device; -extern const device_t fdc_at_ps1_device; -extern const device_t fdc_at_ps1_2121_device; extern const device_t fdc_at_smc_device; extern const device_t fdc_at_ali_device; extern const device_t fdc_at_winbond_device; extern const device_t fdc_at_nsc_device; -extern const device_t fdc_dp8473_device; -extern const device_t fdc_um8398_device; +extern const device_t fdc_at_nsc_dp8473_device; +extern const device_t fdc_ps2_device; +extern const device_t fdc_ps2_mca_device; #endif #endif /*EMU_FDC_H*/ diff --git a/src/include/86box/gameport.h b/src/include/86box/gameport.h index d9c702394..576d6b8e7 100644 --- a/src/include/86box/gameport.h +++ b/src/include/86box/gameport.h @@ -13,10 +13,12 @@ * Authors: Miran Grca, * Sarah Walker, * RichardG, + * Jasmine Iwanek, * - * Copyright 2016-2018 Miran Grca. + * Copyright 2016-2022 Miran Grca. * Copyright 2008-2018 Sarah Walker. * Copyright 2021 RichardG. + * Copyright 2021-2024 Jasmine Iwanek. */ #ifndef EMU_GAMEPORT_H #define EMU_GAMEPORT_H @@ -45,6 +47,9 @@ #define JOYSTICK_PRESENT(n) (joystick_state[n].plat_joystick_nr != 0) +#define GAMEPORT_1ADDR 0x010000 +#define GAMEPORT_6ADDR 0x060000 +#define GAMEPORT_8ADDR 0x080000 #define GAMEPORT_SIO 0x1000000 typedef struct plat_joystick_t { diff --git a/src/include/86box/hdd.h b/src/include/86box/hdd.h index 25c32355e..a4bded58f 100644 --- a/src/include/86box/hdd.h +++ b/src/include/86box/hdd.h @@ -90,6 +90,7 @@ enum { typedef struct hdd_preset_t { const char *name; const char *internal_name; + const char *model; uint32_t zones; uint32_t avg_spt; uint32_t heads; @@ -165,6 +166,7 @@ typedef struct hard_disk_t { uint32_t spt; uint32_t hpc; /* Physical geometry parameters */ uint32_t tracks; + const char *model; hdd_zone_t zones[HDD_MAX_ZONES]; uint32_t num_zones; diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index aaf438762..ba4418da8 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -697,7 +697,7 @@ extern int machine_at_8500tuc_init(const machine_t *); extern int machine_at_p55t2s_init(const machine_t *); extern int machine_at_p5vxb_init(const machine_t *); -extern int machine_at_gw2kte_init(const machine_t *); +extern int machine_at_gw2kma_init(const machine_t *); extern int machine_at_ap5s_init(const machine_t *); extern int machine_at_pc140_6260_init(const machine_t *); @@ -732,6 +732,7 @@ extern int machine_at_pb680_init(const machine_t *); extern int machine_at_pb810_init(const machine_t *); extern int machine_at_mb520n_init(const machine_t *); extern int machine_at_i430vx_init(const machine_t *); +extern int machine_at_hitman_init(const machine_t *); extern int machine_at_ma23c_init(const machine_t *); extern int machine_at_nupro592_init(const machine_t *); @@ -858,6 +859,9 @@ extern int machine_at_vpc2007_init(const machine_t *); /* m_at_t3100e.c */ extern int machine_at_t3100e_init(const machine_t *); +/* m_at_grid.c */ +extern int machine_at_grid1520_init(const machine_t *); + /* m_elt.c */ extern int machine_elt_init(const machine_t *); diff --git a/src/include/86box/plat_cdrom.h b/src/include/86box/plat_cdrom.h index 8e81f9459..06439a652 100644 --- a/src/include/86box/plat_cdrom.h +++ b/src/include/86box/plat_cdrom.h @@ -49,19 +49,23 @@ typedef struct SMSF { uint8_t fr; } TMSF; -extern int plat_cdrom_is_track_audio(uint32_t sector); -extern int plat_cdrom_is_track_pre(uint32_t sector); -extern uint32_t plat_cdrom_get_last_block(void); -extern void plat_cdrom_get_audio_tracks(int *st_track, int *end, TMSF *lead_out); -extern int plat_cdrom_get_audio_track_info(int end, int track, int *track_num, TMSF *start, uint8_t *attr); -extern int plat_cdrom_get_audio_sub(uint32_t sector, uint8_t *attr, uint8_t *track, uint8_t *index, TMSF *rel_pos, TMSF *abs_pos); -extern int plat_cdrom_get_sector_size(uint32_t sector); -extern int plat_cdrom_read_sector(uint8_t *buffer, int raw, uint32_t sector); -extern void plat_cdrom_eject(void); -extern void plat_cdrom_close(void); -extern int plat_cdrom_set_drive(const char *drv); -extern int plat_cdrom_ext_medium_changed(void); -extern uint32_t plat_cdrom_get_track_start(uint32_t sector, uint8_t *attr, uint8_t *track); +extern void plat_cdrom_get_raw_track_info(void *local, int *num, raw_track_info_t *rti); +extern int plat_cdrom_is_track_audio(void *local, uint32_t sector); +extern int plat_cdrom_is_track_pre(void *local, uint32_t sector); +extern uint32_t plat_cdrom_get_last_block(void *local); +extern void plat_cdrom_get_audio_tracks(void *local, int *st_track, int *end, TMSF *lead_out); +extern int plat_cdrom_get_audio_track_info(void *local, int end, int track, int *track_num, TMSF *start, + uint8_t *attr); +extern int plat_cdrom_get_audio_sub(void *local, uint32_t sector, uint8_t *attr, uint8_t *track, + uint8_t *index, TMSF *rel_pos, TMSF *abs_pos); +extern int plat_cdrom_get_sector_size(void *local, uint32_t sector); +extern int plat_cdrom_read_sector(void *local, uint8_t *buffer, int raw, uint32_t sector); +extern void plat_cdrom_eject(void *local); +extern void plat_cdrom_close(void *local); +extern int plat_cdrom_set_drive(void *local, const char *drv); +extern int plat_cdrom_ext_medium_changed(void *local); +extern uint32_t plat_cdrom_get_track_start(void *local, uint32_t sector, uint8_t *attr, uint8_t *track); +extern int plat_cdrom_get_local_size(void); #ifdef __cplusplus } diff --git a/src/include/86box/snd_opl.h b/src/include/86box/snd_opl.h index 441e2a119..fe0112b47 100644 --- a/src/include/86box/snd_opl.h +++ b/src/include/86box/snd_opl.h @@ -18,12 +18,13 @@ #define SOUND_OPL_H enum fm_type { - FM_YM3812 = 0, /* OPL2 */ - FM_YMF262 = 1, /* OPL3 */ - FM_YMF289B = 2, /* OPL3-L */ - FM_YMF278B = 3, /* OPL 4 */ - FM_ESFM = 4, /* ESFM */ - FM_MAX = 5 + FM_YM3812 = 0, /* OPL2 */ + FM_YMF262 = 1, /* OPL3 */ + FM_YMF289B = 2, /* OPL3-L */ + FM_YMF278B = 3, /* OPL 4 */ + FM_ESFM = 4, /* ESFM */ + FM_OPL2BOARD = 5, /* OPL2BOARD (External Device)*/ + FM_MAX = 6 }; enum fm_driver { @@ -47,6 +48,7 @@ extern uint8_t fm_driver_get(int chip_id, fm_drv_t *drv); extern const fm_drv_t nuked_opl_drv; extern const fm_drv_t ymfm_drv; extern const fm_drv_t esfmu_opl_drv; +extern const fm_drv_t ymfm_opl2board_drv; #ifdef EMU_DEVICE_H extern const device_t ym3812_nuked_device; @@ -58,6 +60,10 @@ extern const device_t ymf289b_ymfm_device; extern const device_t ymf278b_ymfm_device; extern const device_t esfm_esfmu_device; +#ifdef USE_LIBSERIALPORT +extern const device_t ym_opl2board_device; +#endif + #endif #endif /*SOUND_OPL_H*/ diff --git a/src/include/86box/snd_resid.h b/src/include/86box/snd_resid.h index 4ddaf9b91..c7e97ac0f 100644 --- a/src/include/86box/snd_resid.h +++ b/src/include/86box/snd_resid.h @@ -4,7 +4,7 @@ #ifdef __cplusplus extern "C" { #endif -void *sid_init(void); +void *sid_init(uint8_t type); void sid_close(void *priv); void sid_reset(void *priv); uint8_t sid_read(uint16_t addr, void *priv); diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h index 9895e73d7..2a41b98f8 100644 --- a/src/include/86box/sound.h +++ b/src/include/86box/sound.h @@ -192,6 +192,7 @@ extern const device_t ps1snd_device; /* Innovation SSI-2001 */ extern const device_t ssi2001_device; +extern const device_t entertainer_device; /* Pro Audio Spectrum Plus, 16, and 16D */ extern const device_t pasplus_device; @@ -209,6 +210,11 @@ extern const device_t tndy_device; extern const device_t wss_device; extern const device_t ncr_business_audio_device; +#ifdef USE_LIBSERIALPORT +/* External Audio device OPL2Board (Host Connected hardware)*/ +extern const device_t opl2board_device; +#endif + #endif #endif /*EMU_SOUND_H*/ diff --git a/src/machine/CMakeLists.txt b/src/machine/CMakeLists.txt index 55e0e0197..ff6a66801 100644 --- a/src/machine/CMakeLists.txt +++ b/src/machine/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(mch OBJECT m_v86p.c m_at.c m_at_commodore.c + m_at_grid.c m_at_t3100e.c m_at_t3100e_vid.c m_ps1.c diff --git a/src/machine/m_at_286_386sx.c b/src/machine/m_at_286_386sx.c index 995301e4f..36629658f 100644 --- a/src/machine/m_at_286_386sx.c +++ b/src/machine/m_at_286_386sx.c @@ -770,7 +770,8 @@ machine_at_sbc350a_init(const machine_t *model) machine_at_common_init(model); device_add(&ali1217_device); - device_add(&fdc37c665_ide_device); + device_add(&ide_isa_device); + device_add(&fdc37c665_ide_pri_device); device_add(&keyboard_ps2_ami_device); return ret; diff --git a/src/machine/m_at_grid.c b/src/machine/m_at_grid.c new file mode 100644 index 000000000..2fc757129 --- /dev/null +++ b/src/machine/m_at_grid.c @@ -0,0 +1,354 @@ +/* + * 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. + * + * Implementation of the GRiD GRiDcase 1520 + * + * The GRiDcase 1520 is a 286-based portable. + * These are HDDs supported by GRiD1520 (and probably other 15XX) BIOS + * "CP3022",5 + * "CP3024",5, 615,4,17 BIOS table type 2 + * "CP344",6, + * "CP3044",9, 980,5,17 BIOS table type 17 + * "CP3042",9 + * "CP3104",7, 776,8,33 extended type 224 (separate entry outside BIOS table) + * The only way to run unpatched BIOS is to run exactly that (or larger) + * geometry and report model name correctly in response to IDENTYIFY command. + * Alternatively you can use RomBuster to patch the BIOS. + * https://classicbits.net/coding-and-software/my-software/rombuster/ + */ +#include +#include +#include +#include +#include <86box/86box.h> +#include "cpu.h" +#include <86box/timer.h> +#include <86box/device.h> +#include <86box/fdd.h> +#include <86box/fdc.h> +#include <86box/fdc_ext.h> +#include <86box/io.h> +#include <86box/keyboard.h> +#include <86box/machine.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/vid_cga.h> + +#define GRID_APPROM_SELECT 0x440 +#define GRID_APPROM_ENABLE 0x405 + +/* +approm mapping regs? +XXX_7FA equ 7FAh +XXX_7F8 equ 7F8h +XXX_7F9 equ 7F9h +XXX_BD0 equ 0BD0h +XXX_BD1 equ 0BD1h +*/ + +#define GRID_EMS_PAGE_0 0x0258 +#define GRID_EMS_PAGE_1 0x4258 +#define GRID_EMS_PAGE_2 0x8258 +#define GRID_EMS_PAGE_3 0xC258 +#define GRID_TURBO 0x416 +#define GRID_UNUSED_424 0x424 +#define GRID_426 0x426 +#define GRID_HIGH_ENABLE 0xFFF +#define GRID_ROM_SUBSYSTEM 0x6F8 + +// EMS window +#define GRID_EMS_BASE 0xE0000 +#define GRID_EMS_PAGE_SIZE 0x4000 +#define GRID_EMS_PAGE_MASK 0x3FFF +#define GRID_EMS_PAGE_SHIFT 14 +// physical base of extended memory +#define GRID_EXTENDED_BASE 0xA0000 +#define GRID_1M 0x100000 + +typedef struct { + uint8_t grid_unknown; + uint8_t grid_unused_424; + uint8_t grid_426; + uint8_t grid_high_enable; + uint8_t grid_ems_page[4]; + mem_mapping_t grid_ems_mapping[4]; + uint8_t grid_turbo; + uint8_t grid_rom_enable; + uint8_t grid_rom_select; +} grid_t; + + +static uint32_t get_grid_ems_paddr(grid_t *dev, uint32_t addr) { + uint32_t slot = (addr >> GRID_EMS_PAGE_SHIFT) & 0x3; + uint32_t paddr = addr; + + if (dev->grid_ems_page[slot] & 0x80) + paddr = GRID_EXTENDED_BASE + ((uint32_t)(dev->grid_ems_page[slot] & 0x7F) << GRID_EMS_PAGE_SHIFT) + (addr & GRID_EMS_PAGE_MASK); + + return paddr; +} + +static void grid_ems_mem_write8(uint32_t addr, uint8_t val, void *priv) { + grid_t *dev = (grid_t *) priv; + + addr = get_grid_ems_paddr(dev, addr); + + if (addr < (mem_size << 10)) + ram[addr] = val; +} + +static uint8_t grid_ems_mem_read8(uint32_t addr, void *priv) { + grid_t *dev = (grid_t *) priv; + uint8_t val = 0xFF; + + addr = get_grid_ems_paddr(dev, addr); + + if (addr < (mem_size << 10)) + val = ram[addr]; + + return val; +} + +static void grid_ems_mem_write16(uint32_t addr, uint16_t val, void *priv) { + grid_t *dev = (grid_t *) priv; + + addr = get_grid_ems_paddr(dev, addr); + + if (addr < (mem_size << 10)) + *(uint16_t *)&(ram[addr]) = val; +} + +static uint16_t grid_ems_mem_read16(uint32_t addr, void *priv) { + grid_t *dev = (grid_t *) priv; + uint16_t val = 0xFFFF; + + addr = get_grid_ems_paddr(dev, addr); + + if (addr < (mem_size << 10)) + val = *(uint16_t *)&(ram[addr]); + + return val; +} + +static void grid_ems_update_mapping(grid_t *dev, uint32_t slot) { + uint32_t vaddr = GRID_EMS_BASE + (slot << GRID_EMS_PAGE_SHIFT); + if (dev->grid_ems_page[slot] & 0x80) { + uint32_t paddr; + mem_mapping_enable(&dev->grid_ems_mapping[slot]); + paddr = get_grid_ems_paddr(dev, vaddr); + mem_mapping_set_exec(&dev->grid_ems_mapping[slot], ram + paddr); + } else { + mem_mapping_disable(&dev->grid_ems_mapping[slot]); + } +} + +static void grid_io_write(uint16_t port, uint8_t val, void *priv) { + grid_t *dev = (grid_t *) priv; + + switch (port) { + case GRID_426: + dev->grid_426 = val; + break; + case GRID_UNUSED_424: + dev->grid_unused_424 = val; + break; + case GRID_ROM_SUBSYSTEM: + case GRID_ROM_SUBSYSTEM+1: + case GRID_ROM_SUBSYSTEM+2: + case GRID_ROM_SUBSYSTEM+3: + case GRID_ROM_SUBSYSTEM+4: + case GRID_ROM_SUBSYSTEM+5: + case GRID_ROM_SUBSYSTEM+6: + case GRID_ROM_SUBSYSTEM+7: + break; + case GRID_APPROM_SELECT: + dev->grid_rom_select = val; + break; + case GRID_APPROM_ENABLE: + dev->grid_rom_enable = val; + break; + case GRID_TURBO: + if ((dev->grid_turbo ^ val) & 1) { + dev->grid_turbo = val; + if (dev->grid_turbo) + cpu_dynamic_switch(cpu); + else + cpu_dynamic_switch(0); /* 286/6 */ + } + break; + case GRID_EMS_PAGE_0: + case GRID_EMS_PAGE_1: + case GRID_EMS_PAGE_2: + case GRID_EMS_PAGE_3: { + uint32_t slot = (port >> 14) & 0x3; + if (dev->grid_ems_page[slot] == val) + break; // no change + + dev->grid_ems_page[slot] = val; + if (dev->grid_high_enable & 0x1) + break; // XMS is enabled + grid_ems_update_mapping(dev, slot); + + flushmmucache(); + break; + } + case GRID_HIGH_ENABLE: { + if (((val ^ dev->grid_high_enable) & 0x1) == 0) + break; // no change + dev->grid_high_enable = val; + if (dev->grid_high_enable & 0x1) { + for (uint8_t i = 0; i < 4; i++) + mem_mapping_disable(&dev->grid_ems_mapping[i]); + mem_mapping_enable(&ram_high_mapping); + } else { + mem_mapping_disable(&ram_high_mapping); + for (uint8_t i = 0; i < 4; i++) + grid_ems_update_mapping(dev, i); + } + flushmmucache(); + break; + } + default: + break; + } +} + +static uint8_t grid_io_read(uint16_t port, void *priv) { + grid_t *dev = (grid_t *) priv; + + switch (port) { + case GRID_426: + return dev->grid_426; + break; + case GRID_UNUSED_424: + return dev->grid_unused_424; + break; + case GRID_ROM_SUBSYSTEM: + return 0x99; + break; + case GRID_ROM_SUBSYSTEM+1: + case GRID_ROM_SUBSYSTEM+2: + case GRID_ROM_SUBSYSTEM+3: + case GRID_ROM_SUBSYSTEM+4: + case GRID_ROM_SUBSYSTEM+5: + case GRID_ROM_SUBSYSTEM+6: + case GRID_ROM_SUBSYSTEM+7: + break; + case GRID_APPROM_SELECT: + return dev->grid_rom_select; + case GRID_APPROM_ENABLE: + return dev->grid_rom_enable; + case GRID_TURBO: + return dev->grid_turbo; + case GRID_HIGH_ENABLE: + return dev->grid_high_enable; + case GRID_EMS_PAGE_0: + case GRID_EMS_PAGE_1: + case GRID_EMS_PAGE_2: + case GRID_EMS_PAGE_3: { + uint32_t slot = (port >> 14) & 0x3; + + return dev->grid_ems_page[slot]; + } + default: + break; + } + + return 0xff; +} + +static void * +grid_init(const device_t *info) +{ + grid_t *dev = calloc(1, sizeof(grid_t)); + + io_sethandler(GRID_ROM_SUBSYSTEM, 0x0008, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_UNUSED_424, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_426, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_APPROM_SELECT, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_APPROM_ENABLE, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_TURBO, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + dev->grid_turbo = 0x1; + + io_sethandler(GRID_HIGH_ENABLE, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_EMS_PAGE_0, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_EMS_PAGE_1, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_EMS_PAGE_2, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + io_sethandler(GRID_EMS_PAGE_3, 0x0001, grid_io_read, NULL, NULL, grid_io_write, NULL, NULL, dev); + + dev->grid_high_enable = 1; + for (uint8_t slot = 0; slot < 4; slot++) { + dev->grid_ems_page[slot] = 0; + mem_mapping_add(&dev->grid_ems_mapping[slot], GRID_EMS_BASE + (slot << GRID_EMS_PAGE_SHIFT), GRID_EMS_PAGE_SIZE, grid_ems_mem_read8, grid_ems_mem_read16, NULL, + grid_ems_mem_write8, grid_ems_mem_write16, NULL, ram + GRID_EXTENDED_BASE + (slot << GRID_EMS_PAGE_SHIFT), MEM_MAPPING_EXTERNAL, dev); + mem_mapping_disable(&dev->grid_ems_mapping[slot]); + } + flushmmucache(); + return dev; +} + +static void grid_close(void *priv) { + grid_t *dev = (grid_t *) priv; + + free(dev); +} + +static void grid_reset(void *priv) { + grid_t *dev = (grid_t *) priv; + + dev->grid_high_enable = 1; + mem_mapping_enable(&ram_high_mapping); + dev->grid_turbo = 0x1; + for (uint8_t slot = 0; slot < 4; slot++) { + dev->grid_ems_page[slot] = 0; + mem_mapping_disable(&dev->grid_ems_mapping[slot]); + } + flushmmucache(); + dev->grid_unknown = 0; + dev->grid_unused_424 = 0; + dev->grid_426 = 0; + dev->grid_rom_enable = 0; + dev->grid_rom_select = 0; +} + +const device_t grid_device = { + .name = "GRiDcase 1520 chipset", + .internal_name = "grid1520", + .flags = 0, + .local = 0, + .init = grid_init, + .close = grid_close, + .reset = grid_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +int machine_at_grid1520_init(const machine_t *model) { + int ret = 0; + + ret = bios_load_linear("roms/machines/grid1520/grid1520_891025.rom", + 0x000f8000, 0x8000, 0); + if (bios_only || !ret) + return ret; + + machine_at_common_ide_init(model); + mem_remap_top(384); + + device_add(&keyboard_at_device); + // for now just select CGA with amber monitor + //device_add(&cga_device); + + if (fdc_current[0] == FDC_INTERNAL) + device_add(&fdc_at_device); + + device_add(&grid_device); + + return ret; +} diff --git a/src/machine/m_at_socket7.c b/src/machine/m_at_socket7.c index d7e2840a9..31afb0f01 100644 --- a/src/machine/m_at_socket7.c +++ b/src/machine/m_at_socket7.c @@ -824,6 +824,39 @@ machine_at_i430vx_init(const machine_t *model) return ret; } +int +machine_at_hitman_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear_combined2("roms/machines/hitman/1008CY1T.BIO", + "roms/machines/hitman/1008CY1T.BI1", + "roms/machines/hitman/1008CY1T.BI2", + "roms/machines/hitman/1008CY1T.BI3", + "roms/machines/hitman/1008CY1T.RCV", + 0x3a000, 128); + + if (bios_only || !ret) + return ret; + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x08, PCI_CARD_VIDEO, 4, 0, 0, 0); + pci_register_slot(0x0D, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x0E, PCI_CARD_NORMAL, 2, 3, 4, 1); + pci_register_slot(0x0F, PCI_CARD_NORMAL, 3, 4, 1, 2); + pci_register_slot(0x10, PCI_CARD_NORMAL, 4, 1, 2, 3); + pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 4); + device_add(&i430vx_device); + device_add(&piix3_device); + device_add(&fdc37c932fr_device); + device_add(&intel_flash_bxt_ami_device); + + return ret; +} + int machine_at_ma23c_init(const machine_t *model) { diff --git a/src/machine/m_at_socket7_3v.c b/src/machine/m_at_socket7_3v.c index c36fade86..40ae221a0 100644 --- a/src/machine/m_at_socket7_3v.c +++ b/src/machine/m_at_socket7_3v.c @@ -671,15 +671,15 @@ machine_at_p5vxb_init(const machine_t *model) } int -machine_at_gw2kte_init(const machine_t *model) +machine_at_gw2kma_init(const machine_t *model) { int ret; - ret = bios_load_linear_combined2("roms/machines/gw2kte/1008CY1T.BIO", - "roms/machines/gw2kte/1008CY1T.BI1", - "roms/machines/gw2kte/1008CY1T.BI2", - "roms/machines/gw2kte/1008CY1T.BI3", - "roms/machines/gw2kte/1008CY1T.RCV", + ret = bios_load_linear_combined2("roms/machines/gw2kma/1007DQ0T.BIO", + "roms/machines/gw2kma/1007DQ0T.BI1", + "roms/machines/gw2kma/1007DQ0T.BI2", + "roms/machines/gw2kma/1007DQ0T.BI3", + "roms/machines/gw2kma/1007DQ0T.RCV", 0x3a000, 128); if (bios_only || !ret) @@ -689,7 +689,6 @@ machine_at_gw2kte_init(const machine_t *model) pci_init(PCI_CONFIG_TYPE_1); pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); - pci_register_slot(0x08, PCI_CARD_VIDEO, 4, 0, 0, 0); pci_register_slot(0x0D, PCI_CARD_NORMAL, 1, 2, 3, 4); pci_register_slot(0x0E, PCI_CARD_NORMAL, 2, 3, 4, 1); pci_register_slot(0x0F, PCI_CARD_NORMAL, 3, 4, 1, 2); diff --git a/src/machine/m_ps1.c b/src/machine/m_ps1.c index a6a16d65c..42bc49de2 100644 --- a/src/machine/m_ps1.c +++ b/src/machine/m_ps1.c @@ -323,6 +323,8 @@ ps1_setup(int model) device_add(&ps_nvr_device); + device_add(&fdc_ps2_device); + if (model == 2011) { if (!strcmp("english_us", device_get_config_bios("bios_language"))) { /* US English */ @@ -350,8 +352,6 @@ ps1_setup(int model) device_add(&ps1snd_device); - device_add(&fdc_at_ps1_device); - /* Enable the builtin HDC. */ if (hdc_current[0] == HDC_INTERNAL) { priv = device_add(&ps1_hdc_device); @@ -378,8 +378,6 @@ ps1_setup(int model) if (gfxcard[0] == VID_INTERNAL) device_add(&ibm_ps1_2121_device); - device_add(&fdc_at_ps1_2121_device); - device_add(&ide_isa_device); device_add(&ps1snd_device); diff --git a/src/machine/m_ps1_hdc.c b/src/machine/m_ps1_hdc.c index f35879458..e1bacad6c 100644 --- a/src/machine/m_ps1_hdc.c +++ b/src/machine/m_ps1_hdc.c @@ -462,7 +462,6 @@ static const geom_t ibm_type_table[] = { // clang-format on }; -#define ENABLE_PS1_HDC_LOG 1 #ifdef ENABLE_PS1_HDC_LOG int ps1_hdc_do_log = ENABLE_PS1_HDC_LOG; @@ -721,7 +720,9 @@ hdc_callback(void *priv) off64_t addr; int no_data = 0; int val; +#ifdef ENABLE_PS1_HDC_LOG uint8_t cmd = ccb->cmd & 0x0f; +#endif /* Clear the SSB error bits. */ dev->ssb.track_0 = 0; diff --git a/src/machine/m_ps2_isa.c b/src/machine/m_ps2_isa.c index 2887cca20..c0c4f7c79 100644 --- a/src/machine/m_ps2_isa.c +++ b/src/machine/m_ps2_isa.c @@ -176,7 +176,7 @@ ps2_isa_setup(int model, int cpu_type) device_add(&ps_nvr_device); - device_add(&fdc_at_ps1_device); + device_add(&fdc_ps2_device); /* Enable the builtin HDC. */ if (hdc_current[0] == HDC_INTERNAL) { diff --git a/src/machine/m_ps2_mca.c b/src/machine/m_ps2_mca.c index cb83d9be7..632381ca1 100644 --- a/src/machine/m_ps2_mca.c +++ b/src/machine/m_ps2_mca.c @@ -1396,7 +1396,7 @@ machine_ps2_common_init(const machine_t *model) machine_common_init(model); if (fdc_current[0] == FDC_INTERNAL) - device_add(&fdc_at_device); + device_add(&fdc_ps2_mca_device); dma16_init(); ps2_dma_init(); diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 580ca541d..59d1075bb 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -3247,6 +3247,45 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + { + .name = "[ISA] GRiD GRiDcase 1520", + .internal_name = "grid1520", + .type = MACHINE_TYPE_286, + .chipset = MACHINE_CHIPSET_PROPRIETARY, + .init = machine_at_grid1520_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_286, + .block = CPU_BLOCK_NONE, + .min_bus = 6000000, + .max_bus = 10000000, + .min_voltage = 0, + .max_voltage = 0, + .min_multi = 0, + .max_multi = 0 + }, + .bus_flags = MACHINE_AT, + .flags = MACHINE_IDE /*| MACHINE_VIDEO_FIXED*/, + .ram = { + .min = 1024, + .max = 8192, + .step = 1024 + }, + .nvrmask = 127, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, /* Has Quadtel KBC firmware. */ { .name = "[GC103] Quadtel 286 clone", @@ -10894,11 +10933,11 @@ const machine_t machines[] = { /* Has a SM(S)C FDC37C932FR Super I/O chip with on-chip KBC with AMI MegaKey (revision '5') KBC firmware. */ { - .name = "[i430VX] Gateway 2000 Tigereye", - .internal_name = "gw2kte", + .name = "[i430VX] Gateway 2000 Mailman", + .internal_name = "gw2kma", .type = MACHINE_TYPE_SOCKET7_3V, .chipset = MACHINE_CHIPSET_INTEL_430VX, - .init = machine_at_gw2kte_init, + .init = machine_at_gw2kma_init, .p1_handler = NULL, .gpio_handler = NULL, .available_flag = MACHINE_AVAILABLE, @@ -11914,6 +11953,51 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + +/* 430VX */ + /* Has a SM(S)C FDC37C932FR Super I/O chip with on-chip KBC with AMI + MegaKey (revision '5') KBC firmware. */ + { + .name = "[i430VX] Gateway 2000 Hitman", + .internal_name = "hitman", + .type = MACHINE_TYPE_SOCKET7, + .chipset = MACHINE_CHIPSET_INTEL_430VX, + .init = machine_at_hitman_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK_NONE, + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 2200, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 3.0 + }, + .bus_flags = MACHINE_PS2_PCI | MACHINE_BUS_USB, + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_GAMEPORT | MACHINE_USB, + .ram = { + .min = 8192, + .max = 131072, + .step = 8192 + }, + .nvrmask = 511, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, + + /* Has a SM(S)C FDC37C935 Super I/O chip with on-chip KBC with Phoenix MultiKey/42 (version 1.38) KBC firmware. */ { diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index a99209273..d7ea91f70 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -455,46 +455,52 @@ if (UNIX AND NOT APPLE AND NOT HAIKU) endif() endif() -# Get the Qt translations directory -get_target_property(QT_QMAKE_EXECUTABLE Qt${QT_MAJOR}::qmake IMPORTED_LOCATION) -execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS OUTPUT_VARIABLE QT_TRANSLATIONS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) +option(EMBED_QTBASE_TRANSLATIONS "Embed the base Qt translations into the executable" ON) + +if (EMBED_QTBASE_TRANSLATIONS) + # Get the Qt translations directory + get_target_property(QT_QMAKE_EXECUTABLE Qt${QT_MAJOR}::qmake IMPORTED_LOCATION) + execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS OUTPUT_VARIABLE QT_TRANSLATIONS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() set(QM_FILES) file(GLOB po_files "${CMAKE_CURRENT_SOURCE_DIR}/languages/*.po") foreach(po_file ${po_files}) get_filename_component(PO_FILE_NAME ${po_file} NAME_WE) - # Get the language and country - string(REGEX MATCH "^[a-z]+" PO_LANGUAGE ${PO_FILE_NAME}) - string(REGEX MATCH "[A-Z]+$" PO_COUNTRY ${PO_FILE_NAME}) + if (EMBED_QTBASE_TRANSLATIONS) + # Get the language and country + string(REGEX MATCH "^[a-z]+" PO_LANGUAGE ${PO_FILE_NAME}) + string(REGEX MATCH "[A-Z]+$" PO_COUNTRY ${PO_FILE_NAME}) - # Find the base Qt translation for the language and country - set(qt_translation_file_dest "qt_${PO_LANGUAGE}_${PO_COUNTRY}.qm") - if (EXISTS "${QT_TRANSLATIONS_DIR}/qtbase_${PO_LANGUAGE}_${PO_COUNTRY}.qm") - set(qt_translation_file "qtbase_${PO_LANGUAGE}_${PO_COUNTRY}.qm") - # Fall back to just the language if country isn't found - elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qtbase_${PO_LANGUAGE}.qm") - set(qt_translation_file "qtbase_${PO_LANGUAGE}.qm") - # If the translation is still not found, try the legacy Qt one - elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qt_${PO_LANGUAGE}_${PO_COUNTRY}.qm") - set(qt_translation_file "qt_${PO_LANGUAGE}_${PO_COUNTRY}.qm") - # Fall back to just the language again - elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qt_${PO_LANGUAGE}.qm") - set(qt_translation_file "qt_${PO_LANGUAGE}.qm") - else() - unset(qt_translation_file) - endif() - - # Copy the translation file to the build directory - if (qt_translation_file) - file(COPY "${QT_TRANSLATIONS_DIR}/${qt_translation_file}" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) - if (NOT (qt_translation_file STREQUAL qt_translation_file_dest)) - # Rename the file for consistency - file(RENAME "${CMAKE_CURRENT_BINARY_DIR}/${qt_translation_file}" "${CMAKE_CURRENT_BINARY_DIR}/${qt_translation_file_dest}") + # Find the base Qt translation for the language and country + set(qt_translation_file_dest "qt_${PO_LANGUAGE}_${PO_COUNTRY}.qm") + if (EXISTS "${QT_TRANSLATIONS_DIR}/qtbase_${PO_LANGUAGE}_${PO_COUNTRY}.qm") + set(qt_translation_file "qtbase_${PO_LANGUAGE}_${PO_COUNTRY}.qm") + # Fall back to just the language if country isn't found + elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qtbase_${PO_LANGUAGE}.qm") + set(qt_translation_file "qtbase_${PO_LANGUAGE}.qm") + # If the translation is still not found, try the legacy Qt one + elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qt_${PO_LANGUAGE}_${PO_COUNTRY}.qm") + set(qt_translation_file "qt_${PO_LANGUAGE}_${PO_COUNTRY}.qm") + # Fall back to just the language again + elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qt_${PO_LANGUAGE}.qm") + set(qt_translation_file "qt_${PO_LANGUAGE}.qm") + else() + unset(qt_translation_file) + endif() + + # Copy the translation file to the build directory + if (qt_translation_file) + file(COPY "${QT_TRANSLATIONS_DIR}/${qt_translation_file}" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + if (NOT (qt_translation_file STREQUAL qt_translation_file_dest)) + # Rename the file for consistency + file(RENAME "${CMAKE_CURRENT_BINARY_DIR}/${qt_translation_file}" "${CMAKE_CURRENT_BINARY_DIR}/${qt_translation_file_dest}") + endif() + # Add the file to the translations list + string(APPEND QT_TRANSLATIONS_LIST " ${qt_translation_file_dest}\n") + list(APPEND QM_FILES "${CMAKE_CURRENT_BINARY_DIR}/${qt_translation_file_dest}") endif() - # Add the file to the translations list - string(APPEND QT_TRANSLATIONS_LIST " ${qt_translation_file_dest}\n") - list(APPEND QM_FILES "${CMAKE_CURRENT_BINARY_DIR}/${qt_translation_file_dest}") endif() add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/86box_${PO_FILE_NAME}.qm" diff --git a/src/qt/dummy_cdrom_ioctl.c b/src/qt/dummy_cdrom_ioctl.c index fed1444d1..bf656177b 100644 --- a/src/qt/dummy_cdrom_ioctl.c +++ b/src/qt/dummy_cdrom_ioctl.c @@ -35,7 +35,9 @@ of the audio while audio still plays. With an absolute conversion, the counter is fine. */ #define MSFtoLBA(m, s, f) ((((m * 60) + s) * 75) + f) -static int toc_valid = 0; +typedef struct dummy_cdrom_ioctl_t { + int toc_valid; +} dummy_cdrom_ioctl_t; #ifdef ENABLE_DUMMY_CDROM_IOCTL_LOG int dummy_cdrom_ioctl_do_log = ENABLE_DUMMY_CDROM_IOCTL_LOG; @@ -56,30 +58,41 @@ dummy_cdrom_ioctl_log(const char *fmt, ...) #endif static int -plat_cdrom_open(void) +plat_cdrom_open(void *local) { return 0; } static int -plat_cdrom_load(void) +plat_cdrom_load(void *local) { return 0; } static void -plat_cdrom_read_toc(void) +plat_cdrom_read_toc(void *local) { - if (!toc_valid) - toc_valid = 1; + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + if (!ioctl->toc_valid) + ioctl->toc_valid = 1; +} + +void +plat_cdrom_get_raw_track_info(UNUSED(void *local), int *num, raw_track_info_t *rti) +{ + *num = 1; + memset(rti, 0x00, 11); } int -plat_cdrom_is_track_audio(uint32_t sector) +plat_cdrom_is_track_audio(void *local, uint32_t sector) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; - const int ret = 0; + plat_cdrom_read_toc(ioctl); + + const int ret = 0; dummy_cdrom_ioctl_log("plat_cdrom_is_track_audio(%08X): %i\n", sector, ret); @@ -87,9 +100,11 @@ plat_cdrom_is_track_audio(uint32_t sector) } int -plat_cdrom_is_track_pre(uint32_t sector) +plat_cdrom_is_track_pre(void *local, uint32_t sector) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); const int ret = 0; @@ -99,25 +114,30 @@ plat_cdrom_is_track_pre(uint32_t sector) } uint32_t -plat_cdrom_get_track_start(uint32_t sector, uint8_t *attr, uint8_t *track) +plat_cdrom_get_track_start(void *local, uint32_t sector, uint8_t *attr, uint8_t *track) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); return 0x00000000; } uint32_t -plat_cdrom_get_last_block(void) +plat_cdrom_get_last_block(void *local) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); return 0x00000000; } int -plat_cdrom_ext_medium_changed(void) +plat_cdrom_ext_medium_changed(void *local) { - int ret = 0; + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + int ret = 0; dummy_cdrom_ioctl_log("plat_cdrom_ext_medium_changed(): %i\n", ret); @@ -125,9 +145,11 @@ plat_cdrom_ext_medium_changed(void) } void -plat_cdrom_get_audio_tracks(int *st_track, int *end, TMSF *lead_out) +plat_cdrom_get_audio_tracks(void *local, int *st_track, int *end, TMSF *lead_out) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); *st_track = 1; *end = 1; @@ -141,9 +163,11 @@ plat_cdrom_get_audio_tracks(int *st_track, int *end, TMSF *lead_out) /* This replaces both Info and EndInfo, they are specified by a variable. */ int -plat_cdrom_get_audio_track_info(UNUSED(int end), int track, int *track_num, TMSF *start, uint8_t *attr) +plat_cdrom_get_audio_track_info(void *local, UNUSED(int end), int track, int *track_num, TMSF *start, uint8_t *attr) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); if ((track < 1) || (track == 0xaa)) { dummy_cdrom_ioctl_log("plat_cdrom_get_audio_track_info(%02i)\n", track); @@ -165,7 +189,8 @@ plat_cdrom_get_audio_track_info(UNUSED(int end), int track, int *track_num, TMSF /* TODO: See if track start is adjusted by 150 or not. */ int -plat_cdrom_get_audio_sub(UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, uint8_t *index, TMSF *rel_pos, TMSF *abs_pos) +plat_cdrom_get_audio_sub(UNUSED(void *local), UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, uint8_t *index, + TMSF *rel_pos, TMSF *abs_pos) { *track = 1; *attr = 0x14; @@ -185,7 +210,7 @@ plat_cdrom_get_audio_sub(UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, } int -plat_cdrom_get_sector_size(UNUSED(uint32_t sector)) +plat_cdrom_get_sector_size(UNUSED(void *local), UNUSED(uint32_t sector)) { dummy_cdrom_ioctl_log("BytesPerSector=2048\n"); @@ -193,42 +218,56 @@ plat_cdrom_get_sector_size(UNUSED(uint32_t sector)) } int -plat_cdrom_read_sector(uint8_t *buffer, int raw, uint32_t sector) +plat_cdrom_read_sector(void *local, uint8_t *buffer, int raw, uint32_t sector) { - plat_cdrom_open(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; - if (raw) { - dummy_cdrom_ioctl_log("Raw\n"); + plat_cdrom_open(ioctl); + + if (raw) /* Raw */ - } else { - dummy_cdrom_ioctl_log("Cooked\n"); + dummy_cdrom_ioctl_log("Raw\n"); + else /* Cooked */ - } - plat_cdrom_close(); - dummy_cdrom_ioctl_log("ReadSector status=%d, sector=%d, size=%" PRId64 ".\n", status, sector, (long long) size); + dummy_cdrom_ioctl_log("Cooked\n"); - return -1; + plat_cdrom_close(ioctl); + + dummy_cdrom_ioctl_log("ReadSector sector=%d.\n", sector); + + return 0; } void -plat_cdrom_eject(void) +plat_cdrom_eject(void *local) { - plat_cdrom_open(); - plat_cdrom_close(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_open(ioctl); + plat_cdrom_close(ioctl); } void -plat_cdrom_close(void) +plat_cdrom_close(UNUSED(void *local)) { } int -plat_cdrom_set_drive(const char *drv) +plat_cdrom_set_drive(void *local, const char *drv) { - plat_cdrom_close(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; - toc_valid = 0; + plat_cdrom_close(ioctl); + + ioctl->toc_valid = 0; + + plat_cdrom_load(ioctl); - plat_cdrom_load(); return 1; } + +int +plat_cdrom_get_local_size(void) +{ + return sizeof(dummy_cdrom_ioctl_t); +} diff --git a/src/qt/languages/86box.pot b/src/qt/languages/86box.pot new file mode 100644 index 000000000..c427d1300 --- /dev/null +++ b/src/qt/languages/86box.pot @@ -0,0 +1,2134 @@ +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Language: \n" +"X-Source-Language: en_US\n" + +msgid "&Action" +msgstr "" + +msgid "&Keyboard requires capture" +msgstr "" + +msgid "&Right CTRL is left ALT" +msgstr "" + +msgid "&Hard Reset..." +msgstr "" + +msgid "&Ctrl+Alt+Del\tCtrl+F12" +msgstr "" + +msgid "Ctrl+Alt+&Esc" +msgstr "" + +msgid "&Pause" +msgstr "" + +msgid "E&xit..." +msgstr "" + +msgid "&View" +msgstr "" + +msgid "&Hide status bar" +msgstr "" + +msgid "Hide &toolbar" +msgstr "" + +msgid "&Resizeable window" +msgstr "" + +msgid "R&emember size && position" +msgstr "" + +msgid "Re&nderer" +msgstr "" + +msgid "&Qt (Software)" +msgstr "" + +msgid "Qt (&OpenGL)" +msgstr "" + +msgid "Open&GL (3.0 Core)" +msgstr "" + +msgid "&VNC" +msgstr "" + +msgid "Specify dimensions..." +msgstr "" + +msgid "F&orce 4:3 display ratio" +msgstr "" + +msgid "&Window scale factor" +msgstr "" + +msgid "&0.5x" +msgstr "" + +msgid "&1x" +msgstr "" + +msgid "1.&5x" +msgstr "" + +msgid "&2x" +msgstr "" + +msgid "&3x" +msgstr "" + +msgid "&4x" +msgstr "" + +msgid "&5x" +msgstr "" + +msgid "&6x" +msgstr "" + +msgid "&7x" +msgstr "" + +msgid "&8x" +msgstr "" + +msgid "Filter method" +msgstr "" + +msgid "&Nearest" +msgstr "" + +msgid "&Linear" +msgstr "" + +msgid "Hi&DPI scaling" +msgstr "" + +msgid "&Fullscreen\tCtrl+Alt+PgUp" +msgstr "" + +msgid "Fullscreen &stretch mode" +msgstr "" + +msgid "&Full screen stretch" +msgstr "" + +msgid "&4:3" +msgstr "" + +msgid "&Square pixels (Keep ratio)" +msgstr "" + +msgid "&Integer scale" +msgstr "" + +msgid "4:&3 Integer scale" +msgstr "" + +msgid "E&GA/(S)VGA settings" +msgstr "" + +msgid "&Inverted VGA monitor" +msgstr "" + +msgid "VGA screen &type" +msgstr "" + +msgid "RGB &Color" +msgstr "" + +msgid "&RGB Grayscale" +msgstr "" + +msgid "&Amber monitor" +msgstr "" + +msgid "&Green monitor" +msgstr "" + +msgid "&White monitor" +msgstr "" + +msgid "Grayscale &conversion type" +msgstr "" + +msgid "BT&601 (NTSC/PAL)" +msgstr "" + +msgid "BT&709 (HDTV)" +msgstr "" + +msgid "&Average" +msgstr "" + +msgid "CGA/PCjr/Tandy/E&GA/(S)VGA overscan" +msgstr "" + +msgid "Change contrast for &monochrome display" +msgstr "" + +msgid "&Media" +msgstr "" + +msgid "&Tools" +msgstr "" + +msgid "&Settings..." +msgstr "" + +msgid "&Update status bar icons" +msgstr "" + +msgid "Take s&creenshot\tCtrl+F11" +msgstr "" + +msgid "&Preferences..." +msgstr "" + +msgid "Enable &Discord integration" +msgstr "" + +msgid "Sound &gain..." +msgstr "" + +msgid "Begin trace\tCtrl+T" +msgstr "" + +msgid "End trace\tCtrl+T" +msgstr "" + +msgid "&Help" +msgstr "" + +msgid "&Documentation..." +msgstr "" + +msgid "&About 86Box..." +msgstr "" + +msgid "&New image..." +msgstr "" + +msgid "&Existing image..." +msgstr "" + +msgid "Existing image (&Write-protected)..." +msgstr "" + +msgid "&Record" +msgstr "" + +msgid "&Play" +msgstr "" + +msgid "&Rewind to the beginning" +msgstr "" + +msgid "&Fast forward to the end" +msgstr "" + +msgid "E&ject" +msgstr "" + +msgid "&Image..." +msgstr "" + +msgid "E&xport to 86F..." +msgstr "" + +msgid "&Mute" +msgstr "" + +msgid "E&mpty" +msgstr "" + +msgid "&Reload previous image" +msgstr "" + +msgid "&Folder..." +msgstr "" + +msgid "Target &framerate" +msgstr "" + +msgid "&Sync with video" +msgstr "" + +msgid "&25 fps" +msgstr "" + +msgid "&30 fps" +msgstr "" + +msgid "&50 fps" +msgstr "" + +msgid "&60 fps" +msgstr "" + +msgid "&75 fps" +msgstr "" + +msgid "&VSync" +msgstr "" + +msgid "&Select shader..." +msgstr "" + +msgid "&Remove shader" +msgstr "" + +msgid "Preferences" +msgstr "" + +msgid "Sound Gain" +msgstr "" + +msgid "New Image" +msgstr "" + +msgid "Settings" +msgstr "" + +msgid "Specify Main Window Dimensions" +msgstr "" + +msgid "OK" +msgstr "" + +msgid "Cancel" +msgstr "" + +msgid "Save these settings as &global defaults" +msgstr "" + +msgid "&Default" +msgstr "" + +msgid "Language:" +msgstr "" + +msgid "Icon set:" +msgstr "" + +msgid "Gain" +msgstr "" + +msgid "File name:" +msgstr "" + +msgid "Disk size:" +msgstr "" + +msgid "RPM mode:" +msgstr "" + +msgid "Progress:" +msgstr "" + +msgid "Width:" +msgstr "" + +msgid "Height:" +msgstr "" + +msgid "Lock to this size" +msgstr "" + +msgid "Machine type:" +msgstr "" + +msgid "Machine:" +msgstr "" + +msgid "Configure" +msgstr "" + +msgid "CPU type:" +msgstr "" + +msgid "Speed:" +msgstr "" + +msgid "Frequency:" +msgstr "" + +msgid "FPU:" +msgstr "" + +msgid "Wait states:" +msgstr "" + +msgid "MB" +msgstr "" + +msgid "Memory:" +msgstr "" + +msgid "Time synchronization" +msgstr "" + +msgid "Disabled" +msgstr "" + +msgid "Enabled (local time)" +msgstr "" + +msgid "Enabled (UTC)" +msgstr "" + +msgid "Dynamic Recompiler" +msgstr "" + +msgid "Video:" +msgstr "" + +msgid "Video #2:" +msgstr "" + +msgid "Voodoo 1 or 2 Graphics" +msgstr "" + +msgid "IBM 8514/A Graphics" +msgstr "" + +msgid "XGA Graphics" +msgstr "" + +msgid "Mouse:" +msgstr "" + +msgid "Joystick:" +msgstr "" + +msgid "Joystick 1..." +msgstr "" + +msgid "Joystick 2..." +msgstr "" + +msgid "Joystick 3..." +msgstr "" + +msgid "Joystick 4..." +msgstr "" + +msgid "Sound card #1:" +msgstr "" + +msgid "Sound card #2:" +msgstr "" + +msgid "Sound card #3:" +msgstr "" + +msgid "Sound card #4:" +msgstr "" + +msgid "MIDI Out Device:" +msgstr "" + +msgid "MIDI In Device:" +msgstr "" + +msgid "Standalone MPU-401" +msgstr "" + +msgid "Use FLOAT32 sound" +msgstr "" + +msgid "FM synth driver" +msgstr "" + +msgid "Nuked (more accurate)" +msgstr "" + +msgid "YMFM (faster)" +msgstr "" + +msgid "Network type:" +msgstr "" + +msgid "PCap device:" +msgstr "" + +msgid "Network adapter:" +msgstr "" + +msgid "COM1 Device:" +msgstr "" + +msgid "COM2 Device:" +msgstr "" + +msgid "COM3 Device:" +msgstr "" + +msgid "COM4 Device:" +msgstr "" + +msgid "LPT1 Device:" +msgstr "" + +msgid "LPT2 Device:" +msgstr "" + +msgid "LPT3 Device:" +msgstr "" + +msgid "LPT4 Device:" +msgstr "" + +msgid "Serial port 1" +msgstr "" + +msgid "Serial port 2" +msgstr "" + +msgid "Serial port 3" +msgstr "" + +msgid "Serial port 4" +msgstr "" + +msgid "Parallel port 1" +msgstr "" + +msgid "Parallel port 2" +msgstr "" + +msgid "Parallel port 3" +msgstr "" + +msgid "Parallel port 4" +msgstr "" + +msgid "HD Controller:" +msgstr "" + +msgid "FD Controller:" +msgstr "" + +msgid "Tertiary IDE Controller" +msgstr "" + +msgid "Quaternary IDE Controller" +msgstr "" + +msgid "SCSI" +msgstr "" + +msgid "Controller 1:" +msgstr "" + +msgid "Controller 2:" +msgstr "" + +msgid "Controller 3:" +msgstr "" + +msgid "Controller 4:" +msgstr "" + +msgid "Cassette" +msgstr "" + +msgid "Hard disks:" +msgstr "" + +msgid "&New..." +msgstr "" + +msgid "&Existing..." +msgstr "" + +msgid "&Remove" +msgstr "" + +msgid "Bus:" +msgstr "" + +msgid "Channel:" +msgstr "" + +msgid "ID:" +msgstr "" + +msgid "&Specify..." +msgstr "" + +msgid "Sectors:" +msgstr "" + +msgid "Heads:" +msgstr "" + +msgid "Cylinders:" +msgstr "" + +msgid "Size (MB):" +msgstr "" + +msgid "Type:" +msgstr "" + +msgid "Image Format:" +msgstr "" + +msgid "Block Size:" +msgstr "" + +msgid "Floppy drives:" +msgstr "" + +msgid "Turbo timings" +msgstr "" + +msgid "Check BPB" +msgstr "" + +msgid "CD-ROM drives:" +msgstr "" + +msgid "MO drives:" +msgstr "" + +msgid "ZIP drives:" +msgstr "" + +msgid "ZIP 250" +msgstr "" + +msgid "ISA RTC:" +msgstr "" + +msgid "ISA Memory Expansion" +msgstr "" + +msgid "Card 1:" +msgstr "" + +msgid "Card 2:" +msgstr "" + +msgid "Card 3:" +msgstr "" + +msgid "Card 4:" +msgstr "" + +msgid "ISABugger device" +msgstr "" + +msgid "POST card" +msgstr "" + +msgid "86Box" +msgstr "" + +msgid "Error" +msgstr "" + +msgid "Fatal error" +msgstr "" + +msgid " - PAUSED" +msgstr "" + +msgid "Press Ctrl+Alt+PgDn to return to windowed mode." +msgstr "" + +msgid "Speed" +msgstr "" + +msgid "ZIP %03i %i (%s): %ls" +msgstr "" + +msgid "ZIP images" +msgstr "" + +msgid "86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory." +msgstr "" + +msgid "(empty)" +msgstr "" + +msgid "All files" +msgstr "" + +msgid "Turbo" +msgstr "" + +msgid "On" +msgstr "" + +msgid "Off" +msgstr "" + +msgid "All images" +msgstr "" + +msgid "Basic sector images" +msgstr "" + +msgid "Surface images" +msgstr "" + +msgid "Machine \"%hs\" is not available due to missing ROMs in the roms/machines directory. Switching to an available machine." +msgstr "" + +msgid "Video card \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card." +msgstr "" + +msgid "Video card #2 \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card." +msgstr "" + +msgid "Machine" +msgstr "" + +msgid "Display" +msgstr "" + +msgid "Input devices" +msgstr "" + +msgid "Sound" +msgstr "" + +msgid "Network" +msgstr "" + +msgid "Ports (COM & LPT)" +msgstr "" + +msgid "Storage controllers" +msgstr "" + +msgid "Hard disks" +msgstr "" + +msgid "Floppy & CD-ROM drives" +msgstr "" + +msgid "Other removable devices" +msgstr "" + +msgid "Other peripherals" +msgstr "" + +msgid "Click to capture mouse" +msgstr "" + +msgid "Press %1 to release mouse" +msgstr "" + +msgid "Press %1 or middle button to release mouse" +msgstr "" + +msgid "Bus" +msgstr "" + +msgid "File" +msgstr "" + +msgid "C" +msgstr "" + +msgid "H" +msgstr "" + +msgid "S" +msgstr "" + +msgid "KB" +msgstr "" + +msgid "Could not initialize the video renderer." +msgstr "" + +msgid "Default" +msgstr "" + +msgid "%i estat(s) d'espera" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "No PCap devices found" +msgstr "" + +msgid "Invalid PCap device" +msgstr "" + +msgid "2-axis, 2-button joystick(s)" +msgstr "" + +msgid "2-axis, 4-button joystick" +msgstr "" + +msgid "2-axis, 6-button joystick" +msgstr "" + +msgid "2-axis, 8-button joystick" +msgstr "" + +msgid "3-axis, 2-button joystick" +msgstr "" + +msgid "3-axis, 4-button joystick" +msgstr "" + +msgid "4-axis, 4-button joystick" +msgstr "" + +msgid "CH Flightstick Pro" +msgstr "" + +msgid "Microsoft SideWinder Pad" +msgstr "" + +msgid "Thrustmaster Flight Control System" +msgstr "" + +msgid "None" +msgstr "" + +msgid "%u MB (CHS: %i, %i, %i)" +msgstr "" + +msgid "Floppy %i (%s): %ls" +msgstr "" + +msgid "Advanced sector images" +msgstr "" + +msgid "Flux images" +msgstr "" + +msgid "Are you sure you want to hard reset the emulated machine?" +msgstr "" + +msgid "Are you sure you want to exit 86Box?" +msgstr "" + +msgid "Unable to initialize Ghostscript" +msgstr "" + +msgid "Unable to initialize GhostPCL" +msgstr "" + +msgid "MO %i (%ls): %ls" +msgstr "" + +msgid "MO images" +msgstr "" + +msgid "Welcome to 86Box!" +msgstr "" + +msgid "Internal device" +msgstr "" + +msgid "Exit" +msgstr "" + +msgid "No ROMs found" +msgstr "" + +msgid "Do you want to save the settings?" +msgstr "" + +msgid "This will hard reset the emulated machine." +msgstr "" + +msgid "Save" +msgstr "" + +msgid "About 86Box" +msgstr "" + +msgid "86Box v" +msgstr "" + +msgid "An emulator of old computers\n\nAuthors: Miran Grča (OBattler), RichardG867, Jasmine Iwanek, TC1995, coldbrewed, Teemu Korhonen (Manaatti), Joakim L. Gilje, Adrien Moulin (elyosh), Daniel Balsom (gloriouscow), Cacodemon345, Fred N. van Kempen (waltje), Tiseno100, reenigne, and others.\n\nWith previous core contributions from Sarah Walker, leilei, JohnElliott, greatpsycho, and others.\n\nReleased under the GNU General Public License version 2 or later. See LICENSE for more information." +msgstr "" + +msgid "Hardware not available" +msgstr "" + +msgid "Make sure %1 is installed and that you are on a %1-compatible network connection." +msgstr "" + +msgid "Invalid configuration" +msgstr "" + +msgid "%1 is required for automatic conversion of PostScript files to PDF.\n\nAny documents sent to the generic PostScript printer will be saved as PostScript (.ps) files." +msgstr "" + +msgid "%1 is required for automatic conversion of PCL files to PDF.\n\nAny documents sent to the generic PCL printer will be saved as Printer Command Language (.pcl) files." +msgstr "" + +msgid "Entering fullscreen mode" +msgstr "" + +msgid "Don't show this message again" +msgstr "" + +msgid "Don't exit" +msgstr "" + +msgid "Reset" +msgstr "" + +msgid "Don't reset" +msgstr "" + +msgid "CD-ROM images" +msgstr "" + +msgid "%1 Device Configuration" +msgstr "" + +msgid "Monitor in sleep mode" +msgstr "" + +msgid "OpenGL Shaders" +msgstr "" + +msgid "OpenGL options" +msgstr "" + +msgid "You are loading an unsupported configuration" +msgstr "" + +msgid "CPU type filtering based on selected machine is disabled for this emulated machine.\n\nThis makes it possible to choose a CPU that is otherwise incompatible with the selected machine. However, you may run into incompatibilities with the machine BIOS or other software.\n\nEnabling this setting is not officially supported and any bug reports filed may be closed as invalid." +msgstr "" + +msgid "Continue" +msgstr "" + +msgid "Cassette: %s" +msgstr "" + +msgid "Cassette images" +msgstr "" + +msgid "Cartridge %i: %ls" +msgstr "" + +msgid "Cartridge images" +msgstr "" + +msgid "Error initializing renderer" +msgstr "" + +msgid "OpenGL (3.0 Core) renderer could not be initialized. Use another renderer." +msgstr "" + +msgid "Resume execution" +msgstr "" + +msgid "Pause execution" +msgstr "" + +msgid "Press Ctrl+Alt+Del" +msgstr "" + +msgid "Press Ctrl+Alt+Esc" +msgstr "" + +msgid "Hard reset" +msgstr "" + +msgid "ACPI shutdown" +msgstr "" + +msgid "Hard disk (%1)" +msgstr "" + +msgid "MFM/RLL or ESDI CD-ROM drives never existed" +msgstr "" + +msgid "Custom..." +msgstr "" + +msgid "Custom (large)..." +msgstr "" + +msgid "Add New Hard Disk" +msgstr "" + +msgid "Add Existing Hard Disk" +msgstr "" + +msgid "HDI disk images cannot be larger than 4 GB." +msgstr "" + +msgid "Disk images cannot be larger than 127 GB." +msgstr "" + +msgid "Hard disk images" +msgstr "" + +msgid "Unable to read file" +msgstr "" + +msgid "Unable to write file" +msgstr "" + +msgid "HDI or HDX images with a sector size other than 512 are not supported." +msgstr "" + +msgid "Disk image file already exists" +msgstr "" + +msgid "Please specify a valid file name." +msgstr "" + +msgid "Disk image created" +msgstr "" + +msgid "Make sure the file exists and is readable." +msgstr "" + +msgid "Make sure the file is being saved to a writable directory." +msgstr "" + +msgid "Disk image too large" +msgstr "" + +msgid "Remember to partition and format the newly-created drive." +msgstr "" + +msgid "The selected file will be overwritten. Are you sure you want to use it?" +msgstr "" + +msgid "Unsupported disk image" +msgstr "" + +msgid "Overwrite" +msgstr "" + +msgid "Don't overwrite" +msgstr "" + +msgid "Raw image" +msgstr "" + +msgid "HDI image" +msgstr "" + +msgid "HDX image" +msgstr "" + +msgid "Fixed-size VHD" +msgstr "" + +msgid "Dynamic-size VHD" +msgstr "" + +msgid "Differencing VHD" +msgstr "" + +msgid "(N/A)" +msgstr "" + +msgid "Raw image (.img)" +msgstr "" + +msgid "HDI image (.hdi)" +msgstr "" + +msgid "HDX image (.hdx)" +msgstr "" + +msgid "Fixed-size VHD (.vhd)" +msgstr "" + +msgid "Dynamic-size VHD (.vhd)" +msgstr "" + +msgid "Differencing VHD (.vhd)" +msgstr "" + +msgid "Large blocks (2 MB)" +msgstr "" + +msgid "Small blocks (512 KB)" +msgstr "" + +msgid "VHD files" +msgstr "" + +msgid "Select the parent VHD" +msgstr "" + +msgid "This could mean that the parent image was modified after the differencing image was created.\n\nIt can also happen if the image files were moved or copied, or by a bug in the program that created this disk.\n\nDo you want to fix the timestamps?" +msgstr "" + +msgid "Parent and child disk timestamps do not match" +msgstr "" + +msgid "Could not fix VHD timestamp." +msgstr "" + +msgid "MFM/RLL" +msgstr "" + +msgid "XTA" +msgstr "" + +msgid "ESDI" +msgstr "" + +msgid "IDE" +msgstr "" + +msgid "ATAPI" +msgstr "" + +msgid "CD-ROM %i (%s): %s" +msgstr "" + +msgid "160 KB" +msgstr "" + +msgid "180 KB" +msgstr "" + +msgid "320 KB" +msgstr "" + +msgid "360 KB" +msgstr "" + +msgid "640 KB" +msgstr "" + +msgid "720 KB" +msgstr "" + +msgid "1.2 MB" +msgstr "" + +msgid "1.25 MB" +msgstr "" + +msgid "1.44 MB" +msgstr "" + +msgid "DMF (cluster 1024)" +msgstr "" + +msgid "DMF (cluster 2048)" +msgstr "" + +msgid "2.88 MB" +msgstr "" + +msgid "ZIP 100" +msgstr "" + +msgid "3.5\" 128 MB (ISO 10090)" +msgstr "" + +msgid "3.5\" 230 MB (ISO 13963)" +msgstr "" + +msgid "3.5\" 540 MB (ISO 15498)" +msgstr "" + +msgid "3.5\" 640 MB (ISO 15498)" +msgstr "" + +msgid "3.5\" 1.3 GB (GigaMO)" +msgstr "" + +msgid "3.5\" 2.3 GB (GigaMO 2)" +msgstr "" + +msgid "5.25\" 600 MB" +msgstr "" + +msgid "5.25\" 650 MB" +msgstr "" + +msgid "5.25\" 1 GB" +msgstr "" + +msgid "5.25\" 1.3 GB" +msgstr "" + +msgid "Perfect RPM" +msgstr "" + +msgid "1% below perfect RPM" +msgstr "" + +msgid "1.5% below perfect RPM" +msgstr "" + +msgid "2% below perfect RPM" +msgstr "" + +msgid "(System Default)" +msgstr "" + +msgid "Failed to initialize network driver" +msgstr "" + +msgid "The network configuration will be switched to the null driver" +msgstr "" + +msgid "Mouse sensitivity:" +msgstr "" + +msgid "Select media images from program working directory" +msgstr "" + +msgid "PIT mode:" +msgstr "" + +msgid "Auto" +msgstr "" + +msgid "Slow" +msgstr "" + +msgid "Fast" +msgstr "" + +msgid "&Auto-pause on focus loss" +msgstr "" + +msgid "WinBox is no longer supported" +msgstr "" + +msgid "Development of the WinBox manager stopped in 2022 due to a lack of maintainers. As we direct our efforts towards making 86Box even better, we have made the decision to no longer support WinBox as a manager.\n\nNo further updates will be provided through WinBox, and you may encounter incorrect behavior should you continue using it with newer versions of 86Box. Any bug reports related to WinBox behavior will be closed as invalid.\n\nGo to 86box.net for a list of other managers you can use." +msgstr "" + +msgid "Generate" +msgstr "" + +msgid "Joystick configuration" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "%1 (X axis)" +msgstr "" + +msgid "%1 (Y axis)" +msgstr "" + +msgid "MCA devices" +msgstr "" + +msgid "List of MCA devices:" +msgstr "" + +msgid "Tablet tool" +msgstr "" + +msgid "Qt (OpenGL &ES)" +msgstr "" + +msgid "About Qt" +msgstr "" + +msgid "MCA devices..." +msgstr "" + +msgid "Show non-primary monitors" +msgstr "" + +msgid "Open screenshots folder..." +msgstr "" + +msgid "Apply fullscreen stretch mode when maximized" +msgstr "" + +msgid "Cursor/Puck" +msgstr "" + +msgid "Pen" +msgstr "" + +msgid "Host CD/DVD Drive (%1:)" +msgstr "" + +msgid "&Connected" +msgstr "" + +msgid "Clear image history" +msgstr "" + +msgid "Create..." +msgstr "" + +msgid "previous image" +msgstr "" + +msgid "Host CD/DVD Drive (%1)" +msgstr "" + +msgid "Unknown Bus" +msgstr "" + +msgid "Null Driver" +msgstr "" + +msgid "NIC %02i (%ls) %ls" +msgstr "" + +msgid "Error opening \"%1\": %2" +msgstr "" + +msgid "Error compiling vertex shader in file \"%1\"" +msgstr "" + +msgid "Error compiling fragment shader in file \"%1\"" +msgstr "" + +msgid "Error linking shader program in file \"%1\"" +msgstr "" + +msgid "OpenGL 3.0 renderer options" +msgstr "" + +msgid "Render behavior" +msgstr "" + +msgid "Use target framerate:" +msgstr "" + +msgid " fps" +msgstr "" + +msgid "VSync" +msgstr "" + +msgid "<html><head/><body><p>Render each frame immediately, in sync with the emulated display.</p><p><span style=" font-style:italic;">This is the recommended option if the shaders in use don't utilize frametime for animated effects.</span></p></body></html>" +msgstr "" + +msgid "Synchronize with video" +msgstr "" + +msgid "Shaders" +msgstr "" + +msgid "Remove" +msgstr "" + +msgid "No shader selected" +msgstr "" + +msgid "Browse..." +msgstr "" + +msgid "Shader error" +msgstr "" + +msgid "Could not load shaders." +msgstr "" + +msgid "More information in details." +msgstr "" + +msgid "Couldn't create OpenGL context." +msgstr "" + +msgid "Couldn't switch to OpenGL context." +msgstr "" + +msgid "OpenGL version 3.0 or greater is required. Current version is %1.%2" +msgstr "" + +msgid "OpenGL initialization failed. Error %1." +msgstr "" + +msgid "Error initializing OpenGL" +msgstr "" + +msgid "Falling back to software rendering.\n" +msgstr "" + +msgid "Allocating memory for unpack buffer failed.\n" +msgstr "" + +msgid "<html><head/><body><p>When selecting media images (CD-ROM, floppy, etc.) the open dialog will start in the same directory as the 86Box configuration file. This setting will likely only make a difference on macOS.</p></body></html>" +msgstr "" + +msgid "This machine might have been moved or copied." +msgstr "" + +msgid "In order to ensure proper networking functionality, 86Box needs to know if this machine was moved or copied.\n\nSelect \"I Copied It\" if you are not sure." +msgstr "" + +msgid "I Moved It" +msgstr "" + +msgid "I Copied It" +msgstr "" + +msgid "86Box Monitor #" +msgstr "" + +msgid "No MCA devices." +msgstr "" + +msgid "MiB" +msgstr "" + +msgid "Network Card #1" +msgstr "" + +msgid "Network Card #2" +msgstr "" + +msgid "Network Card #3" +msgstr "" + +msgid "Network Card #4" +msgstr "" + +msgid "Mode" +msgstr "" + +msgid "Interface" +msgstr "" + +msgid "Adapter" +msgstr "" + +msgid "VDE Socket" +msgstr "" + +msgid "86Box Unit Tester" +msgstr "" + +msgid "Novell NetWare 2.x Key Card" +msgstr "" + +msgid "Serial port passthrough 1" +msgstr "" + +msgid "Serial port passthrough 2" +msgstr "" + +msgid "Serial port passthrough 3" +msgstr "" + +msgid "Serial port passthrough 4" +msgstr "" + +msgid "Vision Systems LBA Enhancer" +msgstr "" + +msgid "Renderer options..." +msgstr "" + +msgid "Logitech/Microsoft Bus Mouse" +msgstr "" + +msgid "Microsoft Bus Mouse (InPort)" +msgstr "" + +msgid "Mouse Systems Serial Mouse" +msgstr "" + +msgid "Microsoft Serial Mouse" +msgstr "" + +msgid "Logitech Serial Mouse" +msgstr "" + +msgid "PS/2 Mouse" +msgstr "" + +msgid "3M MicroTouch (Serial)" +msgstr "" + +msgid "[COM] Standard Hayes-compliant Modem" +msgstr "" + +msgid "Roland MT-32 Emulation" +msgstr "" + +msgid "Roland MT-32 (New) Emulation" +msgstr "" + +msgid "Roland CM-32L Emulation" +msgstr "" + +msgid "Roland CM-32LN Emulation" +msgstr "" + +msgid "OPL4-ML Daughterboard" +msgstr "" + +msgid "System MIDI" +msgstr "" + +msgid "MIDI Input Device" +msgstr "" + +msgid "BIOS Address" +msgstr "" + +msgid "Enable BIOS extension ROM Writes" +msgstr "" + +msgid "Address" +msgstr "" + +msgid "IRQ" +msgstr "" + +msgid "BIOS Revision" +msgstr "" + +msgid "Translate 26 -> 17" +msgstr "" + +msgid "Language" +msgstr "" + +msgid "Enable backlight" +msgstr "" + +msgid "Invert colors" +msgstr "" + +msgid "BIOS size" +msgstr "" + +msgid "Map C0000-C7FFF as UMB" +msgstr "" + +msgid "Map C8000-CFFFF as UMB" +msgstr "" + +msgid "Map D0000-D7FFF as UMB" +msgstr "" + +msgid "Map D8000-DFFFF as UMB" +msgstr "" + +msgid "Map E0000-E7FFF as UMB" +msgstr "" + +msgid "Map E8000-EFFFF as UMB" +msgstr "" + +msgid "JS9 Jumper (JIM)" +msgstr "" + +msgid "MIDI Output Device" +msgstr "" + +msgid "MIDI Real time" +msgstr "" + +msgid "MIDI Thru" +msgstr "" + +msgid "MIDI Clockout" +msgstr "" + +msgid "SoundFont" +msgstr "" + +msgid "Output Gain" +msgstr "" + +msgid "Chorus" +msgstr "" + +msgid "Chorus Voices" +msgstr "" + +msgid "Chorus Level" +msgstr "" + +msgid "Chorus Speed" +msgstr "" + +msgid "Chorus Depth" +msgstr "" + +msgid "Chorus Waveform" +msgstr "" + +msgid "Reverb" +msgstr "" + +msgid "Reverb Room Size" +msgstr "" + +msgid "Reverb Damping" +msgstr "" + +msgid "Reverb Width" +msgstr "" + +msgid "Reverb Level" +msgstr "" + +msgid "Interpolation Method" +msgstr "" + +msgid "Reverb Output Gain" +msgstr "" + +msgid "Reversed stereo" +msgstr "" + +msgid "Nice ramp" +msgstr "" + +msgid "Hz" +msgstr "" + +msgid "Buttons" +msgstr "" + +msgid "Serial Port" +msgstr "" + +msgid "RTS toggle" +msgstr "" + +msgid "Revision" +msgstr "" + +msgid "Controller" +msgstr "" + +msgid "Show Crosshair" +msgstr "" + +msgid "DMA" +msgstr "" + +msgid "MAC Address" +msgstr "" + +msgid "MAC Address OUI" +msgstr "" + +msgid "Enable BIOS" +msgstr "" + +msgid "Baud Rate" +msgstr "" + +msgid "TCP/IP listening port" +msgstr "" + +msgid "Phonebook File" +msgstr "" + +msgid "Telnet emulation" +msgstr "" + +msgid "RAM Address" +msgstr "" + +msgid "RAM size" +msgstr "" + +msgid "Initial RAM size" +msgstr "" + +msgid "Serial Number" +msgstr "" + +msgid "Host ID" +msgstr "" + +msgid "FDC Address" +msgstr "" + +msgid "MPU-401 Address" +msgstr "" + +msgid "MPU-401 IRQ" +msgstr "" + +msgid "Receive MIDI input" +msgstr "" + +msgid "Low DMA" +msgstr "" + +msgid "Enable Game port" +msgstr "" + +msgid "Surround module" +msgstr "" + +msgid "CODEC" +msgstr "" + +msgid "Raise CODEC interrupt on CODEC setup (needed by some drivers)" +msgstr "" + +msgid "SB Address" +msgstr "" + +msgid "WSS IRQ" +msgstr "" + +msgid "WSS DMA" +msgstr "" + +msgid "Enable OPL" +msgstr "" + +msgid "Receive MIDI input (MPU-401)" +msgstr "" + +msgid "SB low DMA" +msgstr "" + +msgid "6CH variant (6-channel)" +msgstr "" + +msgid "Enable CMS" +msgstr "" + +msgid "Mixer" +msgstr "" + +msgid "High DMA" +msgstr "" + +msgid "Control PC speaker" +msgstr "" + +msgid "Memory size" +msgstr "" + +msgid "EMU8000 Address" +msgstr "" + +msgid "IDE Controller" +msgstr "" + +msgid "Codec" +msgstr "" + +msgid "GUS type" +msgstr "" + +msgid "Enable 0x04 \"Exit 86Box\" command" +msgstr "" + +msgid "Display type" +msgstr "" + +msgid "Composite type" +msgstr "" + +msgid "RGB type" +msgstr "" + +msgid "Line doubling type" +msgstr "" + +msgid "Snow emulation" +msgstr "" + +msgid "Monitor type" +msgstr "" + +msgid "Character set" +msgstr "" + +msgid "XGA type" +msgstr "" + +msgid "Instance" +msgstr "" + +msgid "MMIO Address" +msgstr "" + +msgid "RAMDAC type" +msgstr "" + +msgid "Blend" +msgstr "" + +msgid "Bilinear filtering" +msgstr "" + +msgid "Dithering" +msgstr "" + +msgid "Enable NMI for CGA emulation" +msgstr "" + +msgid "Voodoo type" +msgstr "" + +msgid "Framebuffer memory size" +msgstr "" + +msgid "Texture memory size" +msgstr "" + +msgid "Dither subtraction" +msgstr "" + +msgid "Screen Filter" +msgstr "" + +msgid "Render threads" +msgstr "" + +msgid "SLI" +msgstr "" + +msgid "Start Address" +msgstr "" + +msgid "Contiguous Size" +msgstr "" + +msgid "I/O Width" +msgstr "" + +msgid "Transfer Speed" +msgstr "" + +msgid "EMS mode" +msgstr "" + +msgid "Address for > 2 MB" +msgstr "" + +msgid "Frame Address" +msgstr "" + +msgid "USA" +msgstr "" + +msgid "Danish" +msgstr "" + +msgid "Always at selected speed" +msgstr "" + +msgid "BIOS setting + Hotkeys (off during POST)" +msgstr "" + +msgid "64 kB starting from F0000" +msgstr "" + +msgid "128 kB starting from E0000 (address MSB inverted, last 64KB first)" +msgstr "" + +msgid "Sine" +msgstr "" + +msgid "Triangle" +msgstr "" + +msgid "Linear" +msgstr "" + +msgid "4th Order" +msgstr "" + +msgid "7th Order" +msgstr "" + +msgid "Non-timed (original)" +msgstr "" + +msgid "45 Hz (JMP2 not populated)" +msgstr "" + +msgid "Two" +msgstr "" + +msgid "Three" +msgstr "" + +msgid "Wheel" +msgstr "" + +msgid "Five + Wheel" +msgstr "" + +msgid "A3 - SMT2 Serial / SMT3(R)V" +msgstr "" + +msgid "Q1 - SMT3(R) Serial" +msgstr "" + +msgid "8 KB" +msgstr "" + +msgid "32 KB" +msgstr "" + +msgid "16 KB" +msgstr "" + +msgid "64 KB" +msgstr "" + +msgid "Disable BIOS" +msgstr "" + +msgid "512 KB" +msgstr "" + +msgid "2 MB" +msgstr "" + +msgid "8 MB" +msgstr "" + +msgid "28 MB" +msgstr "" + +msgid "1 MB" +msgstr "" + +msgid "4 MB" +msgstr "" + +msgid "12 MB" +msgstr "" + +msgid "16 MB" +msgstr "" + +msgid "20 MB" +msgstr "" + +msgid "24 MB" +msgstr "" + +msgid "SigmaTel STAC9721T (stereo)" +msgstr "" + +msgid "Classic" +msgstr "" + +msgid "256 KB" +msgstr "" + +msgid "Composite" +msgstr "" + +msgid "Old" +msgstr "" + +msgid "New" +msgstr "" + +msgid "Color (generic)" +msgstr "" + +msgid "Green Monochrome" +msgstr "" + +msgid "Amber Monochrome" +msgstr "" + +msgid "Gray Monochrome" +msgstr "" + +msgid "Color (no brown)" +msgstr "" + +msgid "Color (IBM 5153)" +msgstr "" + +msgid "Simple doubling" +msgstr "" + +msgid "sRGB interpolation" +msgstr "" + +msgid "Linear interpolation" +msgstr "" + +msgid "128 KB" +msgstr "" + +msgid "Monochrome (5151/MDA) (white)" +msgstr "" + +msgid "Monochrome (5151/MDA) (green)" +msgstr "" + +msgid "Monochrome (5151/MDA) (amber)" +msgstr "" + +msgid "Color 40x25 (5153/CGA)" +msgstr "" + +msgid "Color 80x25 (5153/CGA)" +msgstr "" + +msgid "Enhanced Color - Normal Mode (5154/ECD)" +msgstr "" + +msgid "Enhanced Color - Enhanced Mode (5154/ECD)" +msgstr "" + +msgid "Green" +msgstr "" + +msgid "Amber" +msgstr "" + +msgid "Gray" +msgstr "" + +msgid "Color" +msgstr "" + +msgid "U.S. English" +msgstr "" + +msgid "Scandinavian" +msgstr "" + +msgid "Other languages" +msgstr "" + +msgid "Bochs latest" +msgstr "" + +msgid "Mono Non-Interlaced" +msgstr "" + +msgid "Color Interlaced" +msgstr "" + +msgid "Color Non-Interlaced" +msgstr "" + +msgid "3Dfx Voodoo Graphics" +msgstr "" + +msgid "Obsidian SB50 + Amethyst (2 TMUs)" +msgstr "" + +msgid "8-bit" +msgstr "" + +msgid "16-bit" +msgstr "" + +msgid "Standard (150ns)" +msgstr "" + +msgid "High-Speed (120ns)" +msgstr "" + +msgid "Enabled" +msgstr "" + +msgid "Standard" +msgstr "" + +msgid "High-Speed" +msgstr "" + +msgid "Stereo LPT DAC" +msgstr "" + +msgid "Generic Text Printer" +msgstr "" + +msgid "Generic ESC/P Dot-Matrix Printer" +msgstr "" + +msgid "Generic PostScript Printer" +msgstr "" + +msgid "Generic PCL5e Printer" +msgstr "" + +msgid "Parallel Line Internet Protocol" +msgstr "" + +msgid "Protection Dongle for Savage Quest" +msgstr "" + +msgid "Serial Passthrough Device" +msgstr "" + +msgid "Passthrough Mode" +msgstr "" + +msgid "Host Serial Device" +msgstr "" + +msgid "Name of pipe" +msgstr "" + +msgid "Data bits" +msgstr "" + +msgid "Stop bits" +msgstr "" + +msgid "Baud Rate of Passthrough" +msgstr "" + +msgid "Named Pipe (Server)" +msgstr "" + +msgid "Host Serial Passthrough" +msgstr "" + +msgid "Eject %s" +msgstr "" + +msgid "&Unmute" +msgstr "" + +msgid "Softfloat FPU" +msgstr "" + +msgid "High performance impact" +msgstr "" + +msgid "RAM Disk (max. speed)" +msgstr "" + +msgid "IBM 8514/A clone (ISA)" +msgstr "" + +msgid "Vendor" +msgstr "" + +msgid "30 Hz (JMP2 = 1)" +msgstr "" + +msgid "60 Hz (JMP2 = 2)" +msgstr "" + +msgid "Generic PC/XT Memory Expansion" +msgstr "" + +msgid "Generic PC/AT Memory Expansion" +msgstr "" diff --git a/src/qt/languages/nl-NL.po b/src/qt/languages/nl-NL.po new file mode 100644 index 000000000..b54bc1cb0 --- /dev/null +++ b/src/qt/languages/nl-NL.po @@ -0,0 +1,2134 @@ +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Language: nl_NL\n" +"X-Source-Language: en_US\n" + +msgid "&Action" +msgstr "&Actie" + +msgid "&Keyboard requires capture" +msgstr "&Keyboard vereist vastleggen" + +msgid "&Right CTRL is left ALT" +msgstr "&Rechtse CTRL is linkse ALT" + +msgid "&Hard Reset..." +msgstr "&Harde Reset..." + +msgid "&Ctrl+Alt+Del\tCtrl+F12" +msgstr "&Ctrl+Alt+Del\tCtrl+F12" + +msgid "Ctrl+Alt+&Esc" +msgstr "Ctrl+Alt+&Esc" + +msgid "&Pause" +msgstr "&Pauze" + +msgid "E&xit..." +msgstr "&Afsluiten..." + +msgid "&View" +msgstr "&Beeld" + +msgid "&Hide status bar" +msgstr "&Statusbalk verbergen" + +msgid "Hide &toolbar" +msgstr "Verberg &toolbar" + +msgid "&Resizeable window" +msgstr "&Venster met aanpasbare grootte" + +msgid "R&emember size && position" +msgstr "&Onthoud grootte && positie" + +msgid "Re&nderer" +msgstr "Re&nderer" + +msgid "&Qt (Software)" +msgstr "&Qt (software)" + +msgid "Qt (&OpenGL)" +msgstr "Qt (&OpenGL)" + +msgid "Open&GL (3.0 Core)" +msgstr "Open&GL (3.0 Core)" + +msgid "&VNC" +msgstr "&VNC" + +msgid "Specify dimensions..." +msgstr "Afmetingen opgeven..." + +msgid "F&orce 4:3 display ratio" +msgstr "F&orceer 4:3 beeldverhouding" + +msgid "&Window scale factor" +msgstr "&Venster schaalfactor" + +msgid "&0.5x" +msgstr "&0,5x" + +msgid "&1x" +msgstr "&1x" + +msgid "1.&5x" +msgstr "1,&5x" + +msgid "&2x" +msgstr "&2x" + +msgid "&3x" +msgstr "&3x" + +msgid "&4x" +msgstr "&4x" + +msgid "&5x" +msgstr "&5x" + +msgid "&6x" +msgstr "&6x" + +msgid "&7x" +msgstr "&7x" + +msgid "&8x" +msgstr "&8x" + +msgid "Filter method" +msgstr "Filtermethode" + +msgid "&Nearest" +msgstr "&Dichtsbijzijnde" + +msgid "&Linear" +msgstr "&Lineair" + +msgid "Hi&DPI scaling" +msgstr "Hi&DPI-schaling" + +msgid "&Fullscreen\tCtrl+Alt+PgUp" +msgstr "&Fullscreen\tCtrl+Alt+PgUp" + +msgid "Fullscreen &stretch mode" +msgstr "Volledig scherm &uitrekmodus" + +msgid "&Full screen stretch" +msgstr "&Volledig scherm uitrekken" + +msgid "&4:3" +msgstr "&4:3" + +msgid "&Square pixels (Keep ratio)" +msgstr "&Vierkante pixels (behoud verhouding)" + +msgid "&Integer scale" +msgstr "&Integerschaal" + +msgid "4:&3 Integer scale" +msgstr "4:&3 integerschaal" + +msgid "E&GA/(S)VGA settings" +msgstr "E&GA/(S)VGA-instellingen" + +msgid "&Inverted VGA monitor" +msgstr "Ge&ïnverteerde VGA-monitor" + +msgid "VGA screen &type" +msgstr "VGA-scherm &type" + +msgid "RGB &Color" +msgstr "RGB &Kleur" + +msgid "&RGB Grayscale" +msgstr "&RGB grijstinten" + +msgid "&Amber monitor" +msgstr "&Amber monitor" + +msgid "&Green monitor" +msgstr "&Groene monitor" + +msgid "&White monitor" +msgstr "&Witte monitor" + +msgid "Grayscale &conversion type" +msgstr "Grijstinten &conversietype" + +msgid "BT&601 (NTSC/PAL)" +msgstr "BT&601 (NTSC/PAL)" + +msgid "BT&709 (HDTV)" +msgstr "BT&709 (HDTV)" + +msgid "&Average" +msgstr "&Gemiddelde" + +msgid "CGA/PCjr/Tandy/E&GA/(S)VGA overscan" +msgstr "CGA/PCjr/Tandy/E&GA/(S)VGA overscan" + +msgid "Change contrast for &monochrome display" +msgstr "Contrast wijzigen voor &monochroom beeldscherm" + +msgid "&Media" +msgstr "&Media" + +msgid "&Tools" +msgstr "&Tools" + +msgid "&Settings..." +msgstr "&Instellingen..." + +msgid "&Update status bar icons" +msgstr "&Statusbalkpictogrammen bijwerken" + +msgid "Take s&creenshot\tCtrl+F11" +msgstr "Maak een schermafbeelding\tCtrl+F11" + +msgid "&Preferences..." +msgstr "&Voorkeuren..." + +msgid "Enable &Discord integration" +msgstr "&Discord integratie inschakelen" + +msgid "Sound &gain..." +msgstr "&Geluidsversterking..." + +msgid "Begin trace\tCtrl+T" +msgstr "Begin traceren\tCtrl+T" + +msgid "End trace\tCtrl+T" +msgstr "Traceren beëindigen\tCtrl+T" + +msgid "&Help" +msgstr "&Help" + +msgid "&Documentation..." +msgstr "&Documentatie..." + +msgid "&About 86Box..." +msgstr "&Over 86Box..." + +msgid "&New image..." +msgstr "&Nieuw image..." + +msgid "&Existing image..." +msgstr "&Bestaande image..." + +msgid "Existing image (&Write-protected)..." +msgstr "Bestaande image (&Schrijfbeveiligd)..." + +msgid "&Record" +msgstr "&Opnemen" + +msgid "&Play" +msgstr "&Play" + +msgid "&Rewind to the beginning" +msgstr "&Terugspoelen naar het begin" + +msgid "&Fast forward to the end" +msgstr "&Snel vooruit naar het einde" + +msgid "E&ject" +msgstr "E&ject" + +msgid "&Image..." +msgstr "&Image..." + +msgid "E&xport to 86F..." +msgstr "E&xporteer naar 86F..." + +msgid "&Mute" +msgstr "&Mute" + +msgid "E&mpty" +msgstr "E&mpty" + +msgid "&Reload previous image" +msgstr "&Herlaad vorige image" + +msgid "&Folder..." +msgstr "&Map..." + +msgid "Target &framerate" +msgstr "Doel &framerate" + +msgid "&Sync with video" +msgstr "&Synchroniseer met video" + +msgid "&25 fps" +msgstr "&25 fps" + +msgid "&30 fps" +msgstr "&30 fps" + +msgid "&50 fps" +msgstr "&50 fps" + +msgid "&60 fps" +msgstr "&60 fps" + +msgid "&75 fps" +msgstr "&75 fps" + +msgid "&VSync" +msgstr "&VSync" + +msgid "&Select shader..." +msgstr "&Selecteer shader..." + +msgid "&Remove shader" +msgstr "&Remove shader" + +msgid "Preferences" +msgstr "Voorkeuren" + +msgid "Sound Gain" +msgstr "Geluidsversterking" + +msgid "New Image" +msgstr "Nieuw image" + +msgid "Settings" +msgstr "Instellingen" + +msgid "Specify Main Window Dimensions" +msgstr "Afmetingen hoofdvenster opgeven" + +msgid "OK" +msgstr "OK" + +msgid "Cancel" +msgstr "Annuleren" + +msgid "Save these settings as &global defaults" +msgstr "Sla deze instellingen op als &globale standaardinstellingen" + +msgid "&Default" +msgstr "&Standaard" + +msgid "Language:" +msgstr "Taal:" + +msgid "Icon set:" +msgstr "Pictogrammenset:" + +msgid "Gain" +msgstr "Versterking" + +msgid "File name:" +msgstr "Bestandsnaam:" + +msgid "Disk size:" +msgstr "Schijfgrootte:" + +msgid "RPM mode:" +msgstr "RPM-modus:" + +msgid "Progress:" +msgstr "Vooruitgang:" + +msgid "Width:" +msgstr "Breedte:" + +msgid "Height:" +msgstr "Hoogte:" + +msgid "Lock to this size" +msgstr "Leg vast op deze grootte" + +msgid "Machine type:" +msgstr "Machinetype:" + +msgid "Machine:" +msgstr "Machine:" + +msgid "Configure" +msgstr "Configureren" + +msgid "CPU type:" +msgstr "CPU type:" + +msgid "Speed:" +msgstr "Snelheid:" + +msgid "Frequency:" +msgstr "Frequentie:" + +msgid "FPU:" +msgstr "FPU:" + +msgid "Wait states:" +msgstr "Wachttoestanden:" + +msgid "MB" +msgstr "MB" + +msgid "Memory:" +msgstr "Geheugen:" + +msgid "Time synchronization" +msgstr "Tijdsynchronisatie" + +msgid "Disabled" +msgstr "Uitgeschakeld" + +msgid "Enabled (local time)" +msgstr "Ingeschakeld (lokale tijd)" + +msgid "Enabled (UTC)" +msgstr "Ingeschakeld (UTC)" + +msgid "Dynamic Recompiler" +msgstr "Dynamische Recompiler" + +msgid "Video:" +msgstr "Video:" + +msgid "Video #2:" +msgstr "Video #2:" + +msgid "Voodoo 1 or 2 Graphics" +msgstr "Voodoo 1 of 2 graphics" + +msgid "IBM 8514/A Graphics" +msgstr "IBM 8514/A-graphics" + +msgid "XGA Graphics" +msgstr "XGA Graphics" + +msgid "Mouse:" +msgstr "Muis:" + +msgid "Joystick:" +msgstr "Joystick:" + +msgid "Joystick 1..." +msgstr "Joystick 1..." + +msgid "Joystick 2..." +msgstr "Joystick 2..." + +msgid "Joystick 3..." +msgstr "Joystick 3..." + +msgid "Joystick 4..." +msgstr "Joystick 4..." + +msgid "Sound card #1:" +msgstr "Geluidskaart #1:" + +msgid "Sound card #2:" +msgstr "Geluidskaart #2:" + +msgid "Sound card #3:" +msgstr "Geluidskaart #3:" + +msgid "Sound card #4:" +msgstr "Geluidskaart #4:" + +msgid "MIDI Out Device:" +msgstr "MIDI Out-apparaat:" + +msgid "MIDI In Device:" +msgstr "MIDI In-apparaat:" + +msgid "Standalone MPU-401" +msgstr "Standalone MPU-401" + +msgid "Use FLOAT32 sound" +msgstr "Gebruik FLOAT32-geluid" + +msgid "FM synth driver" +msgstr "FM-synthesizer" + +msgid "Nuked (more accurate)" +msgstr "Nuked (nauwkeuriger)" + +msgid "YMFM (faster)" +msgstr "YMFM (sneller)" + +msgid "Network type:" +msgstr "Type netwerk:" + +msgid "PCap device:" +msgstr "PCap-apparaat:" + +msgid "Network adapter:" +msgstr "Netwerkadapter:" + +msgid "COM1 Device:" +msgstr "COM1-apparaat:" + +msgid "COM2 Device:" +msgstr "COM2-apparaat:" + +msgid "COM3 Device:" +msgstr "COM3-apparaat:" + +msgid "COM4 Device:" +msgstr "COM4-apparaat:" + +msgid "LPT1 Device:" +msgstr "LPT1-apparaat:" + +msgid "LPT2 Device:" +msgstr "LPT2-apparaat:" + +msgid "LPT3 Device:" +msgstr "LPT3-apparaat:" + +msgid "LPT4 Device:" +msgstr "LPT4-apparaat:" + +msgid "Serial port 1" +msgstr "Seriële poort 1" + +msgid "Serial port 2" +msgstr "Seriële poort 2" + +msgid "Serial port 3" +msgstr "Seriële poort 3" + +msgid "Serial port 4" +msgstr "Seriële poort 4" + +msgid "Parallel port 1" +msgstr "Parallelle poort 1" + +msgid "Parallel port 2" +msgstr "Parallelle poort 2" + +msgid "Parallel port 3" +msgstr "Parallelle poort 3" + +msgid "Parallel port 4" +msgstr "Parallelle poort 4" + +msgid "HD Controller:" +msgstr "HD-controller:" + +msgid "FD Controller:" +msgstr "FD-Controller:" + +msgid "Tertiary IDE Controller" +msgstr "Tertiaire IDE-controller" + +msgid "Quaternary IDE Controller" +msgstr "Quaternaire IDE-controller" + +msgid "SCSI" +msgstr "SCSI" + +msgid "Controller 1:" +msgstr "Controller 1:" + +msgid "Controller 2:" +msgstr "Controller 2:" + +msgid "Controller 3:" +msgstr "Controller 3:" + +msgid "Controller 4:" +msgstr "Controller 4:" + +msgid "Cassette" +msgstr "Cassette" + +msgid "Hard disks:" +msgstr "Harde schijven:" + +msgid "&New..." +msgstr "&Nieuw..." + +msgid "&Existing..." +msgstr "&Bestaande..." + +msgid "&Remove" +msgstr "&Verwijderen" + +msgid "Bus:" +msgstr "Bus:" + +msgid "Channel:" +msgstr "Kanaal:" + +msgid "ID:" +msgstr "ID:" + +msgid "&Specify..." +msgstr "&Specificeer..." + +msgid "Sectors:" +msgstr "Sectoren:" + +msgid "Heads:" +msgstr "Heads:" + +msgid "Cylinders:" +msgstr "Cilinders:" + +msgid "Size (MB):" +msgstr "Grootte (MB):" + +msgid "Type:" +msgstr "Type:" + +msgid "Image Format:" +msgstr "Imageformaat:" + +msgid "Block Size:" +msgstr "Blokgrootte:" + +msgid "Floppy drives:" +msgstr "Floppy-schijfstations:" + +msgid "Turbo timings" +msgstr "Turbo timings" + +msgid "Check BPB" +msgstr "Controleer BPB" + +msgid "CD-ROM drives:" +msgstr "CD-ROM-stations:" + +msgid "MO drives:" +msgstr "MO-schijven:" + +msgid "ZIP drives:" +msgstr "ZIP-schijven:" + +msgid "ZIP 250" +msgstr "ZIP 250" + +msgid "ISA RTC:" +msgstr "ISA RTC:" + +msgid "ISA Memory Expansion" +msgstr "ISA-geheugenuitbreiding" + +msgid "Card 1:" +msgstr "Kaart 1:" + +msgid "Card 2:" +msgstr "Kaart 2:" + +msgid "Card 3:" +msgstr "Kaart 3:" + +msgid "Card 4:" +msgstr "Kaart 4:" + +msgid "ISABugger device" +msgstr "ISABugger-apparaat" + +msgid "POST card" +msgstr "POST-kaart" + +msgid "86Box" +msgstr "86Box" + +msgid "Error" +msgstr "Fout" + +msgid "Fatal error" +msgstr "Fatale fout" + +msgid " - PAUSED" +msgstr " - GEPAUZEERD" + +msgid "Press Ctrl+Alt+PgDn to return to windowed mode." +msgstr "Druk op Ctrl+Alt+PgDn om terug te gaan naar de venstermodus." + +msgid "Speed" +msgstr "Snelheid" + +msgid "ZIP %03i %i (%s): %ls" +msgstr "ZIP %03i %i (%s): %ls" + +msgid "ZIP images" +msgstr "ZIP-images" + +msgid "86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory." +msgstr "86Box kon geen bruikbare ROM images vinden.\n\nDownload een ROM set en pak deze uit in de map \"roms\"." + +msgid "(empty)" +msgstr "(leeg)" + +msgid "All files" +msgstr "Alle bestanden" + +msgid "Turbo" +msgstr "Turbo" + +msgid "On" +msgstr "Aan" + +msgid "Off" +msgstr "Uit" + +msgid "All images" +msgstr "Alle schijfimages" + +msgid "Basic sector images" +msgstr "Basissectorimages" + +msgid "Surface images" +msgstr "Oppervlakte-images" + +msgid "Machine \"%hs\" is not available due to missing ROMs in the roms/machines directory. Switching to an available machine." +msgstr "Machine \"%hs\" is niet beschikbaar door ontbrekende ROMs in de map roms/machines. Overschakelen naar een beschikbare machine." + +msgid "Video card \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card." +msgstr "Videokaart \"%hs\" is niet beschikbaar door ontbrekende ROMs in de map roms/video. Overschakel over naar een beschikbare videokaart." + +msgid "Video card #2 \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card." +msgstr "Videokaart #2 \"%hs\" is niet beschikbaar door ontbrekende ROMs in de map roms/video. Overschakel over naar een beschikbare videokaart." + +msgid "Machine" +msgstr "Machine" + +msgid "Display" +msgstr "Scherm" + +msgid "Input devices" +msgstr "Invoerapparaten" + +msgid "Sound" +msgstr "Geluid" + +msgid "Network" +msgstr "Netwerk" + +msgid "Ports (COM & LPT)" +msgstr "Poorten (COM & LPT)" + +msgid "Storage controllers" +msgstr "Opslagcontrollers" + +msgid "Hard disks" +msgstr "Harde schijven" + +msgid "Floppy & CD-ROM drives" +msgstr "Floppy- en CD-ROM-stations" + +msgid "Other removable devices" +msgstr "Andere verwijderbare apparaten" + +msgid "Other peripherals" +msgstr "Andere randapparatuur" + +msgid "Click to capture mouse" +msgstr "Klik om muis vast te leggen" + +msgid "Press %1 to release mouse" +msgstr "Druk op %1 om de muis los te laten" + +msgid "Press %1 or middle button to release mouse" +msgstr "Druk op %1 of middelste knop om de muis los te laten" + +msgid "Bus" +msgstr "Bus" + +msgid "File" +msgstr "Bestand" + +msgid "C" +msgstr "C" + +msgid "H" +msgstr "H" + +msgid "S" +msgstr "S" + +msgid "KB" +msgstr "KB" + +msgid "Could not initialize the video renderer." +msgstr "Kan de videorenderer niet initialiseren." + +msgid "Default" +msgstr "Standaard" + +msgid "%i estat(s) d'espera" +msgstr "%i estat(s) d'espera" + +msgid "Type" +msgstr "Type" + +msgid "No PCap devices found" +msgstr "Geen PCap-apparaten gevonden" + +msgid "Invalid PCap device" +msgstr "Ongeldig PCap-apparaat" + +msgid "2-axis, 2-button joystick(s)" +msgstr "Joystick(s) met 2 assen en 2 knoppen" + +msgid "2-axis, 4-button joystick" +msgstr "Joystick met 2 assen en 4 knoppen" + +msgid "2-axis, 6-button joystick" +msgstr "Joystick met 2 assen en 6 knoppen" + +msgid "2-axis, 8-button joystick" +msgstr "Joystick met 2 assen en 8 knoppen" + +msgid "3-axis, 2-button joystick" +msgstr "Joystick met 3 assen en 2 knoppen" + +msgid "3-axis, 4-button joystick" +msgstr "Joystick met 3 assen en 4 knoppen" + +msgid "4-axis, 4-button joystick" +msgstr "Joystick met 4 assen en 4 knoppen" + +msgid "CH Flightstick Pro" +msgstr "CH Flightstick Pro" + +msgid "Microsoft SideWinder Pad" +msgstr "Microsoft SideWinder Pad" + +msgid "Thrustmaster Flight Control System" +msgstr "Thrustmaster Flight Control systeem" + +msgid "None" +msgstr "Geen" + +msgid "%u MB (CHS: %i, %i, %i)" +msgstr "%u MB (CHS: %i, %i, %i)" + +msgid "Floppy %i (%s): %ls" +msgstr "Floppy %i (%s): %ls" + +msgid "Advanced sector images" +msgstr "Geavanceerde sector-images" + +msgid "Flux images" +msgstr "Flux images" + +msgid "Are you sure you want to hard reset the emulated machine?" +msgstr "Weet je zeker dat je de geëmuleerde machine wilt resetten?" + +msgid "Are you sure you want to exit 86Box?" +msgstr "Weet je zeker dat je 86Box wilt verlaten?" + +msgid "Unable to initialize Ghostscript" +msgstr "Kan Ghostscript niet initialiseren" + +msgid "Unable to initialize GhostPCL" +msgstr "Kan GhostPCL niet initialiseren" + +msgid "MO %i (%ls): %ls" +msgstr "MO %i (%ls): %ls" + +msgid "MO images" +msgstr "MO-images" + +msgid "Welcome to 86Box!" +msgstr "Welkom bij 86Box!" + +msgid "Internal device" +msgstr "Intern apparaat" + +msgid "Exit" +msgstr "&Afsluiten" + +msgid "No ROMs found" +msgstr "Geen ROMs gevonden" + +msgid "Do you want to save the settings?" +msgstr "Wil je de instellingen opslaan?" + +msgid "This will hard reset the emulated machine." +msgstr "Dit zal de geëmuleerde machine een hard reset geven." + +msgid "Save" +msgstr "Opslaan" + +msgid "About 86Box" +msgstr "Over 86Box" + +msgid "86Box v" +msgstr "86Box v" + +msgid "An emulator of old computers\n\nAuthors: Miran Grča (OBattler), RichardG867, Jasmine Iwanek, TC1995, coldbrewed, Teemu Korhonen (Manaatti), Joakim L. Gilje, Adrien Moulin (elyosh), Daniel Balsom (gloriouscow), Cacodemon345, Fred N. van Kempen (waltje), Tiseno100, reenigne, and others.\n\nWith previous core contributions from Sarah Walker, leilei, JohnElliott, greatpsycho, and others.\n\nReleased under the GNU General Public License version 2 or later. See LICENSE for more information." +msgstr "Een emulator van oude computers\n\nAuteurs: Miran Grča (OBattler), RichardG867, Jasmine Iwanek, TC1995, coldbrewed, Teemu Korhonen (Manaatti), Joakim L. Gilje, Adrien Moulin (elyosh), Daniel Balsom (gloriouscow), Cacodemon345, Fred N. van Kempen (waltje), Tiseno100, reenigne, and others.\n\nMet eerdere bijdragen van Sarah Walker, leilei, JohnElliott, greatpsycho en anderen.\n\nUitgebracht onder de GNU General Public License versie 2 of later. Zie LICENSE voor meer informatie." + +msgid "Hardware not available" +msgstr "Hardware niet beschikbaar" + +msgid "Make sure %1 is installed and that you are on a %1-compatible network connection." +msgstr "Zorg ervoor dat %1 is geïnstalleerd en dat je een %1-compatibele netwerkverbinding hebt." + +msgid "Invalid configuration" +msgstr "Ongeldige configuratie" + +msgid "%1 is required for automatic conversion of PostScript files to PDF.\n\nAny documents sent to the generic PostScript printer will be saved as PostScript (.ps) files." +msgstr "%1 is vereist voor automatische conversie van PostScript-bestanden naar PDF.\n\nAlle documenten die naar de generieke PostScript-printer worden gestuurd, worden opgeslagen als PostScript-bestanden (.ps)." + +msgid "%1 is required for automatic conversion of PCL files to PDF.\n\nAny documents sent to the generic PCL printer will be saved as Printer Command Language (.pcl) files." +msgstr "%1 is vereist voor automatische conversie van PCL-bestanden naar PDF.\n\nAlle documenten die naar de generieke PCL-printer worden gestuurd, worden opgeslagen als Printer Command Language (.pcl) bestanden." + +msgid "Entering fullscreen mode" +msgstr "Volledig scherm modus openen" + +msgid "Don't show this message again" +msgstr "Dit bericht niet meer tonen" + +msgid "Don't exit" +msgstr "Niet afsluiten" + +msgid "Reset" +msgstr "Reset" + +msgid "Don't reset" +msgstr "Niet resetten" + +msgid "CD-ROM images" +msgstr "CD-ROM-images" + +msgid "%1 Device Configuration" +msgstr "%1 Apparaatconfiguratie" + +msgid "Monitor in sleep mode" +msgstr "Monitor in slaapstand" + +msgid "OpenGL Shaders" +msgstr "OpenGL Shaders" + +msgid "OpenGL options" +msgstr "OpenGL-opties" + +msgid "You are loading an unsupported configuration" +msgstr "U laadt een configuratie die niet wordt ondersteund" + +msgid "CPU type filtering based on selected machine is disabled for this emulated machine.\n\nThis makes it possible to choose a CPU that is otherwise incompatible with the selected machine. However, you may run into incompatibilities with the machine BIOS or other software.\n\nEnabling this setting is not officially supported and any bug reports filed may be closed as invalid." +msgstr "Filteren op CPU type voor de geselecteerde machine is niet mogelijk met de geselecteerde machine.\n\nDit maakt het mogelijk een CPU te kunnen kiezen die anders niet compatible zou zijn met de geselecteerde machine. Je kunt hiermee echter compatibiliteitsproblemen krijgen met de BIOS van de machine of met andere software.\n\nHet inschakelen van deze instelling wordt niet officieel ondersteund en bugrapporten die worden ingediend kunnen als ongeldig worden gesloten." + +msgid "Continue" +msgstr "Doorgaan" + +msgid "Cassette: %s" +msgstr "Cassette: %s" + +msgid "Cassette images" +msgstr "Cassette-images" + +msgid "Cartridge %i: %ls" +msgstr "Cartridge %i: %ls" + +msgid "Cartridge images" +msgstr "Cartridge-images" + +msgid "Error initializing renderer" +msgstr "Fout bij het initialiseren van renderer" + +msgid "OpenGL (3.0 Core) renderer could not be initialized. Use another renderer." +msgstr "OpenGL (3.0 Core) renderer kon niet worden geïnitialiseerd. Gebruik een andere renderer." + +msgid "Resume execution" +msgstr "Hervat executie" + +msgid "Pause execution" +msgstr "Pauze executie" + +msgid "Press Ctrl+Alt+Del" +msgstr "Druk op Ctrl+Alt+Del" + +msgid "Press Ctrl+Alt+Esc" +msgstr "Druk op Ctrl+Alt+Esc" + +msgid "Hard reset" +msgstr "Harde reset" + +msgid "ACPI shutdown" +msgstr "ACPI uitschakeling" + +msgid "Hard disk (%1)" +msgstr "Harde schijf (%1)" + +msgid "MFM/RLL or ESDI CD-ROM drives never existed" +msgstr "MFM/RLL of ESDI CD-ROM-stations hebben nooit bestaan" + +msgid "Custom..." +msgstr "Aangepast..." + +msgid "Custom (large)..." +msgstr "Aangepast (groot)..." + +msgid "Add New Hard Disk" +msgstr "Nieuwe harde schijf toevoegen" + +msgid "Add Existing Hard Disk" +msgstr "Bestaande harde schijf toevoegen" + +msgid "HDI disk images cannot be larger than 4 GB." +msgstr "HDI-schijfimages kunnen niet groter zijn dan 4 GB." + +msgid "Disk images cannot be larger than 127 GB." +msgstr "Schijfimages kunnen niet groter zijn dan 127 GB." + +msgid "Hard disk images" +msgstr "Harde schijf-image" + +msgid "Unable to read file" +msgstr "Kan bestand niet lezen" + +msgid "Unable to write file" +msgstr "Kan bestand niet schrijven" + +msgid "HDI or HDX images with a sector size other than 512 are not supported." +msgstr "HDI- of HDX-image met een andere sectorgrootte dan 512 worden niet ondersteund." + +msgid "Disk image file already exists" +msgstr "Schijfimagebestand bestaat al" + +msgid "Please specify a valid file name." +msgstr "Geef een geldige bestandsnaam op." + +msgid "Disk image created" +msgstr "Schijfimage gemaakt" + +msgid "Make sure the file exists and is readable." +msgstr "Controleer of het bestand bestaat en leesbaar is." + +msgid "Make sure the file is being saved to a writable directory." +msgstr "Zorg ervoor dat het bestand wordt opgeslagen in een schrijfbare map." + +msgid "Disk image too large" +msgstr "Schijfimage te groot" + +msgid "Remember to partition and format the newly-created drive." +msgstr "Vergeet niet om de nieuw aangemaakte schijf te partitioneren en te formatteren." + +msgid "The selected file will be overwritten. Are you sure you want to use it?" +msgstr "Het geselecteerde bestand wordt overschreven. Weet u zeker dat u het wilt gebruiken?" + +msgid "Unsupported disk image" +msgstr "Niet-ondersteunde schijfimage" + +msgid "Overwrite" +msgstr "Overschrijven" + +msgid "Don't overwrite" +msgstr "Niet overschrijven" + +msgid "Raw image" +msgstr "Ruw image" + +msgid "HDI image" +msgstr "HDI-image" + +msgid "HDX image" +msgstr "HDX-image" + +msgid "Fixed-size VHD" +msgstr "VHD met vaste grootte" + +msgid "Dynamic-size VHD" +msgstr "VHD met dynamisch grootte" + +msgid "Differencing VHD" +msgstr "Verschil-VHD" + +msgid "(N/A)" +msgstr "(N/A)" + +msgid "Raw image (.img)" +msgstr "Ruw image (.img)" + +msgid "HDI image (.hdi)" +msgstr "HDI-image (.hdi)" + +msgid "HDX image (.hdx)" +msgstr "HDX-image (.hdx)" + +msgid "Fixed-size VHD (.vhd)" +msgstr "VHD met vaste grootte (.vhd)" + +msgid "Dynamic-size VHD (.vhd)" +msgstr "VHD met dynamisch grootte (.vhd)" + +msgid "Differencing VHD (.vhd)" +msgstr "Verschil-VHD (.vhd)" + +msgid "Large blocks (2 MB)" +msgstr "Grote blokken (2 MB)" + +msgid "Small blocks (512 KB)" +msgstr "Kleine blokken (512 KB)" + +msgid "VHD files" +msgstr "VHD-bestanden" + +msgid "Select the parent VHD" +msgstr "Selecteer de bovenliggende VHD" + +msgid "This could mean that the parent image was modified after the differencing image was created.\n\nIt can also happen if the image files were moved or copied, or by a bug in the program that created this disk.\n\nDo you want to fix the timestamps?" +msgstr "Dit kan betekenen dat de bovenliggende image is gewijzigd nadat de verschil-image is gemaakt.\n\nDit kan ook gebeuren als de imagebestanden zijn verplaatst of gekopieerd, of door een fout in het programma waarmee deze schijf is gemaakt.\n\nWil je de tijdstempels herstellen?" + +msgid "Parent and child disk timestamps do not match" +msgstr "Bovenliggende en onderliggende schijf tijdstempels komen niet overeen" + +msgid "Could not fix VHD timestamp." +msgstr "Kan VHD tijdstempel niet herstellen." + +msgid "MFM/RLL" +msgstr "MFM/RLL" + +msgid "XTA" +msgstr "XTA" + +msgid "ESDI" +msgstr "ESDI" + +msgid "IDE" +msgstr "IDE" + +msgid "ATAPI" +msgstr "ATAPI" + +msgid "CD-ROM %i (%s): %s" +msgstr "CD-ROM %i (%s): %s" + +msgid "160 KB" +msgstr "160 KB" + +msgid "180 KB" +msgstr "180 KB" + +msgid "320 KB" +msgstr "320 KB" + +msgid "360 KB" +msgstr "360 KB" + +msgid "640 KB" +msgstr "640 KB" + +msgid "720 KB" +msgstr "720 KB" + +msgid "1.2 MB" +msgstr "1,2 MB" + +msgid "1.25 MB" +msgstr "1,25 MB" + +msgid "1.44 MB" +msgstr "1,44 MB" + +msgid "DMF (cluster 1024)" +msgstr "DMF (cluster 1024)" + +msgid "DMF (cluster 2048)" +msgstr "DMF (cluster 2048)" + +msgid "2.88 MB" +msgstr "2,88 MB" + +msgid "ZIP 100" +msgstr "ZIP 100" + +msgid "3.5\" 128 MB (ISO 10090)" +msgstr "3,5\" 128 MB (ISO 10090)" + +msgid "3.5\" 230 MB (ISO 13963)" +msgstr "3,5\" 230 MB (ISO 13963)" + +msgid "3.5\" 540 MB (ISO 15498)" +msgstr "3,5\" 540 MB (ISO 15498)" + +msgid "3.5\" 640 MB (ISO 15498)" +msgstr "3,5\" 640 MB (ISO 15498)" + +msgid "3.5\" 1.3 GB (GigaMO)" +msgstr "3,5\" 1,3 GB (GigaMO)" + +msgid "3.5\" 2.3 GB (GigaMO 2)" +msgstr "3,5\" 2,3 GB (GigaMO 2)" + +msgid "5.25\" 600 MB" +msgstr "5,25\" 600 MB" + +msgid "5.25\" 650 MB" +msgstr "5,25\" 650 MB" + +msgid "5.25\" 1 GB" +msgstr "5,25\" 1 GB" + +msgid "5.25\" 1.3 GB" +msgstr "5,25\" 1,3 GB" + +msgid "Perfect RPM" +msgstr "Perfecte RPM" + +msgid "1% below perfect RPM" +msgstr "1% onder perfecte RPM" + +msgid "1.5% below perfect RPM" +msgstr "1,5% onder perfecte RPM" + +msgid "2% below perfect RPM" +msgstr "2% onder perfecte RPM" + +msgid "(System Default)" +msgstr "(Systeemstandaard)" + +msgid "Failed to initialize network driver" +msgstr "Netwerkstuurprogramma niet geïnitialiseerd" + +msgid "The network configuration will be switched to the null driver" +msgstr "De netwerkconfiguratie wordt overgeschakeld naar de nul-driver" + +msgid "Mouse sensitivity:" +msgstr "Muisgevoeligheid:" + +msgid "Select media images from program working directory" +msgstr "Selecteer media-images uit de werkmap van het programma" + +msgid "PIT mode:" +msgstr "PIT-modus:" + +msgid "Auto" +msgstr "Auto" + +msgid "Slow" +msgstr "Langzaam" + +msgid "Fast" +msgstr "Snel" + +msgid "&Auto-pause on focus loss" +msgstr "&Autopauze bij focusverlies" + +msgid "WinBox is no longer supported" +msgstr "WinBox wordt niet langer ondersteund" + +msgid "Development of the WinBox manager stopped in 2022 due to a lack of maintainers. As we direct our efforts towards making 86Box even better, we have made the decision to no longer support WinBox as a manager.\n\nNo further updates will be provided through WinBox, and you may encounter incorrect behavior should you continue using it with newer versions of 86Box. Any bug reports related to WinBox behavior will be closed as invalid.\n\nGo to 86box.net for a list of other managers you can use." +msgstr "De ontwikkeling van de WinBox manager is gestopt in 2022 door een gebrek aan beheerders. Omdat we onze inspanningen richten op het nog beter maken van 86Box, hebben we de beslissing genomen om WinBox niet langer te ondersteunen als een manager.\n\nEr zullen geen verdere updates worden geleverd door WinBox. Je kunt onjuist gedrag tegenkomen als je het blijft gebruiken met nieuwere versies van 86Box. Alle bugrapporten gerelateerd aan het gedrag van WinBox zullen worden gesloten als ongeldig.\n\nGa naar 86box.net voor een lijst van andere managers die je kunt gebruiken." + +msgid "Generate" +msgstr "Genereren" + +msgid "Joystick configuration" +msgstr "Joystick configuratie " + +msgid "Device" +msgstr "Apparaat" + +msgid "%1 (X axis)" +msgstr "%1 (X-as)" + +msgid "%1 (Y axis)" +msgstr "%1 (Y-as)" + +msgid "MCA devices" +msgstr "MCA-apparaten" + +msgid "List of MCA devices:" +msgstr "Lijst van MCA-apparaten:" + +msgid "Tablet tool" +msgstr "Tablet-hulpmiddel" + +msgid "Qt (OpenGL &ES)" +msgstr "Qt (OpenGL &ES)" + +msgid "About Qt" +msgstr "Over Qt" + +msgid "MCA devices..." +msgstr "MCA-apparaten..." + +msgid "Show non-primary monitors" +msgstr "Toon niet-primaire monitors" + +msgid "Open screenshots folder..." +msgstr "Map met schermafbeeldingen openen..." + +msgid "Apply fullscreen stretch mode when maximized" +msgstr "Pas fullscreen stretchmodus toe wanneer gemaximaliseerd" + +msgid "Cursor/Puck" +msgstr "Cursor/Puck" + +msgid "Pen" +msgstr "Pen" + +msgid "Host CD/DVD Drive (%1:)" +msgstr "Host cd/dvd-station (%1:)" + +msgid "&Connected" +msgstr "&Verbonden" + +msgid "Clear image history" +msgstr "Imagegeschiedenis verwijderen" + +msgid "Create..." +msgstr "Creëer..." + +msgid "previous image" +msgstr "vorige image" + +msgid "Host CD/DVD Drive (%1)" +msgstr "Host CD/DVD-station (%1)" + +msgid "Unknown Bus" +msgstr "Onbekende bus" + +msgid "Null Driver" +msgstr "Null Driver" + +msgid "NIC %02i (%ls) %ls" +msgstr "NIC %02i (%ls) %ls" + +msgid "Error opening \"%1\": %2" +msgstr "Fout bij het openen van \"%1\": %2" + +msgid "Error compiling vertex shader in file \"%1\"" +msgstr "Fout bij het compileren van vertex shader in bestand \"%1\"" + +msgid "Error compiling fragment shader in file \"%1\"" +msgstr "Fout bij het compileren van fragment shader in bestand \"%1\"" + +msgid "Error linking shader program in file \"%1\"" +msgstr "Fout bij koppelen shaderprogramma in bestand \"%1\"" + +msgid "OpenGL 3.0 renderer options" +msgstr "OpenGL 3.0 renderer opties" + +msgid "Render behavior" +msgstr "Rendergedrag" + +msgid "Use target framerate:" +msgstr "Gebruik doelframerate:" + +msgid " fps" +msgstr " fps" + +msgid "VSync" +msgstr "VSync" + +msgid "<html><head/><body><p>Render each frame immediately, in sync with the emulated display.</p><p><span style=" font-style:italic;">This is the recommended option if the shaders in use don't utilize frametime for animated effects.</span></p></body></html>" +msgstr "<html><head/><body><p>Onmiddellijk elk frame synchroon met het geëmuleerde beeldscherm renderen.</p><p><span style=" font-style:italic;">Dit is de aanbevolen optie als de gebruikte shaders geen frametime gebruiken voor geanimeerde effecten.</span></p></body></html>" + +msgid "Synchronize with video" +msgstr "Synchroniseren met video" + +msgid "Shaders" +msgstr "Shaders" + +msgid "Remove" +msgstr "Verwijderen" + +msgid "No shader selected" +msgstr "Geen shader geselecteerd" + +msgid "Browse..." +msgstr "Bladeren..." + +msgid "Shader error" +msgstr "Shader-fout" + +msgid "Could not load shaders." +msgstr "Kan shaders niet laden." + +msgid "More information in details." +msgstr "Meer informatie in details." + +msgid "Couldn't create OpenGL context." +msgstr "Kan OpenGL context niet maken." + +msgid "Couldn't switch to OpenGL context." +msgstr "Kan niet overschakelen naar OpenGL context." + +msgid "OpenGL version 3.0 or greater is required. Current version is %1.%2" +msgstr "OpenGL versie 3.0 of hoger is vereist. De huidige versie is %1.%2" + +msgid "OpenGL initialization failed. Error %1." +msgstr "OpenGL initialisatie mislukt. Fout %1." + +msgid "Error initializing OpenGL" +msgstr "Fout bij het initialiseren van OpenGL" + +msgid "Falling back to software rendering.\n" +msgstr "Terugvallen op software rendering.\n" + +msgid "Allocating memory for unpack buffer failed.\n" +msgstr "Toewijzen van geheugen voor uitpakbuffer mislukt.\n" + +msgid "<html><head/><body><p>When selecting media images (CD-ROM, floppy, etc.) the open dialog will start in the same directory as the 86Box configuration file. This setting will likely only make a difference on macOS.</p></body></html>" +msgstr "<html><head/><body><p>Bij het selecteren van media-images (CD-ROM, floppy, etc.) zal de \"open dialoog\" starten in dezelfde map als het 86Box configuratiebestand. Deze instelling is doet er waarschijnlijk alleen toe op macOS.</p></body></html>" + +msgid "This machine might have been moved or copied." +msgstr "Deze machine is misschien verplaatst of gekopieerd." + +msgid "In order to ensure proper networking functionality, 86Box needs to know if this machine was moved or copied.\n\nSelect \"I Copied It\" if you are not sure." +msgstr "Om een goede netwerkfunctionaliteit te garanderen, moet 86Box weten of deze machine verplaatst of gekopieerd is.\n\nSelecteer \"Ik heb het gekopieerd\" als u het niet zeker weet." + +msgid "I Moved It" +msgstr "Ik heb het verplaatst" + +msgid "I Copied It" +msgstr "Ik heb het gekopieerd" + +msgid "86Box Monitor #" +msgstr "86Box Monitor #" + +msgid "No MCA devices." +msgstr "Geen MCA-apparaten." + +msgid "MiB" +msgstr "MiB" + +msgid "Network Card #1" +msgstr "Netwerkkaart #1" + +msgid "Network Card #2" +msgstr "Netwerkkaart #2" + +msgid "Network Card #3" +msgstr "Netwerkkaart #3" + +msgid "Network Card #4" +msgstr "Netwerkkaart #4" + +msgid "Mode" +msgstr "Modus" + +msgid "Interface" +msgstr "Interface" + +msgid "Adapter" +msgstr "Adapter" + +msgid "VDE Socket" +msgstr "VDE-socket" + +msgid "86Box Unit Tester" +msgstr "86Box apparaattester" + +msgid "Novell NetWare 2.x Key Card" +msgstr "Novell NetWare 2.x Key Card" + +msgid "Serial port passthrough 1" +msgstr "Seriële poort doorvoer 1" + +msgid "Serial port passthrough 2" +msgstr "Seriële poort doorvoer 2" + +msgid "Serial port passthrough 3" +msgstr "Seriële poort doorvoer 3" + +msgid "Serial port passthrough 4" +msgstr "Seriële poort doorvoer 4" + +msgid "Vision Systems LBA Enhancer" +msgstr "Vision Systems LBA Enhancer" + +msgid "Renderer options..." +msgstr "Renderer-opties..." + +msgid "Logitech/Microsoft Bus Mouse" +msgstr "Logitech/Microsoft busmuis" + +msgid "Microsoft Bus Mouse (InPort)" +msgstr "Microsoft busmuis (InPort)" + +msgid "Mouse Systems Serial Mouse" +msgstr "Mouse Systems seriële muis" + +msgid "Microsoft Serial Mouse" +msgstr "Microsoft seriële muis" + +msgid "Logitech Serial Mouse" +msgstr "Logitech seriële muis" + +msgid "PS/2 Mouse" +msgstr "PS/2-muis" + +msgid "3M MicroTouch (Serial)" +msgstr "3M MicroTouch (serieel)" + +msgid "[COM] Standard Hayes-compliant Modem" +msgstr "COM] Standaard Hayes-compatibele modem " + +msgid "Roland MT-32 Emulation" +msgstr "Roland MT-32-emulatie" + +msgid "Roland MT-32 (New) Emulation" +msgstr "Roland MT-32 (nieuwe) emulatie" + +msgid "Roland CM-32L Emulation" +msgstr "Roland CM-32L-emulatie" + +msgid "Roland CM-32LN Emulation" +msgstr "Roland CM-32LN-emulatie" + +msgid "OPL4-ML Daughterboard" +msgstr "OPL4-ML-dochterbord" + +msgid "System MIDI" +msgstr "Systeem MIDI" + +msgid "MIDI Input Device" +msgstr "MIDI-ingangsapparaat" + +msgid "BIOS Address" +msgstr "BIOS-adres" + +msgid "Enable BIOS extension ROM Writes" +msgstr "BIOS-extensie ROM Writes inschakelen" + +msgid "Address" +msgstr "Adres" + +msgid "IRQ" +msgstr "IRQ" + +msgid "BIOS Revision" +msgstr "BIOS Revisie" + +msgid "Translate 26 -> 17" +msgstr "Vertaal 26 -> 17" + +msgid "Language" +msgstr "Taal" + +msgid "Enable backlight" +msgstr "Backlight inschakelen" + +msgid "Invert colors" +msgstr "Kleuren omkeren" + +msgid "BIOS size" +msgstr "BIOS-grootte" + +msgid "Map C0000-C7FFF as UMB" +msgstr "Geheugenadres C0000-C7FFF toewijzen aan UMB" + +msgid "Map C8000-CFFFF as UMB" +msgstr "Geheugenadres C8000-CFFFF toewijzen aan UMB" + +msgid "Map D0000-D7FFF as UMB" +msgstr "Geheugenadres D0000-D7FFF toewijzen aan UMB" + +msgid "Map D8000-DFFFF as UMB" +msgstr "Geheugenadres D8000-DFFFF toewijzen aan UMB" + +msgid "Map E0000-E7FFF as UMB" +msgstr "Geheugenadres E0000-E7FFF toewijzen aan UMB" + +msgid "Map E8000-EFFFF as UMB" +msgstr "Geheugenadres E8000-EFFFF toewijzen aan UMB" + +msgid "JS9 Jumper (JIM)" +msgstr "JS9 Jumper (JIM)" + +msgid "MIDI Output Device" +msgstr "MIDI-uitgangsapparaat" + +msgid "MIDI Real time" +msgstr "MIDI real-time" + +msgid "MIDI Thru" +msgstr "MIDI doorvoer" + +msgid "MIDI Clockout" +msgstr "MIDI Clockout" + +msgid "SoundFont" +msgstr "SoundFont" + +msgid "Output Gain" +msgstr "Output Gain" + +msgid "Chorus" +msgstr "Chorus" + +msgid "Chorus Voices" +msgstr "Chorus-stemmen" + +msgid "Chorus Level" +msgstr "Chorus-niveau" + +msgid "Chorus Speed" +msgstr "Chorus-snelheid" + +msgid "Chorus Depth" +msgstr "Chorus-diepte" + +msgid "Chorus Waveform" +msgstr "Chorus-golfvorm" + +msgid "Reverb" +msgstr "Reverb" + +msgid "Reverb Room Size" +msgstr "Reverbkamer afmetingen" + +msgid "Reverb Damping" +msgstr "Reverbdemping" + +msgid "Reverb Width" +msgstr "Reverbbreedte" + +msgid "Reverb Level" +msgstr "Reverbniveau" + +msgid "Interpolation Method" +msgstr "Interpolatiemethode" + +msgid "Reverb Output Gain" +msgstr "Reverbuitgang Versterking" + +msgid "Reversed stereo" +msgstr "Omgekeerde stereo" + +msgid "Nice ramp" +msgstr "Mooie helling" + +msgid "Hz" +msgstr "Hz" + +msgid "Buttons" +msgstr "Knoppen" + +msgid "Serial Port" +msgstr "Seriële poort" + +msgid "RTS toggle" +msgstr "RTS toggle" + +msgid "Revision" +msgstr "Revisie" + +msgid "Controller" +msgstr "Controller" + +msgid "Show Crosshair" +msgstr "Toon dradenkruis" + +msgid "DMA" +msgstr "DMA" + +msgid "MAC Address" +msgstr "MAC-adres" + +msgid "MAC Address OUI" +msgstr "MAC-adres OUI" + +msgid "Enable BIOS" +msgstr "BIOS inschakelen" + +msgid "Baud Rate" +msgstr "Baud-snelheid" + +msgid "TCP/IP listening port" +msgstr "TCP/IP-luisterpoort" + +msgid "Phonebook File" +msgstr "Telefoonboekbestand" + +msgid "Telnet emulation" +msgstr "Telnet-emulatie" + +msgid "RAM Address" +msgstr "RAM-adres" + +msgid "RAM size" +msgstr "RAM-grootte" + +msgid "Initial RAM size" +msgstr "Oorspronkelijke RAM-grootte" + +msgid "Serial Number" +msgstr "Serienummer" + +msgid "Host ID" +msgstr "Host-ID" + +msgid "FDC Address" +msgstr "FDC Adres" + +msgid "MPU-401 Address" +msgstr "MPU-401 Adres" + +msgid "MPU-401 IRQ" +msgstr "MPU-401 IRQ" + +msgid "Receive MIDI input" +msgstr "MIDI-ingang ontvangen" + +msgid "Low DMA" +msgstr "Lage DMA" + +msgid "Enable Game port" +msgstr "Game-poort inschakelen" + +msgid "Surround module" +msgstr "Surroundmodule" + +msgid "CODEC" +msgstr "Codec" + +msgid "Raise CODEC interrupt on CODEC setup (needed by some drivers)" +msgstr "Verhoog CODEC interrupt bij CODEC setup (nodig voor sommige stuurprogramma's)" + +msgid "SB Address" +msgstr "SB-adres" + +msgid "WSS IRQ" +msgstr "WSS IRQ" + +msgid "WSS DMA" +msgstr "WSS DMA" + +msgid "Enable OPL" +msgstr "OPL inschakelen" + +msgid "Receive MIDI input (MPU-401)" +msgstr "MIDI-ingang ontvangen (MPU-401)" + +msgid "SB low DMA" +msgstr "SB lage DMA" + +msgid "6CH variant (6-channel)" +msgstr "6CH-variant (6-kanaals)" + +msgid "Enable CMS" +msgstr "CMS inschakelen" + +msgid "Mixer" +msgstr "Mixer" + +msgid "High DMA" +msgstr "Hoge DMA" + +msgid "Control PC speaker" +msgstr "Bestuur pc-luidspreker" + +msgid "Memory size" +msgstr "Geheugengrootte" + +msgid "EMU8000 Address" +msgstr "EMU8000 Adres" + +msgid "IDE Controller" +msgstr "IDE-controller" + +msgid "Codec" +msgstr "Codec" + +msgid "GUS type" +msgstr "GUS-type" + +msgid "Enable 0x04 \"Exit 86Box\" command" +msgstr "Schakel het commando 0x04 \"Exit 86Box\" in" + +msgid "Display type" +msgstr "Scherm type" + +msgid "Composite type" +msgstr "Composite type" + +msgid "RGB type" +msgstr "RGB-type" + +msgid "Line doubling type" +msgstr "Type lijnverdubbeling" + +msgid "Snow emulation" +msgstr "Sneeuwemulatie" + +msgid "Monitor type" +msgstr "Type monitor" + +msgid "Character set" +msgstr "Tekenset" + +msgid "XGA type" +msgstr "XGA-type" + +msgid "Instance" +msgstr "Instantie" + +msgid "MMIO Address" +msgstr "MMIO-adres" + +msgid "RAMDAC type" +msgstr "RAMDAC type" + +msgid "Blend" +msgstr "Mengen" + +msgid "Bilinear filtering" +msgstr "Bilineaire filtering" + +msgid "Dithering" +msgstr "Dithering" + +msgid "Enable NMI for CGA emulation" +msgstr "NMI inschakelen voor CGA-emulatie" + +msgid "Voodoo type" +msgstr "Voodoo-type" + +msgid "Framebuffer memory size" +msgstr "Framebuffer geheugengrootte" + +msgid "Texture memory size" +msgstr "Grootte textuurgeheugen" + +msgid "Dither subtraction" +msgstr "Dither aftrekken" + +msgid "Screen Filter" +msgstr "Schermfilter" + +msgid "Render threads" +msgstr "Render threads" + +msgid "SLI" +msgstr "SLI" + +msgid "Start Address" +msgstr "Startadres" + +msgid "Contiguous Size" +msgstr "Aaneengesloten grootte" + +msgid "I/O Width" +msgstr "I/O-breedte" + +msgid "Transfer Speed" +msgstr "Overdrachtssnelheid" + +msgid "EMS mode" +msgstr "EMS-modus" + +msgid "Address for > 2 MB" +msgstr "Adres voor > 2 MB" + +msgid "Frame Address" +msgstr "Frameadres" + +msgid "USA" +msgstr "USA" + +msgid "Danish" +msgstr "Deens" + +msgid "Always at selected speed" +msgstr "Altijd op geselecteerde snelheid" + +msgid "BIOS setting + Hotkeys (off during POST)" +msgstr "BIOS-instelling + Sneltoetsen (niet actief tijdens POST)" + +msgid "64 kB starting from F0000" +msgstr "64 kB vanaf F0000" + +msgid "128 kB starting from E0000 (address MSB inverted, last 64KB first)" +msgstr "128 kB vanaf E0000 (geïnverteerd MSB adres, laatste 64KB eerst)" + +msgid "Sine" +msgstr "Sinus" + +msgid "Triangle" +msgstr "Driehoek" + +msgid "Linear" +msgstr "Lineair" + +msgid "4th Order" +msgstr "4e Orde" + +msgid "7th Order" +msgstr "7e Orde" + +msgid "Non-timed (original)" +msgstr "Niet-getimed (origineel)" + +msgid "45 Hz (JMP2 not populated)" +msgstr "45 Hz (JMP2 niet gezet)" + +msgid "Two" +msgstr "Twee" + +msgid "Three" +msgstr "Drie" + +msgid "Wheel" +msgstr "Wiel" + +msgid "Five + Wheel" +msgstr "Vijf + Wiel" + +msgid "A3 - SMT2 Serial / SMT3(R)V" +msgstr "A3 - SMT2 Serieel / SMT3(R)V" + +msgid "Q1 - SMT3(R) Serial" +msgstr "Q1 - SMT3(R) Serieel" + +msgid "8 KB" +msgstr "8 KB" + +msgid "32 KB" +msgstr "32 KB" + +msgid "16 KB" +msgstr "16 KB" + +msgid "64 KB" +msgstr "64 KB" + +msgid "Disable BIOS" +msgstr "BIOS uitschakelen" + +msgid "512 KB" +msgstr "512 KB" + +msgid "2 MB" +msgstr "2 MB" + +msgid "8 MB" +msgstr "8 MB" + +msgid "28 MB" +msgstr "28 MB" + +msgid "1 MB" +msgstr "1 MB" + +msgid "4 MB" +msgstr "4 MB" + +msgid "12 MB" +msgstr "12 MB" + +msgid "16 MB" +msgstr "16 MB" + +msgid "20 MB" +msgstr "20 MB" + +msgid "24 MB" +msgstr "24 MB" + +msgid "SigmaTel STAC9721T (stereo)" +msgstr "SigmaTel STAC9721T (stereo)" + +msgid "Classic" +msgstr "Klassiek" + +msgid "256 KB" +msgstr "256 KB" + +msgid "Composite" +msgstr "Composite" + +msgid "Old" +msgstr "Oud" + +msgid "New" +msgstr "Nieuw" + +msgid "Color (generic)" +msgstr "Kleur (algemeen)" + +msgid "Green Monochrome" +msgstr "Groen monochroom" + +msgid "Amber Monochrome" +msgstr "Amber Monochroom" + +msgid "Gray Monochrome" +msgstr "Grijs monochroom" + +msgid "Color (no brown)" +msgstr "Kleur (geen bruin)" + +msgid "Color (IBM 5153)" +msgstr "Kleur (IBM 5153)" + +msgid "Simple doubling" +msgstr "Eenvoudige verdubbeling" + +msgid "sRGB interpolation" +msgstr "sRGB-interpolatie" + +msgid "Linear interpolation" +msgstr "Lineaire interpolatie" + +msgid "128 KB" +msgstr "128 KB" + +msgid "Monochrome (5151/MDA) (white)" +msgstr "Monochroom (5151/MDA) (wit)" + +msgid "Monochrome (5151/MDA) (green)" +msgstr "Monochroom (5151/MDA) (groen)" + +msgid "Monochrome (5151/MDA) (amber)" +msgstr "Monochroom (5151/MDA) (amber)" + +msgid "Color 40x25 (5153/CGA)" +msgstr "Kleur 40x25 (5153/CGA)" + +msgid "Color 80x25 (5153/CGA)" +msgstr "Kleur 80x25 (5153/CGA)" + +msgid "Enhanced Color - Normal Mode (5154/ECD)" +msgstr "Verbeterde kleur - normale modus (5154/ECD)" + +msgid "Enhanced Color - Enhanced Mode (5154/ECD)" +msgstr "Verbeterde kleur - Verbeterde modus (5154/ECD)" + +msgid "Green" +msgstr "Groen" + +msgid "Amber" +msgstr "Amber" + +msgid "Gray" +msgstr "Grijs" + +msgid "Color" +msgstr "Kleur" + +msgid "U.S. English" +msgstr "Amerikaans Engels" + +msgid "Scandinavian" +msgstr "Scandinavisch" + +msgid "Other languages" +msgstr "Andere talen" + +msgid "Bochs latest" +msgstr "Bochs nieuwste" + +msgid "Mono Non-Interlaced" +msgstr "Mono Non-Interlaced" + +msgid "Color Interlaced" +msgstr "Kleur interlaced" + +msgid "Color Non-Interlaced" +msgstr "Kleur non-interlaced" + +msgid "3Dfx Voodoo Graphics" +msgstr "3Dfx Voodoo Graphics" + +msgid "Obsidian SB50 + Amethyst (2 TMUs)" +msgstr "Obsidian SB50 + Amethyst (2 TMU's)" + +msgid "8-bit" +msgstr "8-bit" + +msgid "16-bit" +msgstr "16-bit" + +msgid "Standard (150ns)" +msgstr "Standaard (150ns)" + +msgid "High-Speed (120ns)" +msgstr "Hoge snelheid (120ns)" + +msgid "Enabled" +msgstr "Ingeschakeld" + +msgid "Standard" +msgstr "Standaard" + +msgid "High-Speed" +msgstr "Hoge snelheid" + +msgid "Stereo LPT DAC" +msgstr "Stereo LPT DAC" + +msgid "Generic Text Printer" +msgstr "Generieke tekstprinter" + +msgid "Generic ESC/P Dot-Matrix Printer" +msgstr "Generieke ESC/P dot-matrix-printer" + +msgid "Generic PostScript Printer" +msgstr "Generieke PostScript-printer" + +msgid "Generic PCL5e Printer" +msgstr "Generieke PCL5e-printer" + +msgid "Parallel Line Internet Protocol" +msgstr "Internetprotocol voor parallelle lijnen" + +msgid "Protection Dongle for Savage Quest" +msgstr "Beschermingsdongle voor Savage Quest" + +msgid "Serial Passthrough Device" +msgstr "Serieel doorvoerapparaat" + +msgid "Passthrough Mode" +msgstr "Doorgeefmodus" + +msgid "Host Serial Device" +msgstr "Host Serieel Apparaat" + +msgid "Name of pipe" +msgstr "Naam van de pipe" + +msgid "Data bits" +msgstr "Databits" + +msgid "Stop bits" +msgstr "Stopbits" + +msgid "Baud Rate of Passthrough" +msgstr "Baud-snelheid van doorvoer" + +msgid "Named Pipe (Server)" +msgstr "Named Pipe (Server)" + +msgid "Host Serial Passthrough" +msgstr "Host seriële doorgave" + +msgid "Eject %s" +msgstr "Uitwerpen %s" + +msgid "&Unmute" +msgstr "&Geluid aanzetten" + +msgid "Softfloat FPU" +msgstr "Softfloat FPU" + +msgid "High performance impact" +msgstr "Hoge prestatie-impact" + +msgid "RAM Disk (max. speed)" +msgstr "RAM-schijf (max. snelheid)" + +msgid "IBM 8514/A clone (ISA)" +msgstr "IBM 8514/A-kloon (ISA)" + +msgid "Vendor" +msgstr "Verkoper" + +msgid "30 Hz (JMP2 = 1)" +msgstr "30 Hz (JMP2 = 1)" + +msgid "60 Hz (JMP2 = 2)" +msgstr "60 Hz (JMP2 = 2)" + +msgid "Generic PC/XT Memory Expansion" +msgstr "Generieke PC/XT geheugenuitbreiding" + +msgid "Generic PC/AT Memory Expansion" +msgstr "Generieke PC/AT geheugenuitbreiding" diff --git a/src/qt/languages/pt-BR.po b/src/qt/languages/pt-BR.po index fa3fc94c1..f4e31f6aa 100644 --- a/src/qt/languages/pt-BR.po +++ b/src/qt/languages/pt-BR.po @@ -799,7 +799,7 @@ msgid "Thrustmaster Flight Control System" msgstr "Sistema de Controle de Voo Thrustmaster" msgid "None" -msgstr "Nada" +msgstr "Nenhum" msgid "%u MB (CHS: %i, %i, %i)" msgstr "%u MB (CCS: %i, %i, %i)" diff --git a/src/qt/languages/ru-RU.po b/src/qt/languages/ru-RU.po index 2d3f6046e..8476090d1 100644 --- a/src/qt/languages/ru-RU.po +++ b/src/qt/languages/ru-RU.po @@ -1282,10 +1282,10 @@ msgid "Host CD/DVD Drive (%1)" msgstr "Главный CD/DVD-привод (%1)" msgid "Unknown Bus" -msgstr "Неизвестный автобус" +msgstr "Неизвестная шина" msgid "Null Driver" -msgstr "Нулевой водитель" +msgstr "Нулевой драйвер" msgid "NIC %02i (%ls) %ls" msgstr "NIC %02i (%ls) %ls" @@ -2126,3 +2126,9 @@ msgstr "30 Гц (JMP2 = 1)" msgid "60 Hz (JMP2 = 2)" msgstr "60 Гц (JMP2 = 2)" + +msgid "Generic PC/XT Memory Expansion" +msgstr "Стандартное расширение памяти PC/XT" + +msgid "Generic PC/AT Memory Expansion" +msgstr "Стандартное расширение памяти PC/AT" diff --git a/src/qt/languages/zh-CN.po b/src/qt/languages/zh-CN.po index 95e770357..6b659b504 100644 --- a/src/qt/languages/zh-CN.po +++ b/src/qt/languages/zh-CN.po @@ -1372,13 +1372,13 @@ msgid "This machine might have been moved or copied." msgstr "这台机器可能被移动或复制过。" msgid "In order to ensure proper networking functionality, 86Box needs to know if this machine was moved or copied.\n\nSelect \"I Copied It\" if you are not sure." -msgstr "为了确保网络功能正常,86Box需要知道这台机器是否被移动或复制。\n\n如果您不确定,请选择\"我复制了它\"。" +msgstr "为了确保网络功能正常,86Box需要知道这台机器是否被移动或复制。\n\n如果您不确定,请选择\"我已复制这台机器\"。" msgid "I Moved It" -msgstr "我移动了它" +msgstr "我已移动这台机器" msgid "I Copied It" -msgstr "我复制了它" +msgstr "我已复制这台机器" msgid "86Box Monitor #" msgstr "86Box 监测器 " diff --git a/src/qt/languages/zh-TW.po b/src/qt/languages/zh-TW.po index 2d5576573..f9e917d98 100644 --- a/src/qt/languages/zh-TW.po +++ b/src/qt/languages/zh-TW.po @@ -1372,13 +1372,13 @@ msgid "This machine might have been moved or copied." msgstr "這台機器可能已被移動或複製。" msgid "In order to ensure proper networking functionality, 86Box needs to know if this machine was moved or copied.\n\nSelect \"I Copied It\" if you are not sure." -msgstr "為了確保正常的網路功能,86Box 需要知道這台機器是否被移動或複製。\n\n如果您不確定,請選擇「我複製了它」。" +msgstr "為了確保正常的網路功能,86Box 需要知道這台機器是否被移動或複製。\n\n如果您不確定,請選擇「我已複製這台機器」。" msgid "I Moved It" -msgstr "我移動了它" +msgstr "我已移動這台機器" msgid "I Copied It" -msgstr "我複製了它" +msgstr "我已複製這台機器" msgid "86Box Monitor #" msgstr "86Box Monitor " diff --git a/src/qt/qt_platform.cpp b/src/qt/qt_platform.cpp index bbc9489b8..f8b512f78 100644 --- a/src/qt/qt_platform.cpp +++ b/src/qt/qt_platform.cpp @@ -460,6 +460,7 @@ QMap> ProgSettings::lcid_langcode = { { 0x0410, { "it-IT", "Italian (Italy)" } }, { 0x0411, { "ja-JP", "Japanese (Japan)" } }, { 0x0412, { "ko-KR", "Korean (Korea)" } }, + { 0x0413, { "nl-NL", "Dutch (Netherlands)" } }, { 0x0415, { "pl-PL", "Polish (Poland)" } }, { 0x0416, { "pt-BR", "Portuguese (Brazil)" } }, { 0x0816, { "pt-PT", "Portuguese (Portugal)" } }, diff --git a/src/qt/qt_settingsfloppycdrom.cpp b/src/qt/qt_settingsfloppycdrom.cpp index 190dbc2e1..db54315e4 100644 --- a/src/qt/qt_settingsfloppycdrom.cpp +++ b/src/qt/qt_settingsfloppycdrom.cpp @@ -220,7 +220,7 @@ SettingsFloppyCDROM::save() for (int i = 0; i < CDROM_NUM; i++) { cdrom[i].priv = NULL; cdrom[i].ops = NULL; - cdrom[i].image = NULL; + cdrom[i].local = NULL; cdrom[i].insert = NULL; cdrom[i].close = NULL; cdrom[i].get_volume = NULL; diff --git a/src/qt/qt_settingsharddisks.cpp b/src/qt/qt_settingsharddisks.cpp index 3f02ffe54..4f6811ff1 100644 --- a/src/qt/qt_settingsharddisks.cpp +++ b/src/qt/qt_settingsharddisks.cpp @@ -99,7 +99,7 @@ addRow(QAbstractItemModel *model, hard_disk_t *hd) model->setData(model->index(row, ColumnHeads), hd->hpc); model->setData(model->index(row, ColumnSectors), hd->spt); model->setData(model->index(row, ColumnSize), (hd->tracks * hd->hpc * hd->spt) >> 11); - model->setData(model->index(row, ColumnSpeed), hdd_preset_getname(hd->speed_preset)); + model->setData(model->index(row, ColumnSpeed), QObject::tr(hdd_preset_getname(hd->speed_preset))); model->setData(model->index(row, ColumnSpeed), hd->speed_preset, Qt::UserRole); } @@ -267,7 +267,7 @@ SettingsHarddisks::on_comboBoxSpeed_currentIndexChanged(int index) auto *model = ui->tableView->model(); auto col = idx.siblingAtColumn(ColumnSpeed); model->setData(col, ui->comboBoxSpeed->currentData(Qt::UserRole), Qt::UserRole); - model->setData(col, hdd_preset_getname(ui->comboBoxSpeed->currentData(Qt::UserRole).toUInt())); + model->setData(col, QObject::tr(hdd_preset_getname(ui->comboBoxSpeed->currentData(Qt::UserRole).toUInt()))); } } diff --git a/src/qt/qt_translations.qrc.in b/src/qt/qt_translations.qrc.in index 96f7291fc..655323563 100644 --- a/src/qt/qt_translations.qrc.in +++ b/src/qt/qt_translations.qrc.in @@ -13,6 +13,7 @@ 86box_it-IT.qm 86box_ja-JP.qm 86box_ko-KR.qm + 86box_nl-NL.qm 86box_pl-PL.qm 86box_pt-BR.qm 86box_pt-PT.qm diff --git a/src/qt/win_cdrom_ioctl.c b/src/qt/win_cdrom_ioctl.c index 995876c1d..ff65046a7 100644 --- a/src/qt/win_cdrom_ioctl.c +++ b/src/qt/win_cdrom_ioctl.c @@ -43,11 +43,16 @@ of the audio while audio still plays. With an absolute conversion, the counter is fine. */ #define MSFtoLBA(m, s, f) ((((m * 60) + s) * 75) + f) -static int toc_valid = 0; -static CDROM_TOC cur_toc = { 0 }; -static HANDLE handle = NULL; -static WCHAR ioctl_path[256] = { 0 }; -static WCHAR old_ioctl_path[256] = { 0 }; +typedef struct win_cdrom_ioctl_t { + int toc_valid; + uint8_t cur_toc[65536]; + CDROM_READ_TOC_EX cur_read_toc_ex; + int blocks_num; + uint8_t cur_rti[65536]; + HANDLE handle; + WCHAR path[256]; + WCHAR old_path[256]; +} win_cdrom_ioctl_t; #ifdef ENABLE_WIN_CDROM_IOCTL_LOG int win_cdrom_ioctl_do_log = ENABLE_WIN_CDROM_IOCTL_LOG; @@ -68,63 +73,144 @@ win_cdrom_ioctl_log(const char *fmt, ...) #endif static int -plat_cdrom_open(void) +plat_cdrom_open(void *local) { - plat_cdrom_close(); + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; - handle = CreateFileW((LPCWSTR)ioctl_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - win_cdrom_ioctl_log("handle=%p, error=%x\n", handle, (unsigned int) GetLastError()); + plat_cdrom_close(local); - return (handle != INVALID_HANDLE_VALUE); + ioctl->handle = CreateFileW((LPCWSTR) ioctl->path, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + win_cdrom_ioctl_log("handle=%p, error=%x\n", ioctl->handle, (unsigned int) GetLastError()); + + return (ioctl->handle != INVALID_HANDLE_VALUE); } static int -plat_cdrom_load(void) +plat_cdrom_load(void *local) { - plat_cdrom_close(); + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; - handle = CreateFileW((LPCWSTR)ioctl_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - win_cdrom_ioctl_log("handle=%p, error=%x\n", handle, (unsigned int) GetLastError()); - if (handle != INVALID_HANDLE_VALUE) { + plat_cdrom_close(local); + + ioctl->handle = CreateFileW((LPCWSTR) ioctl->path, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + win_cdrom_ioctl_log("handle=%p, error=%x\n", ioctl->handle, (unsigned int) GetLastError()); + if (ioctl->handle != INVALID_HANDLE_VALUE) { long size; - DeviceIoControl(handle, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, (LPDWORD)&size, NULL); + DeviceIoControl(ioctl->handle, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, (LPDWORD) &size, NULL); return 1; } return 0; } -static void -plat_cdrom_read_toc(void) +static int +plat_cdrom_read_normal_toc(win_cdrom_ioctl_t *ioctl, uint8_t *toc_buf) { - long size = 0; + long size = 0; - if (!toc_valid) { - toc_valid = 1; - plat_cdrom_open(); - DeviceIoControl(handle, IOCTL_CDROM_READ_TOC, NULL, 0, &cur_toc, sizeof(cur_toc), (LPDWORD)&size, NULL); - plat_cdrom_close(); + memset(toc_buf, 0x00, 65536); + plat_cdrom_open(ioctl); + int temp = DeviceIoControl(ioctl->handle, IOCTL_CDROM_READ_TOC, NULL, 0, toc_buf, 65535, (LPDWORD) &size, NULL); + plat_cdrom_close(ioctl); +#ifdef ENABLE_WIN_CDROM_IOCTL_LOG + win_cdrom_ioctl_log("temp = %i\n", temp); + + PCDROM_TOC toc = (PCDROM_TOC) toc_buf; + const int tracks_num = (((toc->Length[0] << 8) | toc->Length[1]) - 2) / 8; + win_cdrom_ioctl_log("%i tracks\n", tracks_num); + for (int i = 0; i < tracks_num; i++) + win_cdrom_ioctl_log("Track %03i: Point %02X\n", i, (int) toc->TrackData[i].TrackNumber); +#endif + + return temp; +} + +static void +plat_cdrom_read_raw_toc(win_cdrom_ioctl_t *ioctl) +{ + long size = 0; + int status; + PCDROM_TOC_FULL_TOC_DATA cur_full_toc = NULL; + + memset(ioctl->cur_rti, 0x00, 65536); + cur_full_toc = (PCDROM_TOC_FULL_TOC_DATA) calloc(1, 65536); + if (ioctl->blocks_num != 0) { + memset(ioctl->cur_rti, 0x00, ioctl->blocks_num * 11); + ioctl->blocks_num = 0; + } + + ioctl->cur_read_toc_ex.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; + win_cdrom_ioctl_log("cur_read_toc_ex.Format = %i\n", ioctl->cur_read_toc_ex.Format); + ioctl->cur_read_toc_ex.Msf = 1; + ioctl->cur_read_toc_ex.SessionTrack = 1; + + plat_cdrom_open(ioctl); + status = DeviceIoControl(ioctl->handle, IOCTL_CDROM_READ_TOC_EX, &ioctl->cur_read_toc_ex, 65535, + cur_full_toc, 65535, (LPDWORD) &size, NULL); + plat_cdrom_close(ioctl); + win_cdrom_ioctl_log("status = %i\n", status); + + if (status != 0) { + ioctl->blocks_num = (((cur_full_toc->Length[0] << 8) | cur_full_toc->Length[1]) - 2) / 11; + memcpy(ioctl->cur_rti, cur_full_toc->Descriptors, ioctl->blocks_num * 11); + } + + free(cur_full_toc); + +#ifdef ENABLE_WIN_CDROM_IOCTL_LOG + win_cdrom_ioctl_log("%i blocks\n", ioctl->blocks_num); + + raw_track_info_t *rti = (raw_track_info_t *) ioctl->cur_rti; + for (int i = 0; i < ioctl->blocks_num; i++) + win_cdrom_ioctl_log("Block %03i: Session %03i, Point %02X\n", i, (int) rti[i].session, (int) rti[i].point); +#endif +} + +void +plat_cdrom_get_raw_track_info(void *local, int *num, raw_track_info_t *rti) +{ + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + + *num = ioctl->blocks_num; + memcpy(rti, ioctl->cur_rti, ioctl->blocks_num * 11); +} + +static void +plat_cdrom_read_toc(win_cdrom_ioctl_t *ioctl) +{ + if (!ioctl->toc_valid) { + ioctl->toc_valid = 1; + (void) plat_cdrom_read_normal_toc(ioctl, ioctl->cur_toc); + plat_cdrom_read_raw_toc(ioctl); } } int -plat_cdrom_is_track_audio(uint32_t sector) +plat_cdrom_is_track_audio(void *local, uint32_t sector) { - int control = 0; - uint32_t track_addr = 0; - uint32_t next_track_addr = 0; + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + PCDROM_TOC toc = (PCDROM_TOC) ioctl->cur_toc; + int control = 0; + uint32_t cur_addr = 0; + uint32_t next_addr = 0; - plat_cdrom_read_toc(); + plat_cdrom_read_toc(ioctl); + + for (int c = 0; toc->TrackData[c].TrackNumber != 0xaa; c++) { + PTRACK_DATA cur_td = &toc->TrackData[c]; + PTRACK_DATA next_td = &toc->TrackData[c + 1]; + + cur_addr = MSFtoLBA(cur_td->Address[1], cur_td->Address[2], cur_td->Address[3]) - 150; + next_addr = MSFtoLBA(next_td->Address[1], next_td->Address[2], next_td->Address[3]) - 150; - for (int c = 0; cur_toc.TrackData[c].TrackNumber != 0xaa; c++) { - track_addr = MSFtoLBA(cur_toc.TrackData[c].Address[1], cur_toc.TrackData[c].Address[2], cur_toc.TrackData[c].Address[3]) - 150; - next_track_addr = MSFtoLBA(cur_toc.TrackData[c + 1].Address[1], cur_toc.TrackData[c + 1].Address[2], cur_toc.TrackData[c + 1].Address[3]) - 150; win_cdrom_ioctl_log("F: %i, L: %i, C: %i (%i), c: %02X, A: %08X, S: %08X\n", - cur_toc.FirstTrack, cur_toc.LastTrack, - cur_toc.TrackData[c].TrackNumber, c, - cur_toc.TrackData[c].Control, track_addr, sector); - if ((cur_toc.TrackData[c].TrackNumber >= cur_toc.FirstTrack) && (cur_toc.TrackData[c].TrackNumber <= cur_toc.LastTrack) && - (sector >= track_addr) && (sector < next_track_addr)) { - control = cur_toc.TrackData[c].Control; + toc->FirstTrack, toc->LastTrack, cur_td->TrackNumber, c, + cur_td->Control, track_addr, sector); + + if ((cur_td->TrackNumber >= toc->FirstTrack) && (cur_td->TrackNumber <= toc->LastTrack) && + (sector >= cur_addr) && (sector < next_addr)) { + control = cur_td->Control; break; } } @@ -137,26 +223,32 @@ plat_cdrom_is_track_audio(uint32_t sector) } int -plat_cdrom_is_track_pre(uint32_t sector) +plat_cdrom_is_track_pre(void *local, uint32_t sector) { - int control = 0; - uint32_t track_addr = 0; - uint32_t next_track_addr = 0; + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + PCDROM_TOC toc = (PCDROM_TOC) ioctl->cur_toc; + int control = 0; + uint32_t cur_addr = 0; + uint32_t next_addr = 0; - plat_cdrom_read_toc(); + plat_cdrom_read_toc(ioctl); + + for (int c = 0; toc->TrackData[c].TrackNumber != 0xaa; c++) { + PTRACK_DATA cur_td = &toc->TrackData[c]; + PTRACK_DATA next_td = &toc->TrackData[c + 1]; + + cur_addr = MSFtoLBA(cur_td->Address[1], cur_td->Address[2], cur_td->Address[3]) - 150; + next_addr = MSFtoLBA(next_td->Address[1], next_td->Address[2], next_td->Address[3]) - 150; - for (int c = 0; cur_toc.TrackData[c].TrackNumber != 0xaa; c++) { - track_addr = MSFtoLBA(cur_toc.TrackData[c].Address[1], cur_toc.TrackData[c].Address[2], cur_toc.TrackData[c].Address[3]) - 150; - next_track_addr = MSFtoLBA(cur_toc.TrackData[c + 1].Address[1], cur_toc.TrackData[c + 1].Address[2], cur_toc.TrackData[c + 1].Address[3]) - 150; win_cdrom_ioctl_log("F: %i, L: %i, C: %i (%i), c: %02X, A: %08X, S: %08X\n", - cur_toc.FirstTrack, cur_toc.LastTrack, - cur_toc.TrackData[c].TrackNumber, c, - cur_toc.TrackData[c].Control, track_addr, sector); - if ((cur_toc.TrackData[c].TrackNumber >= cur_toc.FirstTrack) && (cur_toc.TrackData[c].TrackNumber <= cur_toc.LastTrack) && - (sector >= track_addr) && (sector < next_track_addr)) { - control = cur_toc.TrackData[c].Control; + toc->FirstTrack, toc->LastTrack, cur_td->TrackNumber, c, + cur_td->Control, cur_addr, sector); + + if ((cur_td->TrackNumber >= toc->FirstTrack) && (cur_td->TrackNumber <= toc->LastTrack) && + (sector >= cur_addr) && (sector < next_addr)) { + control = cur_td->Control; break; - } + } } const int ret = (control & 0x01); @@ -167,81 +259,91 @@ plat_cdrom_is_track_pre(uint32_t sector) } uint32_t -plat_cdrom_get_track_start(uint32_t sector, uint8_t *attr, uint8_t *track) +plat_cdrom_get_track_start(void *local, uint32_t sector, uint8_t *attr, uint8_t *track) { - uint32_t track_addr = 0; - uint32_t next_track_addr = 0; + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + PCDROM_TOC toc = (PCDROM_TOC) ioctl->cur_toc; + uint32_t cur_addr = 0; + uint32_t next_addr = 0; - plat_cdrom_read_toc(); + plat_cdrom_read_toc(ioctl); + + for (int c = 0; toc->TrackData[c].TrackNumber != 0xaa; c++) { + PTRACK_DATA cur_td = &toc->TrackData[c]; + PTRACK_DATA next_td = &toc->TrackData[c + 1]; + + cur_addr = MSFtoLBA(cur_td->Address[1], cur_td->Address[2], cur_td->Address[3]) - 150; + next_addr = MSFtoLBA(next_td->Address[1], next_td->Address[2], next_td->Address[3]) - 150; - for (int c = 0; cur_toc.TrackData[c].TrackNumber != 0xaa; c++) { - track_addr = MSFtoLBA(cur_toc.TrackData[c].Address[1], cur_toc.TrackData[c].Address[2], cur_toc.TrackData[c].Address[3]) - 150; - next_track_addr = MSFtoLBA(cur_toc.TrackData[c + 1].Address[1], cur_toc.TrackData[c + 1].Address[2], cur_toc.TrackData[c + 1].Address[3]) - 150; win_cdrom_ioctl_log("F: %i, L: %i, C: %i (%i), c: %02X, a: %02X, A: %08X, S: %08X\n", - cur_toc.FirstTrack, cur_toc.LastTrack, - cur_toc.TrackData[c].TrackNumber, c, - cur_toc.TrackData[c].Control, cur_toc.TrackData[c].Adr, - track_addr, sector); - if ((cur_toc.TrackData[c].TrackNumber >= cur_toc.FirstTrack) && (cur_toc.TrackData[c].TrackNumber <= cur_toc.LastTrack) && - (sector >= track_addr) && (sector < next_track_addr)) { - *track = cur_toc.TrackData[c].TrackNumber; - *attr = cur_toc.TrackData[c].Control; - *attr |= ((cur_toc.TrackData[c].Adr << 4) & 0xf0); + toc->FirstTrack, toc->LastTrack, cur_td->TrackNumber, c, + cur_td->Control, cur_td->Adr, cur_addr, sector); + + if ((cur_td->TrackNumber >= toc->FirstTrack) && (cur_td->TrackNumber <= toc->LastTrack) && + (sector >= cur_addr) && (sector < next_addr)) { + *track = cur_td->TrackNumber; + *attr = cur_td->Control; + *attr |= ((cur_td->Adr << 4) & 0xf0); break; } } - win_cdrom_ioctl_log("plat_cdrom_get_track_start(%08X): %i\n", sector, track_addr); + win_cdrom_ioctl_log("plat_cdrom_get_track_start(%08X): %i\n", sector, cur_addr); - return track_addr; + return cur_addr; } uint32_t -plat_cdrom_get_last_block(void) +plat_cdrom_get_last_block(void *local) { - uint32_t lb = 0; - uint32_t address = 0; + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + PCDROM_TOC toc = (PCDROM_TOC) ioctl->cur_toc; + uint32_t lb = 0; + uint32_t address = 0; - plat_cdrom_read_toc(); + plat_cdrom_read_toc(ioctl); + + for (int c = 0; c <= toc->LastTrack; c++) { + PTRACK_DATA td = &toc->TrackData[c]; + + address = MSFtoLBA(td->Address[1], td->Address[2], td->Address[3]) - 150; - for (int c = 0; c <= cur_toc.LastTrack; c++) { - address = MSFtoLBA(cur_toc.TrackData[c].Address[1], cur_toc.TrackData[c].Address[2], cur_toc.TrackData[c].Address[3]) - 150; if (address > lb) lb = address; } + win_cdrom_ioctl_log("LBCapacity=%d\n", lb); + return lb; } int -plat_cdrom_ext_medium_changed(void) +plat_cdrom_ext_medium_changed(void *local) { - long size; - CDROM_TOC toc; - int ret = 0; + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + PCDROM_TOC toc = (PCDROM_TOC) ioctl->cur_toc; + uint8_t new_toc_buf[65536] = { 0 }; + PCDROM_TOC new_toc = (PCDROM_TOC) new_toc_buf; + int ret = 0; + int temp = plat_cdrom_read_normal_toc(ioctl, new_toc_buf); + PTRACK_DATA cur_ltd = &toc->TrackData[toc->LastTrack]; - plat_cdrom_open(); - int temp = DeviceIoControl(handle, IOCTL_CDROM_READ_TOC, - NULL, 0, &toc, sizeof(toc), - (LPDWORD)&size, NULL); - plat_cdrom_close(); + if (temp != 0) + plat_cdrom_read_raw_toc(ioctl); - if (!temp) + PTRACK_DATA new_ltd = &new_toc->TrackData[new_toc->LastTrack]; + + if (temp == 0) /* There has been some kind of error - not a medium change, but a not ready condition. */ ret = -1; - else if (!toc_valid || (memcmp(ioctl_path, old_ioctl_path, sizeof(ioctl_path)) != 0)) { + else if (!ioctl->toc_valid || (memcmp(ioctl->path, ioctl->old_path, sizeof(ioctl->path)) != 0)) { /* Changed to a different host drive - we already detect such medium changes. */ - toc_valid = 1; - cur_toc = toc; - if (memcmp(ioctl_path, old_ioctl_path, sizeof(ioctl_path)) != 0) - memcpy(old_ioctl_path, ioctl_path, sizeof(ioctl_path)); - } else if ((toc.TrackData[toc.LastTrack].Address[1] != - cur_toc.TrackData[cur_toc.LastTrack].Address[1]) || - (toc.TrackData[toc.LastTrack].Address[2] != - cur_toc.TrackData[cur_toc.LastTrack].Address[2]) || - (toc.TrackData[toc.LastTrack].Address[3] != - cur_toc.TrackData[cur_toc.LastTrack].Address[3])) + ioctl->toc_valid = 1; + memcpy(toc, new_toc, 65535); + if (memcmp(ioctl->path, ioctl->old_path, sizeof(ioctl->path)) != 0) + memcpy(ioctl->old_path, ioctl->path, sizeof(ioctl->path)); + } else if (memcmp(&(new_ltd->Address[1]), &(cur_ltd->Address[1]), 3)) /* The TOC has changed. */ ret = 1; @@ -251,15 +353,20 @@ plat_cdrom_ext_medium_changed(void) } void -plat_cdrom_get_audio_tracks(int *st_track, int *end, TMSF *lead_out) +plat_cdrom_get_audio_tracks(void *local, int *st_track, int *end, TMSF *lead_out) { - plat_cdrom_read_toc(); + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + PCDROM_TOC toc = (PCDROM_TOC) ioctl->cur_toc; + + plat_cdrom_read_toc(ioctl); + + PTRACK_DATA ltd = &toc->TrackData[toc->LastTrack]; *st_track = 1; - *end = cur_toc.LastTrack; - lead_out->min = cur_toc.TrackData[cur_toc.LastTrack].Address[1]; - lead_out->sec = cur_toc.TrackData[cur_toc.LastTrack].Address[2]; - lead_out->fr = cur_toc.TrackData[cur_toc.LastTrack].Address[3]; + *end = toc->LastTrack; + lead_out->min = ltd->Address[1]; + lead_out->sec = ltd->Address[2]; + lead_out->fr = ltd->Address[3]; win_cdrom_ioctl_log("plat_cdrom_get_audio_tracks(): %02i, %02i, %02i:%02i:%02i\n", *st_track, *end, lead_out->min, lead_out->sec, lead_out->fr); @@ -267,22 +374,27 @@ plat_cdrom_get_audio_tracks(int *st_track, int *end, TMSF *lead_out) /* This replaces both Info and EndInfo, they are specified by a variable. */ int -plat_cdrom_get_audio_track_info(UNUSED(int end), int track, int *track_num, TMSF *start, uint8_t *attr) +plat_cdrom_get_audio_track_info(void *local, UNUSED(int end), int track, int *track_num, TMSF *start, uint8_t *attr) { - plat_cdrom_read_toc(); + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + PCDROM_TOC toc = (PCDROM_TOC) ioctl->cur_toc; - if ((track < 1) || (track == 0xaa) || (track > (cur_toc.LastTrack + 1))) { + plat_cdrom_read_toc(ioctl); + + if ((track < 1) || (track == 0xaa) || (track > (toc->LastTrack + 1))) { win_cdrom_ioctl_log("plat_cdrom_get_audio_track_info(%02i)\n", track); return 0; } - start->min = cur_toc.TrackData[track - 1].Address[1]; - start->sec = cur_toc.TrackData[track - 1].Address[2]; - start->fr = cur_toc.TrackData[track - 1].Address[3]; + PTRACK_DATA td = &toc->TrackData[track - 1]; - *track_num = cur_toc.TrackData[track - 1].TrackNumber; - *attr = cur_toc.TrackData[track - 1].Control; - *attr |= ((cur_toc.TrackData[track - 1].Adr << 4) & 0xf0); + start->min = td->Address[1]; + start->sec = td->Address[2]; + start->fr = td->Address[3]; + + *track_num = td->TrackNumber; + *attr = td->Control; + *attr |= ((td->Adr << 4) & 0xf0); win_cdrom_ioctl_log("plat_cdrom_get_audio_track_info(%02i): %02i:%02i:%02i, %02i, %02X\n", track, start->min, start->sec, start->fr, *track_num, *attr); @@ -292,17 +404,20 @@ plat_cdrom_get_audio_track_info(UNUSED(int end), int track, int *track_num, TMSF /* TODO: See if track start is adjusted by 150 or not. */ int -plat_cdrom_get_audio_sub(UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, uint8_t *index, TMSF *rel_pos, TMSF *abs_pos) +plat_cdrom_get_audio_sub(void *local, UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, uint8_t *index, + TMSF *rel_pos, TMSF *abs_pos) { + win_cdrom_ioctl_t * ioctl = (win_cdrom_ioctl_t *) local; CDROM_SUB_Q_DATA_FORMAT insub; - SUB_Q_CHANNEL_DATA sub; - long size = 0; + SUB_Q_CHANNEL_DATA sub; + long size = 0; insub.Format = IOCTL_CDROM_CURRENT_POSITION; - plat_cdrom_open(); - DeviceIoControl(handle, IOCTL_CDROM_READ_Q_CHANNEL, &insub, sizeof(insub), &sub, sizeof(sub), (LPDWORD)&size, NULL); - plat_cdrom_close(); + plat_cdrom_open(ioctl); + DeviceIoControl(ioctl->handle, IOCTL_CDROM_READ_Q_CHANNEL, &insub, sizeof(insub), &sub, sizeof(sub), + (LPDWORD) &size, NULL); + plat_cdrom_close(ioctl); if (sub.CurrentPosition.TrackNumber < 1) return 0; @@ -320,92 +435,107 @@ plat_cdrom_get_audio_sub(UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, abs_pos->fr = sub.CurrentPosition.AbsoluteAddress[3]; win_cdrom_ioctl_log("plat_cdrom_get_audio_sub(): %02i, %02X, %02i, %02i:%02i:%02i, %02i:%02i:%02i\n", - *track, *attr, *index, rel_pos->min, rel_pos->sec, rel_pos->fr, abs_pos->min, abs_pos->sec, abs_pos->fr); + *track, *attr, *index, rel_pos->min, rel_pos->sec, rel_pos->fr, abs_pos->min, abs_pos->sec, + abs_pos->fr); return 1; } int -plat_cdrom_get_sector_size(UNUSED(uint32_t sector)) +plat_cdrom_get_sector_size(void *local, UNUSED(uint32_t sector)) { - long size; - DISK_GEOMETRY dgCDROM; + win_cdrom_ioctl_t * ioctl = (win_cdrom_ioctl_t *) local; + long size; + DISK_GEOMETRY dgCDROM; - plat_cdrom_open(); - DeviceIoControl(handle, IOCTL_CDROM_GET_DRIVE_GEOMETRY, NULL, 0, &dgCDROM, sizeof(dgCDROM), (LPDWORD)&size, NULL); - plat_cdrom_close(); + plat_cdrom_open(ioctl); + DeviceIoControl(ioctl->handle, IOCTL_CDROM_GET_DRIVE_GEOMETRY, NULL, 0, &dgCDROM, sizeof(dgCDROM), + (LPDWORD) &size, NULL); + plat_cdrom_close(ioctl); win_cdrom_ioctl_log("BytesPerSector=%d\n", dgCDROM.BytesPerSector); return dgCDROM.BytesPerSector; } int -plat_cdrom_read_sector(uint8_t *buffer, int raw, uint32_t sector) +plat_cdrom_read_sector(void *local, uint8_t *buffer, int raw, uint32_t sector) { - int status; - long size = 0; - int buflen = raw ? RAW_SECTOR_SIZE : COOKED_SECTOR_SIZE; + win_cdrom_ioctl_t * ioctl = (win_cdrom_ioctl_t *) local; + int status; + long size = 0; + int buflen = raw ? RAW_SECTOR_SIZE : COOKED_SECTOR_SIZE; - plat_cdrom_open(); + plat_cdrom_open(ioctl); if (raw) { - win_cdrom_ioctl_log("Raw\n"); /* Raw */ + win_cdrom_ioctl_log("Raw\n"); RAW_READ_INFO in; in.DiskOffset.LowPart = sector * COOKED_SECTOR_SIZE; in.DiskOffset.HighPart = 0; in.SectorCount = 1; in.TrackMode = CDDA; - status = DeviceIoControl(handle, IOCTL_CDROM_RAW_READ, &in, sizeof(in), - buffer, buflen, (LPDWORD)&size, NULL); + status = DeviceIoControl(ioctl->handle, IOCTL_CDROM_RAW_READ, &in, sizeof(in), + buffer, buflen, (LPDWORD) &size, NULL); } else { - win_cdrom_ioctl_log("Cooked\n"); /* Cooked */ + win_cdrom_ioctl_log("Cooked\n"); int success = 0; - DWORD newPos = SetFilePointer(handle, sector * COOKED_SECTOR_SIZE, 0, FILE_BEGIN); + DWORD newPos = SetFilePointer(ioctl->handle, sector * COOKED_SECTOR_SIZE, 0, FILE_BEGIN); if (newPos != 0xFFFFFFFF) - success = ReadFile(handle, buffer, buflen, (LPDWORD)&size, NULL); + success = ReadFile(ioctl->handle, buffer, buflen, (LPDWORD) &size, NULL); status = (success != 0); } - plat_cdrom_close(); + plat_cdrom_close(ioctl); win_cdrom_ioctl_log("ReadSector status=%d, sector=%d, size=%" PRId64 ".\n", status, sector, (long long) size); return (status > 0) ? (size == buflen) : -1; } void -plat_cdrom_eject(void) +plat_cdrom_eject(void *local) { - long size; + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + long size; - plat_cdrom_open(); - DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, (LPDWORD)&size, NULL); - - plat_cdrom_close(); + plat_cdrom_open(ioctl); + DeviceIoControl(ioctl->handle, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, (LPDWORD) &size, NULL); + plat_cdrom_close(ioctl); } void -plat_cdrom_close(void) +plat_cdrom_close(void *local) { - if (handle != NULL) { - CloseHandle(handle); - handle = NULL; + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; + + if (ioctl->handle != NULL) { + CloseHandle(ioctl->handle); + ioctl->handle = NULL; } } int -plat_cdrom_set_drive(const char *drv) +plat_cdrom_set_drive(void *local, const char *drv) { - plat_cdrom_close(); + win_cdrom_ioctl_t *ioctl = (win_cdrom_ioctl_t *) local; - memcpy(old_ioctl_path, ioctl_path, sizeof(ioctl_path)); - memset(ioctl_path, 0x00, sizeof(ioctl_path)); + plat_cdrom_close(ioctl); - wsprintf(ioctl_path, L"%S", drv); - win_cdrom_ioctl_log("Path is %S\n", ioctl_path); + memcpy(ioctl->old_path, ioctl->path, sizeof(ioctl->path)); + memset(ioctl->path, 0x00, sizeof(ioctl->path)); - toc_valid = 0; + wsprintf(ioctl->path, L"%S", drv); + win_cdrom_ioctl_log("Path is %S\n", ioctl->path); + + ioctl->toc_valid = 0; + + plat_cdrom_load(ioctl); - plat_cdrom_load(); return 1; } + +int +plat_cdrom_get_local_size(void) +{ + return sizeof(win_cdrom_ioctl_t); +} diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt index 9fbdd6ac1..a381051ba 100644 --- a/src/sound/CMakeLists.txt +++ b/src/sound/CMakeLists.txt @@ -64,8 +64,14 @@ if(OPENAL) if(TARGET OpenAL::OpenAL) target_link_libraries(86Box OpenAL::OpenAL) + if(WIN32 AND STATIC_BUILD) + target_link_libraries(OpenAL::OpenAL INTERFACE avrt) + endif() else() target_link_libraries(86Box ${OPENAL_LIBRARY}) + if(WIN32 AND STATIC_BUILD) + target_link_libraries(${OPENAL_LIBRARY} INTERFACE avrt) + endif() endif() include_directories(${OPENAL_INCLUDE_DIR}) @@ -164,6 +170,28 @@ if(OPL4ML) target_compile_definitions(snd PRIVATE USE_OPL4ML) target_sources(snd PRIVATE midi_opl4.c midi_opl4_yrw801.c) endif() + +find_package(PkgConfig ) +pkg_check_modules(SERIALPORT libserialport) + +if(SERIALPORT_FOUND OR DEFINED LIBSERIALPORT_ROOT) + add_compile_definitions(USE_LIBSERIALPORT=1) + + if(APPLE) + include_directories(${LIBSERIALPORT_ROOT}/include) + target_link_libraries(86Box ${LIBSERIALPORT_ROOT}/lib/libserialport.dylib) + elseif(WIN32) + include_directories(${SERIALPORT_INCLUDE_DIRS}) + target_link_libraries(86Box ${SERIALPORT_LIBRARIES} SetupAPI) + else() + include_directories(${SERIALPORT_INCLUDE_DIRS}) + target_link_libraries(86Box ${SERIALPORT_LIBRARIES}) + endif() + target_sources(snd PRIVATE + snd_opl2board.c + snd_opl_opl2board.cpp +) +endif() add_subdirectory(resid-fp) target_link_libraries(86Box resid-fp) diff --git a/src/sound/resid-fp/CMakeLists.txt b/src/sound/resid-fp/CMakeLists.txt index 5246dd73b..b91b48bba 100644 --- a/src/sound/resid-fp/CMakeLists.txt +++ b/src/sound/resid-fp/CMakeLists.txt @@ -13,6 +13,8 @@ # Copyright 2020-2021 David Hrdlička. # +set(CMAKE_CXX_STANDARD 17) + add_library(resid-fp STATIC Dac.cpp EnvelopeGenerator.cpp ExternalFilter.cpp Filter.cpp Filter6581.cpp Filter8580.cpp FilterModelConfig.cpp FilterModelConfig6581.cpp FilterModelConfig8580.cpp diff --git a/src/sound/resid-fp/Dac.cpp b/src/sound/resid-fp/Dac.cpp index 0665da817..5ae5429b6 100644 --- a/src/sound/resid-fp/Dac.cpp +++ b/src/sound/resid-fp/Dac.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2016 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004,2010 Dag Lem * @@ -22,9 +22,14 @@ #include "Dac.h" +#include "sidcxx11.h" + namespace reSIDfp { +constexpr double MOSFET_LEAKAGE_6581 = 0.0075; +constexpr double MOSFET_LEAKAGE_8580 = 0.0035; + Dac::Dac(unsigned int bits) : dac(new double[bits]), dacLength(bits) @@ -41,10 +46,8 @@ double Dac::getOutput(unsigned int input) const for (unsigned int i = 0; i < dacLength; i++) { - if ((input & (1 << i)) != 0) - { - dacValue += dac[i]; - } + const bool transistor_on = (input & (1 << i)) != 0; + dacValue += transistor_on ? dac[i] : dac[i] * leakage; } return dacValue; @@ -52,7 +55,7 @@ double Dac::getOutput(unsigned int input) const void Dac::kinkedDac(ChipModel chipModel) { - const double R_INFINITY = 1e6; + constexpr double R_INFINITY = 1e6; // Non-linearity parameter, 8580 DACs are perfectly linear const double _2R_div_R = chipModel == MOS6581 ? 2.20 : 2.00; @@ -60,6 +63,10 @@ void Dac::kinkedDac(ChipModel chipModel) // 6581 DACs are not terminated by a 2R resistor const bool term = chipModel == MOS8580; + leakage = chipModel == MOS6581 ? MOSFET_LEAKAGE_6581 : MOSFET_LEAKAGE_8580; + + double Vsum = 0.; + // Calculate voltage contribution by each individual bit in the R-2R ladder. for (unsigned int set_bit = 0; set_bit < dacLength; set_bit++) { @@ -102,18 +109,10 @@ void Dac::kinkedDac(ChipModel chipModel) } dac[set_bit] = Vn; + Vsum += Vn; } // Normalize to integerish behavior - double Vsum = 0.; - - for (unsigned int i = 0; i < dacLength; i++) - { - Vsum += dac[i]; - } - - Vsum /= 1 << dacLength; - for (unsigned int i = 0; i < dacLength; i++) { dac[i] /= Vsum; diff --git a/src/sound/resid-fp/Dac.h b/src/sound/resid-fp/Dac.h index 35bc0b2ca..757f12e4e 100644 --- a/src/sound/resid-fp/Dac.h +++ b/src/sound/resid-fp/Dac.h @@ -75,6 +75,15 @@ namespace reSIDfp class Dac { private: + /** + * DAC leakage + * + * "Even in standard transistors a small amount of current leaks even when they are technically switched off." + * + * https://en.wikipedia.org/wiki/Subthreshold_conduction + */ + double leakage; + /// analog values double * const dac; diff --git a/src/sound/resid-fp/EnvelopeGenerator.cpp b/src/sound/resid-fp/EnvelopeGenerator.cpp index af636ac7f..e7f5f4e8a 100644 --- a/src/sound/resid-fp/EnvelopeGenerator.cpp +++ b/src/sound/resid-fp/EnvelopeGenerator.cpp @@ -79,7 +79,7 @@ void EnvelopeGenerator::reset() exponential_counter_period = 1; new_exponential_counter_period = 0; - state = RELEASE; + state = State::RELEASE; counter_enabled = true; rate = adsrtable[release]; } @@ -98,7 +98,7 @@ void EnvelopeGenerator::writeCONTROL_REG(unsigned char control) if (gate_next) { // Gate bit on: Start attack, decay, sustain. - next_state = ATTACK; + next_state = State::ATTACK; state_pipeline = 2; if (resetLfsr || (exponential_pipeline == 2)) @@ -113,7 +113,7 @@ void EnvelopeGenerator::writeCONTROL_REG(unsigned char control) else { // Gate bit off: Start release. - next_state = RELEASE; + next_state = State::RELEASE; state_pipeline = envelope_pipeline > 0 ? 3 : 2; } } @@ -124,11 +124,11 @@ void EnvelopeGenerator::writeATTACK_DECAY(unsigned char attack_decay) attack = (attack_decay >> 4) & 0x0f; decay = attack_decay & 0x0f; - if (state == ATTACK) + if (state == State::ATTACK) { rate = adsrtable[attack]; } - else if (state == DECAY_SUSTAIN) + else if (state == State::DECAY_SUSTAIN) { rate = adsrtable[decay]; } @@ -146,7 +146,7 @@ void EnvelopeGenerator::writeSUSTAIN_RELEASE(unsigned char sustain_release) release = sustain_release & 0x0f; - if (state == RELEASE) + if (state == State::RELEASE) { rate = adsrtable[release]; } diff --git a/src/sound/resid-fp/EnvelopeGenerator.h b/src/sound/resid-fp/EnvelopeGenerator.h index f2aab3874..554b814b1 100644 --- a/src/sound/resid-fp/EnvelopeGenerator.h +++ b/src/sound/resid-fp/EnvelopeGenerator.h @@ -47,68 +47,68 @@ private: * The envelope state machine's distinct states. In addition to this, * envelope has a hold mode, which freezes envelope counter to zero. */ - enum State + enum class State { ATTACK, DECAY_SUSTAIN, RELEASE }; private: /// XOR shift register for ADSR prescaling. - unsigned int lfsr; + unsigned int lfsr = 0x7fff; /// Comparison value (period) of the rate counter before next event. - unsigned int rate; + unsigned int rate = 0; /** * During release mode, the SID approximates envelope decay via piecewise * linear decay rate. */ - unsigned int exponential_counter; + unsigned int exponential_counter = 0; /** * Comparison value (period) of the exponential decay counter before next * decrement. */ - unsigned int exponential_counter_period; - unsigned int new_exponential_counter_period; + unsigned int exponential_counter_period = 1; + unsigned int new_exponential_counter_period = 0; - unsigned int state_pipeline; + unsigned int state_pipeline = 0; /// - unsigned int envelope_pipeline; + unsigned int envelope_pipeline = 0; - unsigned int exponential_pipeline; + unsigned int exponential_pipeline = 0; /// Current envelope state - State state; - State next_state; + State state = State::RELEASE; + State next_state = State::RELEASE; /// Whether counter is enabled. Only switching to ATTACK can release envelope. - bool counter_enabled; + bool counter_enabled = true; /// Gate bit - bool gate; + bool gate = false; /// - bool resetLfsr; + bool resetLfsr = false; /// The current digital value of envelope output. - unsigned char envelope_counter; + unsigned char envelope_counter = 0xaa; /// Attack register - unsigned char attack; + unsigned char attack = 0; /// Decay register - unsigned char decay; + unsigned char decay = 0; /// Sustain register - unsigned char sustain; + unsigned char sustain = 0; /// Release register - unsigned char release; + unsigned char release = 0; /// The ENV3 value, sampled at the first phase of the clock - unsigned char env3; + unsigned char env3 = 0; private: static const unsigned int adsrtable[16]; @@ -129,31 +129,6 @@ public: */ unsigned int output() const { return envelope_counter; } - /** - * Constructor. - */ - EnvelopeGenerator() : - lfsr(0x7fff), - rate(0), - exponential_counter(0), - exponential_counter_period(1), - new_exponential_counter_period(0), - state_pipeline(0), - envelope_pipeline(0), - exponential_pipeline(0), - state(RELEASE), - next_state(RELEASE), - counter_enabled(true), - gate(false), - resetLfsr(false), - envelope_counter(0xaa), - attack(0), - decay(0), - sustain(0), - release(0), - env3(0) - {} - /** * SID reset. */ @@ -218,15 +193,15 @@ void EnvelopeGenerator::clock() { if (likely(counter_enabled)) { - if (state == ATTACK) + if (state == State::ATTACK) { if (++envelope_counter==0xff) { - next_state = DECAY_SUSTAIN; + next_state = State::DECAY_SUSTAIN; state_pipeline = 3; } } - else if ((state == DECAY_SUSTAIN) || (state == RELEASE)) + else if ((state == State::DECAY_SUSTAIN) || (state == State::RELEASE)) { if (--envelope_counter==0x00) { @@ -241,8 +216,8 @@ void EnvelopeGenerator::clock() { exponential_counter = 0; - if (((state == DECAY_SUSTAIN) && (envelope_counter != sustain)) - || (state == RELEASE)) + if (((state == State::DECAY_SUSTAIN) && (envelope_counter != sustain)) + || (state == State::RELEASE)) { // The envelope counter can flip from 0x00 to 0xff by changing state to // attack, then to release. The envelope counter will then continue @@ -257,7 +232,7 @@ void EnvelopeGenerator::clock() lfsr = 0x7fff; resetLfsr = false; - if (state == ATTACK) + if (state == State::ATTACK) { // The first envelope step in the attack state also resets the exponential // counter. This has been verified by sampling ENV3. @@ -344,7 +319,7 @@ void EnvelopeGenerator::state_change() switch (next_state) { - case ATTACK: + case State::ATTACK: if (state_pipeline == 1) { // The decay rate is "accidentally" enabled during first cycle of attack phase @@ -352,24 +327,24 @@ void EnvelopeGenerator::state_change() } else if (state_pipeline == 0) { - state = ATTACK; + state = State::ATTACK; // The attack rate is correctly enabled during second cycle of attack phase rate = adsrtable[attack]; counter_enabled = true; } break; - case DECAY_SUSTAIN: + case State::DECAY_SUSTAIN: if (state_pipeline == 0) { - state = DECAY_SUSTAIN; + state = State::DECAY_SUSTAIN; rate = adsrtable[decay]; } break; - case RELEASE: - if (((state == ATTACK) && (state_pipeline == 0)) - || ((state == DECAY_SUSTAIN) && (state_pipeline == 1))) + case State::RELEASE: + if (((state == State::ATTACK) && (state_pipeline == 0)) + || ((state == State::DECAY_SUSTAIN) && (state_pipeline == 1))) { - state = RELEASE; + state = State::RELEASE; rate = adsrtable[release]; } break; diff --git a/src/sound/resid-fp/ExternalFilter.cpp b/src/sound/resid-fp/ExternalFilter.cpp index eac790b31..7f44715b5 100644 --- a/src/sound/resid-fp/ExternalFilter.cpp +++ b/src/sound/resid-fp/ExternalFilter.cpp @@ -38,9 +38,7 @@ inline double getRC(double res, double cap) return res * cap; } -ExternalFilter::ExternalFilter() : - w0lp_1_s7(0), - w0hp_1_s17(0) +ExternalFilter::ExternalFilter() { reset(); } diff --git a/src/sound/resid-fp/ExternalFilter.h b/src/sound/resid-fp/ExternalFilter.h index 760ee5c22..17e8b1649 100644 --- a/src/sound/resid-fp/ExternalFilter.h +++ b/src/sound/resid-fp/ExternalFilter.h @@ -34,8 +34,6 @@ namespace reSIDfp * acts as a high-pass filter with a cutoff dependent on the attached audio * equipment impedance. Here we suppose an impedance of 10kOhm resulting * in a 3 dB attenuation at 1.6Hz. - * To operate properly the 6581 audio output needs a pull-down resistor - *(1KOhm recommended, not needed on 8580) * * ~~~ * 9/12V @@ -47,15 +45,18 @@ namespace reSIDfp * | | pF +-C----o-----C-----+ 10k * 470 | | * GND GND pF R 1K | amp - * * * | +----- + * * ** | +----- * * GND * ~~~ * * The STC networks are connected with a [BJT] based [common collector] * used as a voltage follower (featuring a 2SC1815 NPN transistor). - * * The C64c board additionally includes a [bootstrap] condenser to increase - * the input impedance of the common collector. + * + * * To operate properly the 6581 audio output needs a pull-down resistor + * (1KOhm recommended, not needed on 8580) + * ** The C64c board additionally includes a [bootstrap] condenser to increase + * the input impedance of the common collector. * * [BJT]: https://en.wikipedia.org/wiki/Bipolar_junction_transistor * [common collector]: https://en.wikipedia.org/wiki/Common_collector @@ -70,9 +71,9 @@ private: /// Highpass filter voltage int Vhp; - int w0lp_1_s7; + int w0lp_1_s7 = 0; - int w0hp_1_s17; + int w0hp_1_s17 = 0; public: /** @@ -80,7 +81,7 @@ public: * * @param input */ - int clock(unsigned short input); + int clock(int input); /** * Constructor. @@ -108,9 +109,9 @@ namespace reSIDfp { RESID_INLINE -int ExternalFilter::clock(unsigned short input) +int ExternalFilter::clock(int input) { - const int Vi = (static_cast(input)<<11) - (1 << (11+15)); + const int Vi = (input<<11) - (1 << (11+15)); const int dVlp = (w0lp_1_s7 * (Vi - Vlp) >> 7); const int dVhp = (w0hp_1_s17 * (Vlp - Vhp) >> 17); Vlp += dVlp; diff --git a/src/sound/resid-fp/Filter.cpp b/src/sound/resid-fp/Filter.cpp index 2a2dd24f7..6255c5729 100644 --- a/src/sound/resid-fp/Filter.cpp +++ b/src/sound/resid-fp/Filter.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2013 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004 Dag Lem * @@ -20,11 +20,87 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#define FILTER_CPP + #include "Filter.h" namespace reSIDfp { +void Filter::updateMixing() +{ + currentVolume = volume[vol]; + + unsigned int Nsum = 0; + unsigned int Nmix = 0; + + (filt1 ? Nsum : Nmix)++; + (filt2 ? Nsum : Nmix)++; + + if (filt3) Nsum++; + else if (!voice3off) Nmix++; + + (filtE ? Nsum : Nmix)++; + + currentSummer = summer[Nsum]; + + if (lp) Nmix++; + if (bp) Nmix++; + if (hp) Nmix++; + + currentMixer = mixer[Nmix]; +} + +void Filter::writeFC_LO(unsigned char fc_lo) +{ + fc = (fc & 0x7f8) | (fc_lo & 0x007); + updateCenterFrequency(); +} + +void Filter::writeFC_HI(unsigned char fc_hi) +{ + fc = (fc_hi << 3 & 0x7f8) | (fc & 0x007); + updateCenterFrequency(); +} + +void Filter::writeRES_FILT(unsigned char res_filt) +{ + filt = res_filt; + + updateResonance((res_filt >> 4) & 0x0f); + + if (enabled) + { + filt1 = (filt & 0x01) != 0; + filt2 = (filt & 0x02) != 0; + filt3 = (filt & 0x04) != 0; + filtE = (filt & 0x08) != 0; + } + + updateMixing(); +} + +void Filter::writeMODE_VOL(unsigned char mode_vol) +{ + vol = mode_vol & 0x0f; + lp = (mode_vol & 0x10) != 0; + bp = (mode_vol & 0x20) != 0; + hp = (mode_vol & 0x40) != 0; + voice3off = (mode_vol & 0x80) != 0; + + updateMixing(); +} + +Filter::Filter(FilterModelConfig& fmc) : + mixer(fmc.getMixer()), + summer(fmc.getSummer()), + resonance(fmc.getResonance()), + volume(fmc.getVolume()), + fmc(fmc) +{ + input(0); +} + void Filter::enable(bool enable) { enabled = enable; @@ -47,44 +123,4 @@ void Filter::reset() writeRES_FILT(0); } -void Filter::writeFC_LO(unsigned char fc_lo) -{ - fc = (fc & 0x7f8) | (fc_lo & 0x007); - updatedCenterFrequency(); -} - -void Filter::writeFC_HI(unsigned char fc_hi) -{ - fc = (fc_hi << 3 & 0x7f8) | (fc & 0x007); - updatedCenterFrequency(); -} - -void Filter::writeRES_FILT(unsigned char res_filt) -{ - filt = res_filt; - - updateResonance((res_filt >> 4) & 0x0f); - - if (enabled) - { - filt1 = (filt & 0x01) != 0; - filt2 = (filt & 0x02) != 0; - filt3 = (filt & 0x04) != 0; - filtE = (filt & 0x08) != 0; - } - - updatedMixing(); -} - -void Filter::writeMODE_VOL(unsigned char mode_vol) -{ - vol = mode_vol & 0x0f; - lp = (mode_vol & 0x10) != 0; - bp = (mode_vol & 0x20) != 0; - hp = (mode_vol & 0x40) != 0; - voice3off = (mode_vol & 0x80) != 0; - - updatedMixing(); -} - } // namespace reSIDfp diff --git a/src/sound/resid-fp/Filter.h b/src/sound/resid-fp/Filter.h index 4b3473369..6873d9906 100644 --- a/src/sound/resid-fp/Filter.h +++ b/src/sound/resid-fp/Filter.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2017 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004 Dag Lem * @@ -23,6 +23,10 @@ #ifndef FILTER_H #define FILTER_H +#include "FilterModelConfig.h" + +#include "siddefs-fp.h" + namespace reSIDfp { @@ -31,93 +35,97 @@ namespace reSIDfp */ class Filter { +private: + unsigned short** mixer; + unsigned short** summer; + unsigned short** resonance; + unsigned short** volume; + protected: - /// Current volume amplifier setting. - unsigned short* currentGain; + FilterModelConfig& fmc; /// Current filter/voice mixer setting. - unsigned short* currentMixer; + unsigned short* currentMixer = nullptr; /// Filter input summer setting. - unsigned short* currentSummer; + unsigned short* currentSummer = nullptr; /// Filter resonance value. - unsigned short* currentResonance; + unsigned short* currentResonance = nullptr; + + /// Current volume amplifier setting. + unsigned short* currentVolume = nullptr; /// Filter highpass state. - int Vhp; + int Vhp = 0; /// Filter bandpass state. - int Vbp; + int Vbp = 0; /// Filter lowpass state. - int Vlp; + int Vlp = 0; /// Filter external input. - int ve; + int Ve = 0; /// Filter cutoff frequency. - unsigned int fc; + unsigned int fc = 0; /// Routing to filter or outside filter - bool filt1, filt2, filt3, filtE; + //@{ + bool filt1 = false; + bool filt2 = false; + bool filt3 = false; + bool filtE = false; + //@} /// Switch voice 3 off. - bool voice3off; + bool voice3off = false; /// Highpass, bandpass, and lowpass filter modes. - bool hp, bp, lp; - - /// Current volume. - unsigned char vol; + //@{ + bool hp = false; + bool bp = false; + bool lp = false; + //@} private: + /// Current volume. + unsigned char vol = 0; + /// Filter enabled. - bool enabled; + bool enabled = true; /// Selects which inputs to route through filter. - unsigned char filt; + unsigned char filt = 0; protected: /** - * Set filter cutoff frequency. + * Update filter cutoff frequency. */ - virtual void updatedCenterFrequency() = 0; + virtual void updateCenterFrequency() = 0; /** - * Set filter resonance. + * Update filter resonance. + * + * @param res the new resonance value */ - virtual void updateResonance(unsigned char res) = 0; + void updateResonance(unsigned char res) { currentResonance = resonance[res]; } /** * Mixing configuration modified (offsets change) */ - virtual void updatedMixing() = 0; + void updateMixing(); + + /** + * Get the filter cutoff register value + */ + unsigned int getFC() const { return fc; } public: - Filter() : - currentGain(nullptr), - currentMixer(nullptr), - currentSummer(nullptr), - currentResonance(nullptr), - Vhp(0), - Vbp(0), - Vlp(0), - ve(0), - fc(0), - filt1(false), - filt2(false), - filt3(false), - filtE(false), - voice3off(false), - hp(false), - bp(false), - lp(false), - vol(0), - enabled(true), - filt(0) {} + Filter(FilterModelConfig& fmc); - virtual ~Filter() {} + virtual ~Filter() = default; /** * SID clocking - 1 cycle @@ -169,7 +177,14 @@ public: */ void writeMODE_VOL(unsigned char mode_vol); - virtual void input(int input) = 0; + /** + * Apply a signal to EXT-IN + * + * @param input a signed 16 bit sample + */ + void input(short input) { Ve = fmc.getNormalizedVoice(input/32768.f, 0); } + + inline int getNormalizedVoice(float value, unsigned int env) const { return fmc.getNormalizedVoice(value, env); } }; } // namespace reSIDfp diff --git a/src/sound/resid-fp/Filter6581.cpp b/src/sound/resid-fp/Filter6581.cpp index c064a8801..b761c22ea 100644 --- a/src/sound/resid-fp/Filter6581.cpp +++ b/src/sound/resid-fp/Filter6581.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2015 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004,2010 Dag Lem * @@ -20,8 +20,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define FILTER6581_CPP - #include "Filter6581.h" #include "Integrator6581.h" @@ -29,47 +27,60 @@ namespace reSIDfp { +unsigned short Filter6581::clock(int voice1, int voice2, int voice3) +{ + const int V1 = voice1; + const int V2 = voice2; + // Voice 3 is silenced by voice3off if it is not routed through the filter. + const int V3 = (filt3 || !voice3off) ? voice3 : 0; + + int Vsum = 0; + int Vmix = 0; + + (filt1 ? Vsum : Vmix) += V1; + (filt2 ? Vsum : Vmix) += V2; + (filt3 ? Vsum : Vmix) += V3; + (filtE ? Vsum : Vmix) += Ve; + + Vhp = currentSummer[currentResonance[Vbp] + Vlp + Vsum]; + Vbp = hpIntegrator.solve(Vhp); + Vlp = bpIntegrator.solve(Vbp); + + int Vfilt = 0; + if (lp) Vfilt += Vlp; + if (bp) Vfilt += Vbp; + if (hp) Vfilt += Vhp; + + // The filter input resistors are slightly bigger than the voice ones + // Scale the values accordingly + constexpr int filterGain = static_cast(0.93 * (1 << 12)); + Vfilt = (Vfilt * filterGain) >> 12; + + return currentVolume[currentMixer[Vmix + Vfilt]]; +} + Filter6581::~Filter6581() { delete [] f0_dac; } -void Filter6581::updatedCenterFrequency() +void Filter6581::updateCenterFrequency() { - const unsigned short Vw = f0_dac[fc]; - hpIntegrator->setVw(Vw); - bpIntegrator->setVw(Vw); -} - -void Filter6581::updatedMixing() -{ - currentGain = gain_vol[vol]; - - unsigned int ni = 0; - unsigned int no = 0; - - (filt1 ? ni : no)++; - (filt2 ? ni : no)++; - - if (filt3) ni++; - else if (!voice3off) no++; - - (filtE ? ni : no)++; - - currentSummer = summer[ni]; - - if (lp) no++; - if (bp) no++; - if (hp) no++; - - currentMixer = mixer[no]; + const unsigned short Vw = f0_dac[getFC()]; + hpIntegrator.setVw(Vw); + bpIntegrator.setVw(Vw); } void Filter6581::setFilterCurve(double curvePosition) { delete [] f0_dac; f0_dac = FilterModelConfig6581::getInstance()->getDAC(curvePosition); - updatedCenterFrequency(); + updateCenterFrequency(); +} + +void Filter6581::setFilterRange(double adjustment) +{ + FilterModelConfig6581::getInstance()->setFilterRange(adjustment); } } // namespace reSIDfp diff --git a/src/sound/resid-fp/Filter6581.h b/src/sound/resid-fp/Filter6581.h index 7fca331ab..27b97b991 100644 --- a/src/sound/resid-fp/Filter6581.h +++ b/src/sound/resid-fp/Filter6581.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2022 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004,2010 Dag Lem * @@ -23,12 +23,9 @@ #ifndef FILTER6581_H #define FILTER6581_H -#include "siddefs-fp.h" - -#include - #include "Filter.h" #include "FilterModelConfig6581.h" +#include "Integrator6581.h" #include "sidcxx11.h" @@ -108,7 +105,7 @@ class Integrator6581; * | | | v1 | | | | * D0 | | | \ ---R8--+ | | +---------------------------+ * | | | | | | | - * R6 R6 R6 R6 R6 R6 R6 + * R6 R6 R6 R6 R6* R6* R6* * | | | | $18 | | | $18 * | \ | | D7: 1=open \ \ \ D6 - D4: 0=open * | | | | | | | @@ -143,6 +140,7 @@ class Integrator6581; * * R2 ~ 2.0*R1 * R6 ~ 6.0*R1 + * R6* ~ 1.07*R6 * R8 ~ 8.0*R1 * R24 ~ 24.0*R1 * @@ -322,104 +320,49 @@ class Integrator6581; class Filter6581 final : public Filter { private: - const unsigned short* f0_dac; - - unsigned short** mixer; - unsigned short** summer; - unsigned short** gain_res; - unsigned short** gain_vol; - - const int voiceScaleS11; - const int voiceDC; - /// VCR + associated capacitor connected to highpass output. - std::unique_ptr const hpIntegrator; + Integrator6581 hpIntegrator; /// VCR + associated capacitor connected to bandpass output. - std::unique_ptr const bpIntegrator; + Integrator6581 bpIntegrator; + + const unsigned short* f0_dac; protected: /** * Set filter cutoff frequency. */ - void updatedCenterFrequency() override; - - /** - * Set filter resonance. - * - * In the MOS 6581, 1/Q is controlled linearly by res. - */ - void updateResonance(unsigned char res) override { currentResonance = gain_res[res]; } - - void updatedMixing() override; + void updateCenterFrequency() override; public: Filter6581() : - f0_dac(FilterModelConfig6581::getInstance()->getDAC(0.5)), - mixer(FilterModelConfig6581::getInstance()->getMixer()), - summer(FilterModelConfig6581::getInstance()->getSummer()), - gain_res(FilterModelConfig6581::getInstance()->getGainRes()), - gain_vol(FilterModelConfig6581::getInstance()->getGainVol()), - voiceScaleS11(FilterModelConfig6581::getInstance()->getVoiceScaleS11()), - voiceDC(FilterModelConfig6581::getInstance()->getNormalizedVoiceDC()), - hpIntegrator(FilterModelConfig6581::getInstance()->buildIntegrator()), - bpIntegrator(FilterModelConfig6581::getInstance()->buildIntegrator()) - { - input(0); - } + Filter(*FilterModelConfig6581::getInstance()), + hpIntegrator(*FilterModelConfig6581::getInstance()), + bpIntegrator(*FilterModelConfig6581::getInstance()), + f0_dac(FilterModelConfig6581::getInstance()->getDAC(0.5)) + {} - ~Filter6581(); + ~Filter6581() override; - unsigned short clock(int voice1, int voice2, int voice3) override; - - void input(int sample) override { ve = (sample * voiceScaleS11 * 3 >> 11) + mixer[0][0]; } + unsigned short clock(int v1, int v2, int v3) override; /** * Set filter curve type based on single parameter. * - * @param curvePosition 0 .. 1, where 0 sets center frequency high ("light") and 1 sets it low ("dark"), default is 0.5 + * @param curvePosition 0 .. 1, where 0 sets center frequency high ("bright") and 1 sets it low ("dark"). + * Default is 0.5 */ void setFilterCurve(double curvePosition); + + /** + * Set filter offset and range based on single parameter. + * + * @param adjustment 0 .. 1, where 0 sets center frequency low ("dark"), 1 sets it high ("bright"). + * This also affects the range. Default is 0.5 + */ + void setFilterRange(double adjustment); }; } // namespace reSIDfp -#if RESID_INLINING || defined(FILTER6581_CPP) - -#include "Integrator6581.h" - -namespace reSIDfp -{ - -RESID_INLINE -unsigned short Filter6581::clock(int voice1, int voice2, int voice3) -{ - voice1 = (voice1 * voiceScaleS11 >> 15) + voiceDC; - voice2 = (voice2 * voiceScaleS11 >> 15) + voiceDC; - // Voice 3 is silenced by voice3off if it is not routed through the filter. - voice3 = (filt3 || !voice3off) ? (voice3 * voiceScaleS11 >> 15) + voiceDC : 0; - - int Vi = 0; - int Vo = 0; - - (filt1 ? Vi : Vo) += voice1; - (filt2 ? Vi : Vo) += voice2; - (filt3 ? Vi : Vo) += voice3; - (filtE ? Vi : Vo) += ve; - - Vhp = currentSummer[currentResonance[Vbp] + Vlp + Vi]; - Vbp = hpIntegrator->solve(Vhp); - Vlp = bpIntegrator->solve(Vbp); - - if (lp) Vo += Vlp; - if (bp) Vo += Vbp; - if (hp) Vo += Vhp; - - return currentGain[currentMixer[Vo]]; -} - -} // namespace reSIDfp - -#endif - #endif diff --git a/src/sound/resid-fp/Filter8580.cpp b/src/sound/resid-fp/Filter8580.cpp index a70285a8a..c54e2b741 100644 --- a/src/sound/resid-fp/Filter8580.cpp +++ b/src/sound/resid-fp/Filter8580.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2019 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004,2010 Dag Lem * @@ -20,8 +20,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define FILTER8580_CPP - #include "Filter8580.h" #include "Integrator8580.h" @@ -29,6 +27,32 @@ namespace reSIDfp { +unsigned short Filter8580::clock(int voice1, int voice2, int voice3) +{ + const int V1 = voice1; + const int V2 = voice2; + // Voice 3 is silenced by voice3off if it is not routed through the filter. + const int V3 = (filt3 || !voice3off) ? voice3 : 0; + + int Vsum = 0; + int Vmix = 0; + + (filt1 ? Vsum : Vmix) += V1; + (filt2 ? Vsum : Vmix) += V2; + (filt3 ? Vsum : Vmix) += V3; + (filtE ? Vsum : Vmix) += Ve; + + Vhp = currentSummer[currentResonance[Vbp] + Vlp + Vsum]; + Vbp = hpIntegrator.solve(Vhp); + Vlp = bpIntegrator.solve(Vbp); + + if (lp) Vmix += Vlp; + if (bp) Vmix += Vbp; + if (hp) Vmix += Vhp; + + return currentVolume[currentMixer[Vmix]]; +} + /** * W/L ratio of frequency DAC bit 0, * other bit are proportional. @@ -37,18 +61,18 @@ namespace reSIDfp */ const double DAC_WL0 = 0.00615; -Filter8580::~Filter8580() {} +Filter8580::~Filter8580() = default; -void Filter8580::updatedCenterFrequency() +void Filter8580::updateCenterFrequency() { double wl; double dacWL = DAC_WL0; - if (fc) + if (getFC()) { wl = 0.; for (unsigned int i = 0; i < 11; i++) { - if (fc & (1 << i)) + if (getFC() & (1 << i)) { wl += dacWL; } @@ -60,32 +84,8 @@ void Filter8580::updatedCenterFrequency() wl = dacWL/2.; } - hpIntegrator->setFc(wl); - bpIntegrator->setFc(wl); -} - -void Filter8580::updatedMixing() -{ - currentGain = gain_vol[vol]; - - unsigned int ni = 0; - unsigned int no = 0; - - (filt1 ? ni : no)++; - (filt2 ? ni : no)++; - - if (filt3) ni++; - else if (!voice3off) no++; - - (filtE ? ni : no)++; - - currentSummer = summer[ni]; - - if (lp) no++; - if (bp) no++; - if (hp) no++; - - currentMixer = mixer[no]; + hpIntegrator.setFc(wl); + bpIntegrator.setFc(wl); } void Filter8580::setFilterCurve(double curvePosition) @@ -94,8 +94,8 @@ void Filter8580::setFilterCurve(double curvePosition) // 1.2 <= cp <= 1.8 cp = 1.8 - curvePosition * 3./5.; - hpIntegrator->setV(cp); - bpIntegrator->setV(cp); + hpIntegrator.setV(cp); + bpIntegrator.setV(cp); } } // namespace reSIDfp diff --git a/src/sound/resid-fp/Filter8580.h b/src/sound/resid-fp/Filter8580.h index 2166ec0da..59dbfceee 100644 --- a/src/sound/resid-fp/Filter8580.h +++ b/src/sound/resid-fp/Filter8580.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2022 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004,2010 Dag Lem * @@ -23,10 +23,6 @@ #ifndef FILTER8580_H #define FILTER8580_H -#include "siddefs-fp.h" - -#include - #include "Filter.h" #include "FilterModelConfig8580.h" #include "Integrator8580.h" @@ -281,58 +277,32 @@ class Integrator8580; class Filter8580 final : public Filter { private: - unsigned short** mixer; - unsigned short** summer; - unsigned short** gain_res; - unsigned short** gain_vol; - - const int voiceScaleS11; - const int voiceDC; - - double cp; - /// VCR + associated capacitor connected to highpass output. - std::unique_ptr const hpIntegrator; + Integrator8580 hpIntegrator; /// VCR + associated capacitor connected to bandpass output. - std::unique_ptr const bpIntegrator; + Integrator8580 bpIntegrator; + + double cp; protected: /** * Set filter cutoff frequency. */ - void updatedCenterFrequency() override; - - /** - * Set filter resonance. - * - * @param res the new resonance value - */ - void updateResonance(unsigned char res) override { currentResonance = gain_res[res]; } - - void updatedMixing() override; + void updateCenterFrequency() override; public: Filter8580() : - mixer(FilterModelConfig8580::getInstance()->getMixer()), - summer(FilterModelConfig8580::getInstance()->getSummer()), - gain_res(FilterModelConfig8580::getInstance()->getGainRes()), - gain_vol(FilterModelConfig8580::getInstance()->getGainVol()), - voiceScaleS11(FilterModelConfig8580::getInstance()->getVoiceScaleS11()), - voiceDC(FilterModelConfig8580::getInstance()->getNormalizedVoiceDC()), - cp(0.5), - hpIntegrator(FilterModelConfig8580::getInstance()->buildIntegrator()), - bpIntegrator(FilterModelConfig8580::getInstance()->buildIntegrator()) + Filter(*FilterModelConfig8580::getInstance()), + hpIntegrator(*FilterModelConfig8580::getInstance()), + bpIntegrator(*FilterModelConfig8580::getInstance()) { - setFilterCurve(cp); - input(0); + setFilterCurve(0.5); } - ~Filter8580(); + ~Filter8580() override; - unsigned short clock(int voice1, int voice2, int voice3) override; - - void input(int sample) override { ve = (sample * voiceScaleS11 * 3 >> 11) + mixer[0][0]; } + unsigned short clock(int v1, int v2, int v3) override; /** * Set filter curve type based on single parameter. @@ -344,40 +314,4 @@ public: } // namespace reSIDfp -#if RESID_INLINING || defined(FILTER8580_CPP) - -namespace reSIDfp -{ - -RESID_INLINE -unsigned short Filter8580::clock(int voice1, int voice2, int voice3) -{ - voice1 = (voice1 * voiceScaleS11 >> 15) + voiceDC; - voice2 = (voice2 * voiceScaleS11 >> 15) + voiceDC; - // Voice 3 is silenced by voice3off if it is not routed through the filter. - voice3 = (filt3 || !voice3off) ? (voice3 * voiceScaleS11 >> 15) + voiceDC : 0; - - int Vi = 0; - int Vo = 0; - - (filt1 ? Vi : Vo) += voice1; - (filt2 ? Vi : Vo) += voice2; - (filt3 ? Vi : Vo) += voice3; - (filtE ? Vi : Vo) += ve; - - Vhp = currentSummer[currentResonance[Vbp] + Vlp + Vi]; - Vbp = hpIntegrator->solve(Vhp); - Vlp = bpIntegrator->solve(Vbp); - - if (lp) Vo += Vlp; - if (bp) Vo += Vbp; - if (hp) Vo += Vhp; - - return currentGain[currentMixer[Vo]]; -} - -} // namespace reSIDfp - -#endif - #endif diff --git a/src/sound/resid-fp/FilterModelConfig.cpp b/src/sound/resid-fp/FilterModelConfig.cpp index cd4b20400..2ab459164 100644 --- a/src/sound/resid-fp/FilterModelConfig.cpp +++ b/src/sound/resid-fp/FilterModelConfig.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2022 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004,2010 Dag Lem * @@ -29,7 +29,6 @@ namespace reSIDfp FilterModelConfig::FilterModelConfig( double vvr, - double vdv, double c, double vdd, double vth, @@ -37,21 +36,19 @@ FilterModelConfig::FilterModelConfig( const Spline::Point *opamp_voltage, int opamp_size ) : - voice_voltage_range(vvr), - voice_DC_voltage(vdv), C(c), Vdd(vdd), Vth(vth), - Ut(26.0e-3), - uCox(ucox), Vddt(Vdd - Vth), vmin(opamp_voltage[0].x), vmax(std::max(Vddt, opamp_voltage[0].y)), denorm(vmax - vmin), norm(1.0 / denorm), N16(norm * ((1 << 16) - 1)), - currFactorCoeff(denorm * (uCox / 2. * 1.0e-6 / C)) + voice_voltage_range(vvr) { + setUCox(ucox); + // Convert op-amp voltage transfer to 16 bit values. std::vector scaled_voltage(opamp_size); @@ -79,4 +76,29 @@ FilterModelConfig::FilterModelConfig( } } +FilterModelConfig::~FilterModelConfig() +{ + for (int i = 0; i < 8; i++) + { + delete [] mixer[i]; + } + + for (int i = 0; i < 5; i++) + { + delete [] summer[i]; + } + + for (int i = 0; i < 16; i++) + { + delete [] volume[i]; + delete [] resonance[i]; + } +} + +void FilterModelConfig::setUCox(double new_uCox) +{ + uCox = new_uCox; + currFactorCoeff = denorm * (uCox / 2. * 1.0e-6 / C); +} + } // namespace reSIDfp diff --git a/src/sound/resid-fp/FilterModelConfig.h b/src/sound/resid-fp/FilterModelConfig.h index 9e557d363..1d37a8bf3 100644 --- a/src/sound/resid-fp/FilterModelConfig.h +++ b/src/sound/resid-fp/FilterModelConfig.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2023 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004,2010 Dag Lem * @@ -24,8 +24,10 @@ #define FILTERMODELCONFIG_H #include +#include #include +#include "OpAmp.h" #include "Spline.h" #include "sidcxx11.h" @@ -35,20 +37,46 @@ namespace reSIDfp class FilterModelConfig { -protected: - const double voice_voltage_range; - const double voice_DC_voltage; +private: + /* + * Hack to add quick dither when converting values from float to int + * and avoid quantization noise. + * Hopefully this can be removed the day we move all the analog part + * processing to floats. + * + * Not sure about the effect of using such small buffer of numbers + * since the random sequence repeats every 1024 values but for + * now it seems to do the job. + */ + class Randomnoise + { + private: + double buffer[1024]; + mutable int index = 0; + public: + Randomnoise() + { + std::uniform_real_distribution unif(0., 1.); + std::default_random_engine re; + for (int i=0; i<1024; i++) + buffer[i] = unif(re); + } + double getNoise() const { index = (index + 1) & 0x3ff; return buffer[index]; } + }; +protected: /// Capacitor value. const double C; /// Transistor parameters. //@{ - const double Vdd; + /// Thermal voltage: Ut = kT/q = 8.61734315e-5*T ~ 26mV + static constexpr double Ut = 26.0e-3; + + const double Vdd; ///< Positive supply voltage const double Vth; ///< Threshold voltage - const double Ut; ///< Thermal voltage: Ut = kT/q = 8.61734315e-5*T ~ 26mV - const double uCox; ///< Transconductance coefficient: u*Cox const double Vddt; ///< Vdd - Vth + double uCox; ///< Transconductance coefficient: u*Cox //@} // Derived stuff @@ -58,38 +86,46 @@ protected: /// Fixed point scaling for 16 bit op-amp output. const double N16; + const double voice_voltage_range; + /// Current factor coefficient for op-amp integrators. - const double currFactorCoeff; + double currFactorCoeff; /// Lookup tables for gain and summer op-amps in output stage / filter. //@{ unsigned short* mixer[8]; //-V730_NOINIT this is initialized in the derived class constructor unsigned short* summer[5]; //-V730_NOINIT this is initialized in the derived class constructor - unsigned short* gain_vol[16]; //-V730_NOINIT this is initialized in the derived class constructor - unsigned short* gain_res[16]; //-V730_NOINIT this is initialized in the derived class constructor + unsigned short* volume[16]; //-V730_NOINIT this is initialized in the derived class constructor + unsigned short* resonance[16]; //-V730_NOINIT this is initialized in the derived class constructor //@} /// Reverse op-amp transfer function. unsigned short opamp_rev[1 << 16]; //-V730_NOINIT this is initialized in the derived class constructor private: - FilterModelConfig (const FilterModelConfig&) DELETE; - FilterModelConfig& operator= (const FilterModelConfig&) DELETE; + Randomnoise rnd; + +private: + FilterModelConfig(const FilterModelConfig&) = delete; + FilterModelConfig& operator= (const FilterModelConfig&) = delete; + + inline double getVoiceVoltage(float value, unsigned int env) const + { + return value * voice_voltage_range + getVoiceDC(env); + } protected: /** * @param vvr voice voltage range - * @param vdv voice DC voltage * @param c capacitor value - * @param vdd Vdd + * @param vdd Vdd supply voltage * @param vth threshold voltage * @param ucox u*Cox - * @param ominv opamp min voltage - * @param omaxv opamp max voltage + * @param opamp_voltage opamp voltage array + * @param opamp_size opamp voltage array size */ FilterModelConfig( double vvr, - double vdv, double c, double vdd, double vth, @@ -98,52 +134,139 @@ protected: int opamp_size ); - ~FilterModelConfig() + ~FilterModelConfig(); + + void setUCox(double new_uCox); + + virtual double getVoiceDC(unsigned int env) const = 0; + + /** + * The filter summer operates at n ~ 1, and has 5 fundamentally different + * input configurations (2 - 6 input "resistors"). + * + * Note that all "on" transistors are modeled as one. This is not + * entirely accurate, since the input for each transistor is different, + * and transistors are not linear components. However modeling all + * transistors separately would be extremely costly. + */ + inline void buildSummerTable(const OpAmp& opampModel) { - for (int i = 0; i < 8; i++) - { - delete [] mixer[i]; - } + const double r_N16 = 1. / N16; for (int i = 0; i < 5; i++) { - delete [] summer[i]; - } + const int idiv = 2 + i; // 2 - 6 input "resistors". + const int size = idiv << 16; + const double n = idiv; + const double r_idiv = 1. / idiv; + opampModel.reset(); + summer[i] = new unsigned short[size]; - for (int i = 0; i < 16; i++) + for (int vi = 0; vi < size; vi++) + { + const double vin = vmin + vi * r_N16 * r_idiv; /* vmin .. vmax */ + summer[i][vi] = getNormalizedValue(opampModel.solve(n, vin)); + } + } + } + + /** + * The audio mixer operates at n ~ 8/6 (6581) or 8/5 (8580), + * and has 8 fundamentally different input configurations + * (0 - 7 input "resistors"). + * + * All "on", transistors are modeled as one - see comments above for + * the filter summer. + */ + inline void buildMixerTable(const OpAmp& opampModel, double nRatio) + { + const double r_N16 = 1. / N16; + + for (int i = 0; i < 8; i++) { - delete [] gain_vol[i]; - delete [] gain_res[i]; + const int idiv = (i == 0) ? 1 : i; + const int size = (i == 0) ? 1 : i << 16; + const double n = i * nRatio; + const double r_idiv = 1. / idiv; + opampModel.reset(); + mixer[i] = new unsigned short[size]; + + for (int vi = 0; vi < size; vi++) + { + const double vin = vmin + vi * r_N16 * r_idiv; /* vmin .. vmax */ + mixer[i][vi] = getNormalizedValue(opampModel.solve(n, vin)); + } + } + } + + /** + * 4 bit "resistor" ladders in the audio output gain + * necessitate 16 gain tables. + * From die photographs of the volume "resistor" ladders + * it follows that gain ~ vol/12 (6581) or vol/16 (8580) + * (assuming ideal op-amps and ideal "resistors"). + */ + inline void buildVolumeTable(const OpAmp& opampModel, double nDivisor) + { + const double r_N16 = 1. / N16; + + for (int n8 = 0; n8 < 16; n8++) + { + const int size = 1 << 16; + const double n = n8 / nDivisor; + opampModel.reset(); + volume[n8] = new unsigned short[size]; + + for (int vi = 0; vi < size; vi++) + { + const double vin = vmin + vi * r_N16; /* vmin .. vmax */ + volume[n8][vi] = getNormalizedValue(opampModel.solve(n, vin)); + } + } + } + + /** + * 4 bit "resistor" ladders in the bandpass resonance gain + * necessitate 16 gain tables. + * From die photographs of the bandpass "resistor" ladders + * it follows that 1/Q ~ ~res/8 (6581) or 2^((4 - res)/8) (8580) + * (assuming ideal op-amps and ideal "resistors"). + */ + inline void buildResonanceTable(const OpAmp& opampModel, const double resonance_n[16]) + { + const double r_N16 = 1. / N16; + + for (int n8 = 0; n8 < 16; n8++) + { + const int size = 1 << 16; + opampModel.reset(); + resonance[n8] = new unsigned short[size]; + + for (int vi = 0; vi < size; vi++) + { + const double vin = vmin + vi * r_N16; /* vmin .. vmax */ + resonance[n8][vi] = getNormalizedValue(opampModel.solve(resonance_n[n8], vin)); + } } } public: - unsigned short** getGainVol() { return gain_vol; } - unsigned short** getGainRes() { return gain_res; } + unsigned short** getVolume() { return volume; } + unsigned short** getResonance() { return resonance; } unsigned short** getSummer() { return summer; } unsigned short** getMixer() { return mixer; } - /** - * The digital range of one voice is 20 bits; create a scaling term - * for multiplication which fits in 11 bits. - */ - int getVoiceScaleS11() const { return static_cast((norm * ((1 << 11) - 1)) * voice_voltage_range); } - - /** - * The "zero" output level of the voices. - */ - int getNormalizedVoiceDC() const { return static_cast(N16 * (voice_DC_voltage - vmin)); } - inline unsigned short getOpampRev(int i) const { return opamp_rev[i]; } inline double getVddt() const { return Vddt; } inline double getVth() const { return Vth; } // helper functions + inline unsigned short getNormalizedValue(double value) const { const double tmp = N16 * (value - vmin); - assert(tmp > -0.5 && tmp < 65535.5); - return static_cast(tmp + 0.5); + assert(tmp >= 0. && tmp <= 65535.); + return static_cast(tmp + rnd.getNoise()); } inline unsigned short getNormalizedCurrentFactor(double wl) const @@ -153,11 +276,17 @@ public: return static_cast(tmp + 0.5); } - inline unsigned short getNVmin() const { + inline unsigned short getNVmin() const + { const double tmp = N16 * vmin; assert(tmp > -0.5 && tmp < 65535.5); return static_cast(tmp + 0.5); } + + inline int getNormalizedVoice(float value, unsigned int env) const + { + return static_cast(getNormalizedValue(getVoiceVoltage(value, env))); + } }; } // namespace reSIDfp diff --git a/src/sound/resid-fp/FilterModelConfig6581.cpp b/src/sound/resid-fp/FilterModelConfig6581.cpp index 143b9e91c..fcbf32a46 100644 --- a/src/sound/resid-fp/FilterModelConfig6581.cpp +++ b/src/sound/resid-fp/FilterModelConfig6581.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2023 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2010 Dag Lem * @@ -22,28 +22,20 @@ #include "FilterModelConfig6581.h" -#include - #include "Integrator6581.h" #include "OpAmp.h" +#include "sidcxx11.h" + +#include +#include +#include +#include + namespace reSIDfp { -#ifndef HAVE_CXX11 -/** - * Compute log(1+x) without losing precision for small values of x - * - * @note when compiling with -ffastm-math the compiler will - * optimize the expression away leaving a plain log(1. + x) - */ -inline double log1p(double x) -{ - return log(1. + x) - (((1. + x) - 1.) - x) / (1. + x); -} -#endif - -const unsigned int OPAMP_SIZE = 33; +constexpr unsigned int OPAMP_SIZE = 33; /** * This is the SID 6581 op-amp voltage transfer function, measured on @@ -51,7 +43,7 @@ const unsigned int OPAMP_SIZE = 33; * All measured chips have op-amps with output voltages (and thus input * voltages) within the range of 0.81V - 10.31V. */ -const Spline::Point opamp_voltage[OPAMP_SIZE] = +constexpr Spline::Point opamp_voltage[OPAMP_SIZE] = { { 0.81, 10.31 }, // Approximate start of actual range { 2.40, 10.31 }, @@ -90,8 +82,12 @@ const Spline::Point opamp_voltage[OPAMP_SIZE] = std::unique_ptr FilterModelConfig6581::instance(nullptr); +std::mutex Instance6581_Lock; + FilterModelConfig6581* FilterModelConfig6581::getInstance() { + std::lock_guard lock(Instance6581_Lock); + if (!instance.get()) { instance.reset(new FilterModelConfig6581()); @@ -100,14 +96,32 @@ FilterModelConfig6581* FilterModelConfig6581::getInstance() return instance.get(); } +void FilterModelConfig6581::setFilterRange(double adjustment) +{ + // clamp into allowed range +#ifdef HAVE_CXX17 + adjustment = std::clamp(adjustment, 0.0, 1.0); +#else + adjustment = std::max(std::min(adjustment, 1.0), 0.); +#endif + + // Get the new uCox value, in the range [1,40] + const double new_uCox = (1. + 39. * adjustment) * 1e-6; + + // Ignore small changes + if (std::abs(uCox - new_uCox) < 1e-12) + return; + + setUCox(new_uCox); +} + FilterModelConfig6581::FilterModelConfig6581() : FilterModelConfig( - 1.5, // voice voltage range - 5.075, // voice DC voltage - 470e-12, // capacitor value - 12.18, // Vdd - 1.31, // Vth - 20e-6, // uCox + 1.5, // voice voltage range FIXME should theoretically be ~3,571V + 470e-12, // capacitor value + 12. * VOLTAGE_SKEW, // Vdd + 1.31, // Vth + 20e-6, // uCox opamp_voltage, OPAMP_SIZE ), @@ -119,190 +133,144 @@ FilterModelConfig6581::FilterModelConfig6581() : { dac.kinkedDac(MOS6581); - // Create lookup tables for gains / summers. - -#ifndef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); -#endif - -// #pragma omp parallel sections { -// #pragma omp section + Dac envDac(8); + envDac.kinkedDac(MOS6581); + for(int i=0; i<256; i++) { -#ifdef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); -#endif - // The filter summer operates at n ~ 1, and has 5 fundamentally different - // input configurations (2 - 6 input "resistors"). - // - // Note that all "on" transistors are modeled as one. This is not - // entirely accurate, since the input for each transistor is different, - // and transistors are not linear components. However modeling all - // transistors separately would be extremely costly. - for (int i = 0; i < 5; i++) - { - const int idiv = 2 + i; // 2 - 6 input "resistors". - const int size = idiv << 16; - const double n = idiv; - opampModel.reset(); - summer[i] = new unsigned short[size]; - - for (int vi = 0; vi < size; vi++) - { - const double vin = vmin + vi / N16 / idiv; /* vmin .. vmax */ - summer[i][vi] = getNormalizedValue(opampModel.solve(n, vin)); - } - } - } - -// #pragma omp section - { -#ifdef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); -#endif - // The audio mixer operates at n ~ 8/6, and has 8 fundamentally different - // input configurations (0 - 7 input "resistors"). - // - // All "on", transistors are modeled as one - see comments above for - // the filter summer. - for (int i = 0; i < 8; i++) - { - const int idiv = (i == 0) ? 1 : i; - const int size = (i == 0) ? 1 : i << 16; - const double n = i * 8.0 / 6.0; - opampModel.reset(); - mixer[i] = new unsigned short[size]; - - for (int vi = 0; vi < size; vi++) - { - const double vin = vmin + vi / N16 / idiv; /* vmin .. vmax */ - mixer[i][vi] = getNormalizedValue(opampModel.solve(n, vin)); - } - } - } - -// #pragma omp section - { -#ifdef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); -#endif - // 4 bit "resistor" ladders in the audio output gain - // necessitate 16 gain tables. - // From die photographs of the volume "resistor" ladders - // it follows that gain ~ vol/12 (assuming ideal - // op-amps and ideal "resistors"). - for (int n8 = 0; n8 < 16; n8++) - { - const int size = 1 << 16; - const double n = n8 / 12.0; - opampModel.reset(); - gain_vol[n8] = new unsigned short[size]; - - for (int vi = 0; vi < size; vi++) - { - const double vin = vmin + vi / N16; /* vmin .. vmax */ - gain_vol[n8][vi] = getNormalizedValue(opampModel.solve(n, vin)); - } - } - } - -// #pragma omp section - { -#ifdef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); -#endif - // 4 bit "resistor" ladders in the bandpass resonance gain - // necessitate 16 gain tables. - // From die photographs of the bandpass "resistor" ladders - // it follows that 1/Q ~ ~res/8 (assuming ideal - // op-amps and ideal "resistors"). - for (int n8 = 0; n8 < 16; n8++) - { - const int size = 1 << 16; - const double n = (~n8 & 0xf) / 8.0; - opampModel.reset(); - gain_res[n8] = new unsigned short[size]; - - for (int vi = 0; vi < size; vi++) - { - const double vin = vmin + vi / N16; /* vmin .. vmax */ - gain_res[n8][vi] = getNormalizedValue(opampModel.solve(n, vin)); - } - } - } - -// #pragma omp section - { - const double nVddt = N16 * (Vddt - vmin); - - for (unsigned int i = 0; i < (1 << 16); i++) - { - // The table index is right-shifted 16 times in order to fit in - // 16 bits; the argument to sqrt is thus multiplied by (1 << 16). - const double tmp = nVddt - sqrt(static_cast(i << 16)); - assert(tmp > -0.5 && tmp < 65535.5); - vcr_nVg[i] = static_cast(tmp + 0.5); - } - } - -// #pragma omp section - { - // EKV model: - // - // Ids = Is * (if - ir) - // Is = (2 * u*Cox * Ut^2)/k * W/L - // if = ln^2(1 + e^((k*(Vg - Vt) - Vs)/(2*Ut)) - // ir = ln^2(1 + e^((k*(Vg - Vt) - Vd)/(2*Ut)) - - // moderate inversion characteristic current - const double Is = (2. * uCox * Ut * Ut) * WL_vcr; - - // Normalized current factor for 1 cycle at 1MHz. - const double N15 = norm * ((1 << 15) - 1); - const double n_Is = N15 * 1.0e-6 / C * Is; - - // kVgt_Vx = k*(Vg - Vt) - Vx - // I.e. if k != 1.0, Vg must be scaled accordingly. - for (int kVgt_Vx = 0; kVgt_Vx < (1 << 16); kVgt_Vx++) - { - const double log_term = log1p(exp((kVgt_Vx / N16) / (2. * Ut))); - // Scaled by m*2^15 - const double tmp = n_Is * log_term * log_term; - assert(tmp > -0.5 && tmp < 65535.5); - vcr_n_Ids_term[kVgt_Vx] = static_cast(tmp + 0.5); - } + const double envI = envDac.getOutput(i); + voiceDC[i] = 5. * VOLTAGE_SKEW + (0.2143 * envI); } } + + // Create lookup tables for gains / summers. + + // + // We spawn six threads to calculate these tables in parallel + // + auto filterSummer = [this] + { + OpAmp opampModel( + std::vector( + std::begin(opamp_voltage), + std::end(opamp_voltage)), + Vddt, + vmin, + vmax); + + buildSummerTable(opampModel); + }; + + auto filterMixer = [this] + { + OpAmp opampModel( + std::vector( + std::begin(opamp_voltage), + std::end(opamp_voltage)), + Vddt, + vmin, + vmax); + + buildMixerTable(opampModel, 8.0 / 6.0); + }; + + auto filterGain = [this] + { + OpAmp opampModel( + std::vector( + std::begin(opamp_voltage), + std::end(opamp_voltage)), + Vddt, + vmin, + vmax); + + buildVolumeTable(opampModel, 12.0); + }; + + auto filterResonance = [this] + { + OpAmp opampModel( + std::vector( + std::begin(opamp_voltage), + std::end(opamp_voltage)), + Vddt, + vmin, + vmax); + + // build temp n table + double resonance_n[16]; + for (int n8 = 0; n8 < 16; n8++) + { + resonance_n[n8] = (~n8 & 0xf) / 8.0; + } + + buildResonanceTable(opampModel, resonance_n); + }; + + auto filterVcrVg = [this] + { + const double nVddt = N16 * (Vddt - vmin); + + for (unsigned int i = 0; i < (1 << 16); i++) + { + // The table index is right-shifted 16 times in order to fit in + // 16 bits; the argument to sqrt is thus multiplied by (1 << 16). + const double tmp = nVddt - std::sqrt(static_cast(i << 16)); + assert(tmp > -0.5 && tmp < 65535.5); + vcr_nVg[i] = static_cast(tmp + 0.5); + } + }; + + auto filterVcrIds = [this] + { + // EKV model: + // + // Ids = Is * (if - ir) + // Is = (2 * u*Cox * Ut^2)/k * W/L + // if = ln^2(1 + e^((k*(Vg - Vt) - Vs)/(2*Ut)) + // ir = ln^2(1 + e^((k*(Vg - Vt) - Vd)/(2*Ut)) + + // moderate inversion characteristic current + // will be multiplied by uCox later + const double Is = (2. * Ut * Ut) * WL_vcr; + + // Normalized current factor for 1 cycle at 1MHz. + const double N15 = norm * ((1 << 15) - 1); + const double n_Is = N15 * 1.0e-6 / C * Is; + + // kVgt_Vx = k*(Vg - Vt) - Vx + // I.e. if k != 1.0, Vg must be scaled accordingly. + const double r_N16_2Ut = 1.0 / (N16 * 2.0 * Ut); + for (int i = 0; i < (1 << 16); i++) + { + const int kVgt_Vx = i - (1 << 15); + const double log_term = std::log1p(std::exp(kVgt_Vx * r_N16_2Ut)); + // Scaled by m*2^15 + vcr_n_Ids_term[i] = n_Is * log_term * log_term; + } + }; + +#if defined(HAVE_CXX20) && defined(__cpp_lib_jthread) + using sidThread = std::jthread; +#else + using sidThread = std::thread; +#endif + + sidThread thdSummer(filterSummer); + sidThread thdMixer(filterMixer); + sidThread thdGain(filterGain); + sidThread thdResonance(filterResonance); + sidThread thdVcrVg(filterVcrVg); + sidThread thdVcrIds(filterVcrIds); + +#if !defined(HAVE_CXX20) || !defined(__cpp_lib_jthread) + thdSummer.join(); + thdMixer.join(); + thdGain.join(); + thdResonance.join(); + thdVcrVg.join(); + thdVcrIds.join(); +#endif } unsigned short* FilterModelConfig6581::getDAC(double adjustment) const @@ -314,15 +282,10 @@ unsigned short* FilterModelConfig6581::getDAC(double adjustment) const for (unsigned int i = 0; i < (1 << DAC_BITS); i++) { const double fcd = dac.getOutput(i); - f0_dac[i] = getNormalizedValue(dac_zero + fcd * dac_scale / (1 << DAC_BITS)); + f0_dac[i] = getNormalizedValue(dac_zero + fcd * dac_scale); } return f0_dac; } -std::unique_ptr FilterModelConfig6581::buildIntegrator() -{ - return MAKE_UNIQUE(Integrator6581, this, WL_snake); -} - } // namespace reSIDfp diff --git a/src/sound/resid-fp/FilterModelConfig6581.h b/src/sound/resid-fp/FilterModelConfig6581.h index 06fcc5ce8..75a52dadb 100644 --- a/src/sound/resid-fp/FilterModelConfig6581.h +++ b/src/sound/resid-fp/FilterModelConfig6581.h @@ -41,17 +41,18 @@ class Integrator6581; */ class FilterModelConfig6581 final : public FilterModelConfig { -private: - static const unsigned int DAC_BITS = 11; - private: static std::unique_ptr instance; // This allows access to the private constructor -#ifdef HAVE_CXX11 friend std::unique_ptr::deleter_type; -#else - friend class std::auto_ptr; -#endif + +private: + static constexpr unsigned int DAC_BITS = 11; + + /** + * Power bricks generate voltages slightly out of spec + */ + static constexpr double VOLTAGE_SKEW = 1.015; /// Transistor parameters. //@{ @@ -68,21 +69,36 @@ private: /// DAC lookup table Dac dac; - /// VCR - 6581 only. + /// Voltage Controlled Resistors //@{ unsigned short vcr_nVg[1 << 16]; - unsigned short vcr_n_Ids_term[1 << 16]; + double vcr_n_Ids_term[1 << 16]; //@} + // Voice DC offset LUT + double voiceDC[256]; + private: double getDacZero(double adjustment) const { return dac_zero + (1. - adjustment); } FilterModelConfig6581(); - ~FilterModelConfig6581() DEFAULT; + ~FilterModelConfig6581() = default; + +protected: + /** + * On 6581 the DC offset varies between ~5.0V and ~5.214V depending on + * the envelope value. + */ + inline double getVoiceDC(unsigned int env) const override + { + return voiceDC[env]; + } public: static FilterModelConfig6581* getInstance(); + void setFilterRange(double adjustment); + /** * Construct an 11 bit cutoff frequency DAC output voltage table. * Ownership is transferred to the requester which becomes responsible @@ -93,17 +109,17 @@ public: */ unsigned short* getDAC(double adjustment) const; - /** - * Construct an integrator solver. - * - * @return the integrator - */ - std::unique_ptr buildIntegrator(); + inline double getWL_snake() const { return WL_snake; } inline unsigned short getVcr_nVg(int i) const { return vcr_nVg[i]; } - inline unsigned short getVcr_n_Ids_term(int i) const { return vcr_n_Ids_term[i]; } + inline unsigned short getVcr_n_Ids_term(int i) const + { + const double tmp = vcr_n_Ids_term[i] * uCox; + assert(tmp > -0.5 && tmp < 65535.5); + return static_cast(tmp + 0.5); + } // only used if SLOPE_FACTOR is defined - inline double getUt() const { return Ut; } + inline constexpr double getUt() const { return Ut; } inline double getN16() const { return N16; } }; diff --git a/src/sound/resid-fp/FilterModelConfig8580.cpp b/src/sound/resid-fp/FilterModelConfig8580.cpp index e838a366c..a0a0c9ad8 100644 --- a/src/sound/resid-fp/FilterModelConfig8580.cpp +++ b/src/sound/resid-fp/FilterModelConfig8580.cpp @@ -25,6 +25,10 @@ #include "Integrator8580.h" #include "OpAmp.h" +#include "sidcxx11.h" + +#include +#include namespace reSIDfp { @@ -57,7 +61,7 @@ namespace reSIDfp * E Rf|R2 RC * F Rf|R3 RC */ -const double resGain[16] = +constexpr double resGain[16] = { 1.4/1.0, // Rf/Ri 1.4 ((1.4*15.3)/(1.4+15.3))/1.0, // (Rf|R1)/Ri 1.28263 @@ -77,13 +81,13 @@ const double resGain[16] = ((1.4*4.7)/(1.4+4.7))/2.8, // (Rf|R3)/RC 0.385246 }; -const unsigned int OPAMP_SIZE = 21; +constexpr unsigned int OPAMP_SIZE = 21; /** * This is the SID 8580 op-amp voltage transfer function, measured on * CAP1B/CAP1A on a chip marked CSG 8580R5 1690 25. */ -const Spline::Point opamp_voltage[OPAMP_SIZE] = +constexpr Spline::Point opamp_voltage[OPAMP_SIZE] = { { 1.30, 8.91 }, // Approximate start of actual range { 4.76, 8.91 }, @@ -110,8 +114,12 @@ const Spline::Point opamp_voltage[OPAMP_SIZE] = std::unique_ptr FilterModelConfig8580::instance(nullptr); +std::mutex Instance8580_Lock; + FilterModelConfig8580* FilterModelConfig8580::getInstance() { + std::lock_guard lock(Instance8580_Lock); + if (!instance.get()) { instance.reset(new FilterModelConfig8580()); @@ -122,161 +130,89 @@ FilterModelConfig8580* FilterModelConfig8580::getInstance() FilterModelConfig8580::FilterModelConfig8580() : FilterModelConfig( - 0.30, // voice voltage range FIXME measure - 4.84, // voice DC voltage FIXME measure - 22e-9, // capacitor value - 9.09, // Vdd - 0.80, // Vth - 100e-6, // uCox + 0.24, // voice voltage range FIXME should theoretically be ~0,474V + 22e-9, // capacitor value + 9. * VOLTAGE_SKEW, // Vdd + 0.80, // Vth + 100e-6, // uCox opamp_voltage, OPAMP_SIZE ) { // Create lookup tables for gains / summers. -#ifndef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); -#endif -// #pragma omp parallel sections + // + // We spawn four threads to calculate these tables in parallel + // + auto filterSummer = [this] { -// #pragma omp section - { -#ifdef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); + OpAmp opampModel( + std::vector( + std::begin(opamp_voltage), + std::end(opamp_voltage)), + Vddt, + vmin, + vmax); + + buildSummerTable(opampModel); + }; + + auto filterMixer = [this] + { + OpAmp opampModel( + std::vector( + std::begin(opamp_voltage), + std::end(opamp_voltage)), + Vddt, + vmin, + vmax); + + buildMixerTable(opampModel, 8.0 / 5.0); + }; + + auto filterGain = [this] + { + OpAmp opampModel( + std::vector( + std::begin(opamp_voltage), + std::end(opamp_voltage)), + Vddt, + vmin, + vmax); + + buildVolumeTable(opampModel, 16.0); + }; + + auto filterResonance = [this] + { + OpAmp opampModel( + std::vector( + std::begin(opamp_voltage), + std::end(opamp_voltage)), + Vddt, + vmin, + vmax); + + buildResonanceTable(opampModel, resGain); + }; + +#if defined(HAVE_CXX20) && defined(__cpp_lib_jthread) + using sidThread = std::jthread; +#else + using sidThread = std::thread; #endif - // The filter summer operates at n ~ 1, and has 5 fundamentally different - // input configurations (2 - 6 input "resistors"). - // - // Note that all "on" transistors are modeled as one. This is not - // entirely accurate, since the input for each transistor is different, - // and transistors are not linear components. However modeling all - // transistors separately would be extremely costly. - for (int i = 0; i < 5; i++) - { - const int idiv = 2 + i; // 2 - 6 input "resistors". - const int size = idiv << 16; - const double n = idiv; - opampModel.reset(); - summer[i] = new unsigned short[size]; - for (int vi = 0; vi < size; vi++) - { - const double vin = vmin + vi / N16 / idiv; /* vmin .. vmax */ - summer[i][vi] = getNormalizedValue(opampModel.solve(n, vin)); - } - } - } + sidThread thdSummer(filterSummer); + sidThread thdMixer(filterMixer); + sidThread thdGain(filterGain); + sidThread thdResonance(filterResonance); -// #pragma omp section - { -#ifdef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); +#if !defined(HAVE_CXX20) || !defined(__cpp_lib_jthread) + thdSummer.join(); + thdMixer.join(); + thdGain.join(); + thdResonance.join(); #endif - // The audio mixer operates at n ~ 8/5, and has 8 fundamentally different - // input configurations (0 - 7 input "resistors"). - // - // All "on", transistors are modeled as one - see comments above for - // the filter summer. - for (int i = 0; i < 8; i++) - { - const int idiv = (i == 0) ? 1 : i; - const int size = (i == 0) ? 1 : i << 16; - const double n = i * 8.0 / 5.0; - opampModel.reset(); - mixer[i] = new unsigned short[size]; - - for (int vi = 0; vi < size; vi++) - { - const double vin = vmin + vi / N16 / idiv; /* vmin .. vmax */ - mixer[i][vi] = getNormalizedValue(opampModel.solve(n, vin)); - } - } - } - -// #pragma omp section - { -#ifdef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); -#endif - // 4 bit "resistor" ladders in the audio output gain - // necessitate 16 gain tables. - // From die photographs of the volume "resistor" ladders - // it follows that gain ~ vol/16 (assuming ideal - // op-amps and ideal "resistors"). - for (int n8 = 0; n8 < 16; n8++) - { - const int size = 1 << 16; - const double n = n8 / 16.0; - opampModel.reset(); - gain_vol[n8] = new unsigned short[size]; - - for (int vi = 0; vi < size; vi++) - { - const double vin = vmin + vi / N16; /* vmin .. vmax */ - gain_vol[n8][vi] = getNormalizedValue(opampModel.solve(n, vin)); - } - } - } - -// #pragma omp section - { -#ifdef _OPENMP - OpAmp opampModel( - std::vector( - std::begin(opamp_voltage), - std::end(opamp_voltage)), - Vddt, - vmin, - vmax); -#endif - // 4 bit "resistor" ladders in the bandpass resonance gain - // necessitate 16 gain tables. - // From die photographs of the bandpass "resistor" ladders - // it follows that 1/Q ~ 2^((4 - res)/8) (assuming ideal - // op-amps and ideal "resistors"). - for (int n8 = 0; n8 < 16; n8++) - { - const int size = 1 << 16; - opampModel.reset(); - gain_res[n8] = new unsigned short[size]; - - for (int vi = 0; vi < size; vi++) - { - const double vin = vmin + vi / N16; /* vmin .. vmax */ - gain_res[n8][vi] = getNormalizedValue(opampModel.solve(resGain[n8], vin)); - } - } - } - } -} - -std::unique_ptr FilterModelConfig8580::buildIntegrator() -{ - return MAKE_UNIQUE(Integrator8580, this); } } // namespace reSIDfp diff --git a/src/sound/resid-fp/FilterModelConfig8580.h b/src/sound/resid-fp/FilterModelConfig8580.h index 509171bc3..72a055929 100644 --- a/src/sound/resid-fp/FilterModelConfig8580.h +++ b/src/sound/resid-fp/FilterModelConfig8580.h @@ -42,25 +42,30 @@ class FilterModelConfig8580 final : public FilterModelConfig private: static std::unique_ptr instance; // This allows access to the private constructor -#ifdef HAVE_CXX11 friend std::unique_ptr::deleter_type; -#else - friend class std::auto_ptr; -#endif + +private: + /** + * Reference voltage generated from Vcc by a voltage divider + */ + static constexpr double Vref = 4.75; + + /** + * Power bricks generate voltages slightly out of spec + */ + static constexpr double VOLTAGE_SKEW = 1.01; private: FilterModelConfig8580(); - ~FilterModelConfig8580() DEFAULT; + ~FilterModelConfig8580() = default; + +protected: + inline double getVoiceDC(unsigned int) const override { return getVref(); } public: static FilterModelConfig8580* getInstance(); - /** - * Construct an integrator solver. - * - * @return the integrator - */ - std::unique_ptr buildIntegrator(); + inline constexpr double getVref() const { return Vref * VOLTAGE_SKEW; } }; } // namespace reSIDfp diff --git a/src/sound/resid-fp/Integrator.h b/src/sound/resid-fp/Integrator.h new file mode 100644 index 000000000..e8b5ec0b7 --- /dev/null +++ b/src/sound/resid-fp/Integrator.h @@ -0,0 +1,47 @@ +/* + * This file is part of libsidplayfp, a SID player engine. + * + * Copyright 2011-2024 Leandro Nini + * Copyright 2007-2010 Antti Lankila + * Copyright 2004, 2010 Dag Lem + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef INTEGRATOR_H +#define INTEGRATOR_H + +namespace reSIDfp +{ + +class Integrator +{ +protected: + mutable int vx; + mutable int vc; + + Integrator() : + vx(0), + vc(0) {} + +public: + virtual int solve(int vi) const = 0; + + virtual ~Integrator() = default; +}; + +} // namespace reSIDfp + +#endif diff --git a/src/sound/resid-fp/Integrator6581.cpp b/src/sound/resid-fp/Integrator6581.cpp index 490be9b5c..0a48f5c49 100644 --- a/src/sound/resid-fp/Integrator6581.cpp +++ b/src/sound/resid-fp/Integrator6581.cpp @@ -18,8 +18,80 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define INTEGRATOR_CPP - #include "Integrator6581.h" -// This is needed when compiling with --disable-inline +#ifdef SLOPE_FACTOR +# include +# include "sidcxx11.h" +#endif + +namespace reSIDfp +{ + +int Integrator6581::solve(int vi) const +{ + // Make sure Vgst>0 so we're not in subthreshold mode + assert(vx < nVddt); + + // Check that transistor is actually in triode mode + // Vds < Vgs - Vth + assert(vi < nVddt); + + // "Snake" voltages for triode mode calculation. + const unsigned int Vgst = nVddt - vx; + const unsigned int Vgdt = nVddt - vi; + + const unsigned int Vgst_2 = Vgst * Vgst; + const unsigned int Vgdt_2 = Vgdt * Vgdt; + + // "Snake" current, scaled by (1/m)*2^13*m*2^16*m*2^16*2^-15 = m*2^30 + const int n_I_snake = fmc.getNormalizedCurrentFactor(wlSnake) * (static_cast(Vgst_2 - Vgdt_2) >> 15); + + // VCR gate voltage. // Scaled by m*2^16 + // Vg = Vddt - sqrt(((Vddt - Vw)^2 + Vgdt^2)/2) + const int nVg = static_cast(fmc.getVcr_nVg((nVddt_Vw_2 + (Vgdt_2 >> 1)) >> 16)); +#ifdef SLOPE_FACTOR + const double nVp = static_cast(nVg - nVt) / n; // Pinch-off voltage + const int kVgt = static_cast(nVp + 0.5) - nVmin; +#else + const int kVgt = (nVg - nVt) - nVmin; +#endif + + // VCR voltages for EKV model table lookup. + const int kVgt_Vs = (kVgt - vx) + (1 << 15); + assert((kVgt_Vs >= 0) && (kVgt_Vs < (1 << 16))); + const int kVgt_Vd = (kVgt - vi) + (1 << 15); + assert((kVgt_Vd >= 0) && (kVgt_Vd < (1 << 16))); + + // VCR current, scaled by m*2^15*2^15 = m*2^30 + const unsigned int If = static_cast(fmc.getVcr_n_Ids_term(kVgt_Vs)) << 15; + const unsigned int Ir = static_cast(fmc.getVcr_n_Ids_term(kVgt_Vd)) << 15; +#ifdef SLOPE_FACTOR + const double iVcr = static_cast(If - Ir); + const int n_I_vcr = static_cast(iVcr * n); +#else + const int n_I_vcr = If - Ir; +#endif + +#ifdef SLOPE_FACTOR + // estimate new slope factor based on gate voltage + constexpr double gamma = 1.0; // body effect factor + constexpr double phi = 0.8; // bulk Fermi potential + const double Vp = nVp / fmc.getN16(); + n = 1. + (gamma / (2. * std::sqrt(Vp + phi + 4. * fmc.getUt()))); + assert((n > 1.2) && (n < 1.8)); +#endif + + // Change in capacitor charge. + vc += n_I_snake + n_I_vcr; + + // vx = g(vc) + const int tmp = (vc >> 15) + (1 << 15); + assert(tmp < (1 << 16)); + vx = fmc.getOpampRev(tmp); + + // Return vo. + return vx - (vc >> 14); +} + +} // namespace reSIDfp diff --git a/src/sound/resid-fp/Integrator6581.h b/src/sound/resid-fp/Integrator6581.h index 5bdeca37d..71db37342 100644 --- a/src/sound/resid-fp/Integrator6581.h +++ b/src/sound/resid-fp/Integrator6581.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2022 Leandro Nini + * Copyright 2011-2023 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004, 2010 Dag Lem * @@ -23,6 +23,7 @@ #ifndef INTEGRATOR6581_H #define INTEGRATOR6581_H +#include "Integrator.h" #include "FilterModelConfig6581.h" #include @@ -33,10 +34,6 @@ // actually produces worse results, needs investigation //#define SLOPE_FACTOR -#ifdef SLOPE_FACTOR -# include -#endif - #include "siddefs-fp.h" namespace reSIDfp @@ -164,12 +161,10 @@ namespace reSIDfp * * Vg = nVddt - sqrt(((nVddt - vi)^2 + (nVddt - Vw)^2)/2) */ -class Integrator6581 +class Integrator6581 : public Integrator { private: - unsigned int nVddt_Vw_2; - mutable int vx; - mutable int vc; + const double wlSnake; #ifdef SLOPE_FACTOR // Slope factor n = 1/k @@ -177,109 +172,32 @@ private: // k = Cox/(Cox+Cdep) ~ 0.7 (depends on gate voltage) mutable double n; #endif + + unsigned int nVddt_Vw_2; + const unsigned short nVddt; const unsigned short nVt; const unsigned short nVmin; - const unsigned short nSnake; - const FilterModelConfig6581* fmc; + FilterModelConfig6581& fmc; public: - Integrator6581(const FilterModelConfig6581* fmc, - double WL_snake) : - nVddt_Vw_2(0), - vx(0), - vc(0), + Integrator6581(FilterModelConfig6581& fmc) : + wlSnake(fmc.getWL_snake()), #ifdef SLOPE_FACTOR n(1.4), #endif - nVddt(fmc->getNormalizedValue(fmc->getVddt())), - nVt(fmc->getNormalizedValue(fmc->getVth())), - nVmin(fmc->getNVmin()), - nSnake(fmc->getNormalizedCurrentFactor(WL_snake)), + nVddt_Vw_2(0), + nVddt(fmc.getNormalizedValue(fmc.getVddt())), + nVt(fmc.getNormalizedValue(fmc.getVth())), + nVmin(fmc.getNVmin()), fmc(fmc) {} void setVw(unsigned short Vw) { nVddt_Vw_2 = ((nVddt - Vw) * (nVddt - Vw)) >> 1; } - int solve(int vi) const; + int solve(int vi) const override; }; } // namespace reSIDfp -#if RESID_INLINING || defined(INTEGRATOR_CPP) - -namespace reSIDfp -{ - -RESID_INLINE -int Integrator6581::solve(int vi) const -{ - // Make sure Vgst>0 so we're not in subthreshold mode - assert(vx < nVddt); - - // Check that transistor is actually in triode mode - // Vds < Vgs - Vth - assert(vi < nVddt); - - // "Snake" voltages for triode mode calculation. - const unsigned int Vgst = nVddt - vx; - const unsigned int Vgdt = nVddt - vi; - - const unsigned int Vgst_2 = Vgst * Vgst; - const unsigned int Vgdt_2 = Vgdt * Vgdt; - - // "Snake" current, scaled by (1/m)*2^13*m*2^16*m*2^16*2^-15 = m*2^30 - const int n_I_snake = nSnake * (static_cast(Vgst_2 - Vgdt_2) >> 15); - - // VCR gate voltage. // Scaled by m*2^16 - // Vg = Vddt - sqrt(((Vddt - Vw)^2 + Vgdt^2)/2) - const int nVg = static_cast(fmc->getVcr_nVg((nVddt_Vw_2 + (Vgdt_2 >> 1)) >> 16)); -#ifdef SLOPE_FACTOR - const double nVp = static_cast(nVg - nVt) / n; // Pinch-off voltage - const int kVgt = static_cast(nVp + 0.5) - nVmin; -#else - const int kVgt = (nVg - nVt) - nVmin; -#endif - - // VCR voltages for EKV model table lookup. - const int kVgt_Vs = (vx < kVgt) ? kVgt - vx : 0; - assert(kVgt_Vs < (1 << 16)); - const int kVgt_Vd = (vi < kVgt) ? kVgt - vi : 0; - assert(kVgt_Vd < (1 << 16)); - - // VCR current, scaled by m*2^15*2^15 = m*2^30 - const unsigned int If = static_cast(fmc->getVcr_n_Ids_term(kVgt_Vs)) << 15; - const unsigned int Ir = static_cast(fmc->getVcr_n_Ids_term(kVgt_Vd)) << 15; -#ifdef SLOPE_FACTOR - const double iVcr = static_cast(If - Ir); - const int n_I_vcr = static_cast(iVcr * n); -#else - const int n_I_vcr = If - Ir; -#endif - -#ifdef SLOPE_FACTOR - // estimate new slope factor based on gate voltage - const double gamma = 1.0; // body effect factor - const double phi = 0.8; // bulk Fermi potential - const double Vp = nVp / fmc->getN16(); - n = 1. + (gamma / (2. * sqrt(Vp + phi + 4. * fmc->getUt()))); - assert((n > 1.2) && (n < 1.8)); -#endif - - // Change in capacitor charge. - vc += n_I_snake + n_I_vcr; - - // vx = g(vc) - const int tmp = (vc >> 15) + (1 << 15); - assert(tmp < (1 << 16)); - vx = fmc->getOpampRev(tmp); - - // Return vo. - return vx - (vc >> 14); -} - -} // namespace reSIDfp - -#endif - #endif diff --git a/src/sound/resid-fp/Integrator8580.cpp b/src/sound/resid-fp/Integrator8580.cpp index 6fba9521b..762442d92 100644 --- a/src/sound/resid-fp/Integrator8580.cpp +++ b/src/sound/resid-fp/Integrator8580.cpp @@ -18,8 +18,36 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define INTEGRATOR8580_CPP - #include "Integrator8580.h" -// This is needed when compiling with --disable-inline +namespace reSIDfp +{ + +int Integrator8580::solve(int vi) const +{ + // Make sure we're not in subthreshold mode + assert(vx < nVgt); + + // DAC voltages + const unsigned int Vgst = nVgt - vx; + const unsigned int Vgdt = (vi < nVgt) ? nVgt - vi : 0; // triode/saturation mode + + const unsigned int Vgst_2 = Vgst * Vgst; + const unsigned int Vgdt_2 = Vgdt * Vgdt; + + // DAC current, scaled by (1/m)*2^13*m*2^16*m*2^16*2^-15 = m*2^30 + const int n_I_dac = n_dac * (static_cast(Vgst_2 - Vgdt_2) >> 15); + + // Change in capacitor charge. + vc += n_I_dac; + + // vx = g(vc) + const int tmp = (vc >> 15) + (1 << 15); + assert(tmp < (1 << 16)); + vx = fmc.getOpampRev(tmp); + + // Return vo. + return vx - (vc >> 14); +} + +} // namespace reSIDfp diff --git a/src/sound/resid-fp/Integrator8580.h b/src/sound/resid-fp/Integrator8580.h index db9e46b05..857d22ca2 100644 --- a/src/sound/resid-fp/Integrator8580.h +++ b/src/sound/resid-fp/Integrator8580.h @@ -23,6 +23,7 @@ #ifndef INTEGRATOR8580_H #define INTEGRATOR8580_H +#include "Integrator.h" #include "FilterModelConfig8580.h" #include @@ -51,21 +52,16 @@ namespace reSIDfp * * Rfc gate voltage is generated by an OP Amp and depends on chip temperature. */ -class Integrator8580 +class Integrator8580 : public Integrator { private: - mutable int vx; - mutable int vc; - unsigned short nVgt; unsigned short n_dac; - const FilterModelConfig8580* fmc; + FilterModelConfig8580& fmc; public: - Integrator8580(const FilterModelConfig8580* fmc) : - vx(0), - vc(0), + Integrator8580(FilterModelConfig8580& fmc) : fmc(fmc) { setV(1.5); @@ -78,7 +74,7 @@ public: { // Normalized current factor, 1 cycle at 1MHz. // Fit in 5 bits. - n_dac = fmc->getNormalizedCurrentFactor(wl); + n_dac = fmc.getNormalizedCurrentFactor(wl); } /** @@ -87,56 +83,19 @@ public: void setV(double v) { // Gate voltage is controlled by the switched capacitor voltage divider - // Ua = Ue * v = 4.76v 1 1.0 && v < 2.0); - const double Vg = 4.76 * v; - const double Vgt = Vg - fmc->getVth(); + const double Vg = fmc.getVref() * v; + const double Vgt = Vg - fmc.getVth(); // Vg - Vth, normalized so that translated values can be subtracted: // Vgt - x = (Vgt - t) - (x - t) - nVgt = fmc->getNormalizedValue(Vgt); + nVgt = fmc.getNormalizedValue(Vgt); } - int solve(int vi) const; + int solve(int vi) const override; }; } // namespace reSIDfp -#if RESID_INLINING || defined(INTEGRATOR8580_CPP) - -namespace reSIDfp -{ - -RESID_INLINE -int Integrator8580::solve(int vi) const -{ - // Make sure we're not in subthreshold mode - assert(vx < nVgt); - - // DAC voltages - const unsigned int Vgst = nVgt - vx; - const unsigned int Vgdt = (vi < nVgt) ? nVgt - vi : 0; // triode/saturation mode - - const unsigned int Vgst_2 = Vgst * Vgst; - const unsigned int Vgdt_2 = Vgdt * Vgdt; - - // DAC current, scaled by (1/m)*2^13*m*2^16*m*2^16*2^-15 = m*2^30 - const int n_I_dac = n_dac * (static_cast(Vgst_2 - Vgdt_2) >> 15); - - // Change in capacitor charge. - vc += n_I_dac; - - // vx = g(vc) - const int tmp = (vc >> 15) + (1 << 15); - assert(tmp < (1 << 16)); - vx = fmc->getOpampRev(tmp); - - // Return vo. - return vx - (vc >> 14); -} - -} // namespace reSIDfp - -#endif - #endif diff --git a/src/sound/resid-fp/OpAmp.cpp b/src/sound/resid-fp/OpAmp.cpp index b26b2efcb..ed4f2700c 100644 --- a/src/sound/resid-fp/OpAmp.cpp +++ b/src/sound/resid-fp/OpAmp.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2015 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * * This program is free software; you can redistribute it and/or modify @@ -28,7 +28,7 @@ namespace reSIDfp { -const double EPSILON = 1e-8; +constexpr double EPSILON = 1e-8; double OpAmp::solve(double n, double vi) const { @@ -48,7 +48,7 @@ double OpAmp::solve(double n, double vi) const // Calculate f and df. - Spline::Point out = opamp->evaluate(x); + Spline::Point out = opamp.evaluate(x); const double vo = out.x; const double dvo = out.y; @@ -64,9 +64,9 @@ double OpAmp::solve(double n, double vi) const // Newton-Raphson step: xk1 = xk - f(xk)/f'(xk) x -= f / df; - if (unlikely(fabs(x - xk) < EPSILON)) + if (unlikely(std::fabs(x - xk) < EPSILON)) { - out = opamp->evaluate(x); + out = opamp.evaluate(x); return out.x; } diff --git a/src/sound/resid-fp/OpAmp.h b/src/sound/resid-fp/OpAmp.h index f048b1845..ec9d68cbb 100644 --- a/src/sound/resid-fp/OpAmp.h +++ b/src/sound/resid-fp/OpAmp.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2023 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004,2010 Dag Lem * @@ -23,7 +23,6 @@ #ifndef OPAMP_H #define OPAMP_H -#include #include #include "Spline.h" @@ -72,13 +71,13 @@ class OpAmp { private: /// Current root position (cached as guess to speed up next iteration) - mutable double x; + mutable double x = 0.; const double Vddt; const double vmin; const double vmax; - std::unique_ptr const opamp; + Spline opamp; public: /** @@ -89,14 +88,13 @@ public: * @param vmin * @param vmax */ - OpAmp(const std::vector &opamp, double Vddt, + OpAmp(const std::vector &opamp_voltages, double Vddt, double vmin, double vmax ) : - x(0.), Vddt(Vddt), vmin(vmin), vmax(vmax), - opamp(new Spline(opamp)) {} + opamp(opamp_voltages) {} /** * Reset root position diff --git a/src/sound/resid-fp/SID.cpp b/src/sound/resid-fp/SID.cpp index 840d264e2..5b5506bc3 100644 --- a/src/sound/resid-fp/SID.cpp +++ b/src/sound/resid-fp/SID.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2016 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004 Dag Lem * @@ -26,11 +26,12 @@ #include +#include "sidcxx11.h" + #include "array.h" #include "Dac.h" #include "Filter6581.h" #include "Filter8580.h" -#include "Potentiometer.h" #include "WaveformCalculator.h" #include "resample/TwoPassSincResampler.h" #include "resample/ZeroOrderResampler.h" @@ -38,8 +39,8 @@ namespace reSIDfp { -const unsigned int ENV_DAC_BITS = 8; -const unsigned int OSC_DAC_BITS = 12; +constexpr unsigned int ENV_DAC_BITS = 8; +constexpr unsigned int OSC_DAC_BITS = 12; /** * The waveform D/A converter introduces a DC offset in the signal @@ -106,8 +107,8 @@ const unsigned int OSC_DAC_BITS = 12; * On my 6581R4AR has 0x3A as the only value giving the same output level as 1.prg */ //@{ -unsigned int constexpr OFFSET_6581 = 0x380; -unsigned int constexpr OFFSET_8580 = 0x9c0; +constexpr unsigned int OFFSET_6581 = 0x380; +constexpr unsigned int OFFSET_8580 = 0x9c0; //@} /** @@ -128,31 +129,24 @@ unsigned int constexpr OFFSET_8580 = 0x9c0; * [2]: http://noname.c64.org/csdb/forums/?roomid=11&topicid=29025&showallposts=1 */ //@{ -int constexpr BUS_TTL_6581 = 0x01d00; -int constexpr BUS_TTL_8580 = 0xa2000; +constexpr int BUS_TTL_6581 = 0x01d00; +constexpr int BUS_TTL_8580 = 0xa2000; //@} SID::SID() : filter6581(new Filter6581()), filter8580(new Filter8580()), - externalFilter(new ExternalFilter()), resampler(nullptr), - potX(new Potentiometer()), - potY(new Potentiometer()) + cws(AVERAGE) { - voice[0].reset(new Voice()); - voice[1].reset(new Voice()); - voice[2].reset(new Voice()); - - muted[0] = muted[1] = muted[2] = false; - + setChipModel(MOS6581); reset(); - setChipModel(MOS8580); } SID::~SID() { - // Needed to delete auto_ptr with complete type + delete filter6581; + delete filter8580; } void SID::setFilter6581Curve(double filterCurve) @@ -160,6 +154,11 @@ void SID::setFilter6581Curve(double filterCurve) filter6581->setFilterCurve(filterCurve); } +void SID::setFilter6581Range(double adjustment) +{ + filter6581->setFilterRange(adjustment); +} + void SID::setFilter8580Curve(double filterCurve) { filter8580->setFilterCurve(filterCurve); @@ -178,7 +177,7 @@ void SID::voiceSync(bool sync) // Synchronize the 3 waveform generators. for (int i = 0; i < 3; i++) { - voice[i]->wave()->synchronize(voice[(i + 1) % 3]->wave(), voice[(i + 2) % 3]->wave()); + voice[i].wave()->synchronize(voice[(i + 1) % 3].wave(), voice[(i + 2) % 3].wave()); } } @@ -187,10 +186,10 @@ void SID::voiceSync(bool sync) for (int i = 0; i < 3; i++) { - WaveformGenerator* const wave = voice[i]->wave(); + WaveformGenerator* const wave = voice[i].wave(); const unsigned int freq = wave->readFreq(); - if (wave->readTest() || freq == 0 || !voice[(i + 1) % 3]->wave()->readSync()) + if (wave->readTest() || freq == 0 || !voice[(i + 1) % 3].wave()->readSync()) { continue; } @@ -210,12 +209,14 @@ void SID::setChipModel(ChipModel model) switch (model) { case MOS6581: - filter = filter6581.get(); + filter = filter6581; + scaleFactor = 3; modelTTL = BUS_TTL_6581; break; case MOS8580: - filter = filter8580.get(); + filter = filter8580; + scaleFactor = 5; modelTTL = BUS_TTL_8580; break; @@ -227,7 +228,7 @@ void SID::setChipModel(ChipModel model) // calculate waveform-related tables matrix_t* wavetables = WaveformCalculator::getInstance()->getWaveTable(); - matrix_t* pulldowntables = WaveformCalculator::getInstance()->buildPulldownTable(model); + matrix_t* pulldowntables = WaveformCalculator::getInstance()->buildPulldownTable(model, cws); // calculate envelope DAC table { @@ -247,7 +248,8 @@ void SID::setChipModel(ChipModel model) Dac dacBuilder(OSC_DAC_BITS); dacBuilder.kinkedDac(model); - const double offset = dacBuilder.getOutput(is6581 ? OFFSET_6581 : OFFSET_8580); + //const double offset = dacBuilder.getOutput(is6581 ? OFFSET_6581 : OFFSET_8580); + const double offset = dacBuilder.getOutput(0x7ff); for (unsigned int i = 0; i < (1 << OSC_DAC_BITS); i++) { @@ -259,11 +261,35 @@ void SID::setChipModel(ChipModel model) // set voice tables for (int i = 0; i < 3; i++) { - voice[i]->setEnvDAC(envDAC); - voice[i]->setWavDAC(oscDAC); - voice[i]->wave()->setModel(is6581); - voice[i]->wave()->setWaveformModels(wavetables); - voice[i]->wave()->setPulldownModels(pulldowntables); + voice[i].setEnvDAC(envDAC); + voice[i].setWavDAC(oscDAC); + voice[i].wave()->setModel(is6581); + voice[i].wave()->setWaveformModels(wavetables); + voice[i].wave()->setPulldownModels(pulldowntables); + } +} + +void SID::setCombinedWaveforms(CombinedWaveforms cws) +{ + switch (cws) + { + case AVERAGE: + case WEAK: + case STRONG: + break; + + default: + throw SIDError("Unknown combined waveforms type"); + } + + this->cws = cws; + + // rebuild waveform-related tables + matrix_t* pulldowntables = WaveformCalculator::getInstance()->buildPulldownTable(model, cws); + + for (int i = 0; i < 3; i++) + { + voice[i].wave()->setPulldownModels(pulldowntables); } } @@ -271,12 +297,12 @@ void SID::reset() { for (int i = 0; i < 3; i++) { - voice[i]->reset(); + voice[i].reset(); } filter6581->reset(); filter8580->reset(); - externalFilter->reset(); + externalFilter.reset(); if (resampler.get()) { @@ -299,22 +325,22 @@ unsigned char SID::read(int offset) switch (offset) { case 0x19: // X value of paddle - busValue = potX->readPOT(); + busValue = potX.readPOT(); busValueTtl = modelTTL; break; case 0x1a: // Y value of paddle - busValue = potY->readPOT(); + busValue = potY.readPOT(); busValueTtl = modelTTL; break; case 0x1b: // Voice #3 waveform output - busValue = voice[2]->wave()->readOSC(); + busValue = voice[2].wave()->readOSC(); busValueTtl = modelTTL; break; case 0x1c: // Voice #3 ADSR output - busValue = voice[2]->envelope()->readENV(); + busValue = voice[2].envelope()->readENV(); busValueTtl = modelTTL; break; @@ -337,87 +363,87 @@ void SID::write(int offset, unsigned char value) switch (offset) { case 0x00: // Voice #1 frequency (Low-byte) - voice[0]->wave()->writeFREQ_LO(value); + voice[0].wave()->writeFREQ_LO(value); break; case 0x01: // Voice #1 frequency (High-byte) - voice[0]->wave()->writeFREQ_HI(value); + voice[0].wave()->writeFREQ_HI(value); break; case 0x02: // Voice #1 pulse width (Low-byte) - voice[0]->wave()->writePW_LO(value); + voice[0].wave()->writePW_LO(value); break; case 0x03: // Voice #1 pulse width (bits #8-#15) - voice[0]->wave()->writePW_HI(value); + voice[0].wave()->writePW_HI(value); break; case 0x04: // Voice #1 control register - voice[0]->writeCONTROL_REG(muted[0] ? 0 : value); + voice[0].writeCONTROL_REG(value); break; case 0x05: // Voice #1 Attack and Decay length - voice[0]->envelope()->writeATTACK_DECAY(value); + voice[0].envelope()->writeATTACK_DECAY(value); break; case 0x06: // Voice #1 Sustain volume and Release length - voice[0]->envelope()->writeSUSTAIN_RELEASE(value); + voice[0].envelope()->writeSUSTAIN_RELEASE(value); break; case 0x07: // Voice #2 frequency (Low-byte) - voice[1]->wave()->writeFREQ_LO(value); + voice[1].wave()->writeFREQ_LO(value); break; case 0x08: // Voice #2 frequency (High-byte) - voice[1]->wave()->writeFREQ_HI(value); + voice[1].wave()->writeFREQ_HI(value); break; case 0x09: // Voice #2 pulse width (Low-byte) - voice[1]->wave()->writePW_LO(value); + voice[1].wave()->writePW_LO(value); break; case 0x0a: // Voice #2 pulse width (bits #8-#15) - voice[1]->wave()->writePW_HI(value); + voice[1].wave()->writePW_HI(value); break; case 0x0b: // Voice #2 control register - voice[1]->writeCONTROL_REG(muted[1] ? 0 : value); + voice[1].writeCONTROL_REG(value); break; case 0x0c: // Voice #2 Attack and Decay length - voice[1]->envelope()->writeATTACK_DECAY(value); + voice[1].envelope()->writeATTACK_DECAY(value); break; case 0x0d: // Voice #2 Sustain volume and Release length - voice[1]->envelope()->writeSUSTAIN_RELEASE(value); + voice[1].envelope()->writeSUSTAIN_RELEASE(value); break; case 0x0e: // Voice #3 frequency (Low-byte) - voice[2]->wave()->writeFREQ_LO(value); + voice[2].wave()->writeFREQ_LO(value); break; case 0x0f: // Voice #3 frequency (High-byte) - voice[2]->wave()->writeFREQ_HI(value); + voice[2].wave()->writeFREQ_HI(value); break; case 0x10: // Voice #3 pulse width (Low-byte) - voice[2]->wave()->writePW_LO(value); + voice[2].wave()->writePW_LO(value); break; case 0x11: // Voice #3 pulse width (bits #8-#15) - voice[2]->wave()->writePW_HI(value); + voice[2].wave()->writePW_HI(value); break; case 0x12: // Voice #3 control register - voice[2]->writeCONTROL_REG(muted[2] ? 0 : value); + voice[2].writeCONTROL_REG(value); break; case 0x13: // Voice #3 Attack and Decay length - voice[2]->envelope()->writeATTACK_DECAY(value); + voice[2].envelope()->writeATTACK_DECAY(value); break; case 0x14: // Voice #3 Sustain volume and Release length - voice[2]->envelope()->writeSUSTAIN_RELEASE(value); + voice[2].envelope()->writeSUSTAIN_RELEASE(value); break; case 0x15: // Filter cut off frequency (bits #0-#2) @@ -448,9 +474,9 @@ void SID::write(int offset, unsigned char value) voiceSync(false); } -void SID::setSamplingParameters(double clockFrequency, SamplingMethod method, double samplingFrequency, double highestAccurateFrequency) +void SID::setSamplingParameters(double clockFrequency, SamplingMethod method, double samplingFrequency) { - externalFilter->setClockFrequency(clockFrequency); + externalFilter.setClockFrequency(clockFrequency); switch (method) { @@ -459,7 +485,7 @@ void SID::setSamplingParameters(double clockFrequency, SamplingMethod method, do break; case RESAMPLE: - resampler.reset(TwoPassSincResampler::create(clockFrequency, samplingFrequency, highestAccurateFrequency)); + resampler.reset(TwoPassSincResampler::create(clockFrequency, samplingFrequency)); break; default: @@ -480,16 +506,16 @@ void SID::clockSilent(unsigned int cycles) for (int i = 0; i < delta_t; i++) { // clock waveform generators (can affect OSC3) - voice[0]->wave()->clock(); - voice[1]->wave()->clock(); - voice[2]->wave()->clock(); + voice[0].wave()->clock(); + voice[1].wave()->clock(); + voice[2].wave()->clock(); - voice[0]->wave()->output(voice[2]->wave()); - voice[1]->wave()->output(voice[0]->wave()); - voice[2]->wave()->output(voice[1]->wave()); + voice[0].wave()->output(voice[2].wave()); + voice[1].wave()->output(voice[0].wave()); + voice[2].wave()->output(voice[1].wave()); // clock ENV3 only - voice[2]->envelope()->clock(); + voice[2].envelope()->clock(); } cycles -= delta_t; diff --git a/src/sound/resid-fp/Spline.cpp b/src/sound/resid-fp/Spline.cpp index 50d55fef1..273fea032 100644 --- a/src/sound/resid-fp/Spline.cpp +++ b/src/sound/resid-fp/Spline.cpp @@ -92,11 +92,11 @@ Spline::Point Spline::evaluate(double x) const { if ((x < c->x1) || (x > c->x2)) { - for (size_t i = 0; i < params.size(); i++) + for (const auto & param : params) { - if (x <= params[i].x2) + if (x <= param.x2) { - c = ¶ms[i]; + c = ¶m; break; } } diff --git a/src/sound/resid-fp/Spline.h b/src/sound/resid-fp/Spline.h index 6cc2b1edc..c3ef1637b 100644 --- a/src/sound/resid-fp/Spline.h +++ b/src/sound/resid-fp/Spline.h @@ -38,14 +38,14 @@ namespace reSIDfp class Spline { public: - typedef struct + using Point = struct { double x; double y; - } Point; + }; private: - typedef struct + using Param = struct { double x1; double x2; @@ -53,9 +53,9 @@ private: double b; double c; double d; - } Param; + }; - typedef std::vector ParamVector; + using ParamVector = std::vector; private: /// Interpolation parameters diff --git a/src/sound/resid-fp/Voice.h b/src/sound/resid-fp/Voice.h index fc7ed41b7..0fb708b1e 100644 --- a/src/sound/resid-fp/Voice.h +++ b/src/sound/resid-fp/Voice.h @@ -23,14 +23,10 @@ #ifndef VOICE_H #define VOICE_H -#include - #include "siddefs-fp.h" #include "WaveformGenerator.h" #include "EnvelopeGenerator.h" -#include "sidcxx11.h" - namespace reSIDfp { @@ -40,9 +36,9 @@ namespace reSIDfp class Voice { private: - std::unique_ptr const waveformGenerator; + WaveformGenerator waveformGenerator; - std::unique_ptr const envelopeGenerator; + EnvelopeGenerator envelopeGenerator; /// The DAC LUT for analog waveform output float* wavDAC; //-V730_NOINIT this is initialized in the SID constructor @@ -67,23 +63,16 @@ public: * @return the voice analog output */ RESID_INLINE - int output(const WaveformGenerator* ringModulator) const + float output(const WaveformGenerator* ringModulator) { - unsigned int const wav = waveformGenerator->output(ringModulator); - unsigned int const env = envelopeGenerator->output(); + unsigned int const wav = waveformGenerator.output(ringModulator); + unsigned int const env = envelopeGenerator.output(); // DAC imperfections are emulated by using the digital output // as an index into a DAC lookup table. - return static_cast(wavDAC[wav] * envDAC[env]); + return wavDAC[wav] * envDAC[env]; } - /** - * Constructor. - */ - Voice() : - waveformGenerator(new WaveformGenerator()), - envelopeGenerator(new EnvelopeGenerator()) {} - /** * Set the analog DAC emulation for waveform generator. * Must be called before any operation. @@ -100,9 +89,9 @@ public: */ void setEnvDAC(float* dac) { envDAC = dac; } - WaveformGenerator* wave() const { return waveformGenerator.get(); } + WaveformGenerator* wave() { return &waveformGenerator; } - EnvelopeGenerator* envelope() const { return envelopeGenerator.get(); } + EnvelopeGenerator* envelope() { return &envelopeGenerator; } /** * Write control register. @@ -111,8 +100,8 @@ public: */ void writeCONTROL_REG(unsigned char control) { - waveformGenerator->writeCONTROL_REG(control); - envelopeGenerator->writeCONTROL_REG(control); + waveformGenerator.writeCONTROL_REG(control); + envelopeGenerator.writeCONTROL_REG(control); } /** @@ -120,8 +109,8 @@ public: */ void reset() { - waveformGenerator->reset(); - envelopeGenerator->reset(); + waveformGenerator.reset(); + envelopeGenerator.reset(); } }; diff --git a/src/sound/resid-fp/WaveformCalculator.cpp b/src/sound/resid-fp/WaveformCalculator.cpp index 74a93cce5..7e167bb18 100644 --- a/src/sound/resid-fp/WaveformCalculator.cpp +++ b/src/sound/resid-fp/WaveformCalculator.cpp @@ -21,66 +21,154 @@ #include "WaveformCalculator.h" +#include "sidcxx11.h" + +#include +#include #include namespace reSIDfp { +/** + * Combined waveform model parameters. + */ +using distance_t = float (*)(float, int); + +using CombinedWaveformConfig = struct +{ + distance_t distFunc; + float threshold; + float topbit; + float pulsestrength; + float distance1; + float distance2; +}; + +using cw_cache_t = std::map; + +cw_cache_t PULLDOWN_CACHE; + +std::mutex PULLDOWN_CACHE_Lock; + WaveformCalculator* WaveformCalculator::getInstance() { static WaveformCalculator instance; return &instance; } -/** - * Parameters derived with the Monte Carlo method based on - * samplings by kevtris. Code and data available in the project repository [1]. - * - * The score here reported is the acoustic error - * calculated XORing the estimated and the sampled values. - * In parentheses the number of mispredicted bits. - * - * [1] https://github.com/libsidplayfp/combined-waveforms - */ -const CombinedWaveformConfig config[2][5] = -{ - { /* kevtris chip G (6581 R2) */ - {0.862147212f, 0.f, 10.8962431f, 2.50848103f }, // TS error 1941 (327/28672) - {0.932746708f, 2.07508397f, 1.03668225f, 1.14876997f }, // PT error 5992 (126/32768) - {0.860927045f, 2.43506575f, 0.908603609f, 1.07907593f }, // PS error 3693 (521/28672) - {0.741343081f, 0.0452554375f, 1.1439606f, 1.05711341f }, // PTS error 338 ( 29/28672) - {0.96f, 2.5f, 1.1f, 1.2f }, // NP guessed - }, - { /* kevtris chip V (8580 R5) */ - {0.715788841f, 0.f, 1.32999945f, 2.2172699f }, // TS error 928 (135/32768) - {0.93500334f, 1.05977178f, 1.08629429f, 1.43518543f }, // PT error 7991 (212/32768) - {0.920648575f, 0.943601072f, 1.13034654f, 1.41881108f }, // PS error 12566 (394/32768) - {0.90921098f, 0.979807794f, 0.942194462f, 1.40958893f }, // PTS error 2092 ( 60/32768) - {0.95f, 1.15f, 1.f, 1.45f }, // NP guessed - }, -}; - -typedef float (*distance_t)(float, int); - // Distance functions static float exponentialDistance(float distance, int i) { return pow(distance, -i); } -#if 0 MAYBE_UNUSED static float linearDistance(float distance, int i) { return 1.f / (1.f + i * distance); } -#endif -#if 0 -MAYBE_UNUSED static float quadraticDistance(float distance, int i) +static float quadraticDistance(float distance, int i) { return 1.f / (1.f + (i*i) * distance); } -#endif + +/** + * Parameters derived with the Monte Carlo method based on + * samplings from real machines. + * Code and data available in the project repository [1]. + * Sampling program made by Dag Lem [2]. + * + * The score here reported is the acoustic error + * calculated XORing the estimated and the sampled values. + * In parentheses the number of mispredicted bits. + * + * [1] https://github.com/libsidplayfp/combined-waveforms + * [2] https://github.com/daglem/reDIP-SID/blob/master/research/combsample.d64 + */ +const CombinedWaveformConfig configAverage[2][5] = +{ + { /* 6581 R3 0486S sampled by Trurl */ + // TS error 3555 (324/32768) [RMS: 73.98] + { exponentialDistance, 0.877322257f, 1.11349654f, 0.f, 2.14537621f, 9.08618164f }, + // PT error 4590 (124/32768) [RMS: 68.90] + { linearDistance, 0.941692829f, 1.f, 1.80072665f, 0.033124879f, 0.232303441f }, + // PS error 19352 (763/32768) [RMS: 96.91] + { linearDistance, 1.66494179f, 1.03760982f, 5.62705326f, 0.291590303f, 0.283631504f }, + // PTS error 5068 ( 94/32768) [RMS: 41.69] + { linearDistance, 1.09762526f, 0.975265801f, 1.52196741f, 0.151528224f, 0.841949463f }, + // NP guessed + { exponentialDistance, 0.96f, 1.f, 2.5f, 1.1f, 1.2f }, + }, + { /* 8580 R5 1088 sampled by reFX-Mike */ + // TS error 10660 (353/32768) [RMS: 58.34] + { exponentialDistance, 0.853578329f, 1.09615636f, 0.f, 1.8819375f, 6.80794907f }, + // PT error 10635 (289/32768) [RMS: 108.81] + { exponentialDistance, 0.929835618f, 1.f, 1.12836814f, 1.10453653f, 1.48065746f }, + // PS error 12255 (554/32768) [RMS: 102.27] + { quadraticDistance, 0.911938608f, 0.996440411f, 1.2278074f, 0.000117214302f, 0.18948476f }, + // PTS error 6913 (127/32768) [RMS: 55.80] + { exponentialDistance, 0.938004673f, 1.04827631f, 1.21178246f, 0.915959001f, 1.42698038f }, + // NP guessed + { exponentialDistance, 0.95f, 1.f, 1.15f, 1.f, 1.45f }, + }, +}; + +const CombinedWaveformConfig configWeak[2][5] = +{ + { /* 6581 R2 4383 sampled by ltx128 */ + // TS error 1474 (198/32768) [RMS: 62.81] + { exponentialDistance, 0.892563999f, 1.11905622f, 0.f, 2.21876144f, 9.63837719f }, + // PT error 612 (102/32768) [RMS: 43.71] + { linearDistance, 1.01262534f, 1.f, 2.46070528f, 0.0537485816f, 0.0986242667f }, + // PS error 8135 (575/32768) [RMS: 75.10] + { linearDistance, 2.14896345f, 1.0216713f, 10.5400085f, 0.244498149f, 0.126134038f }, + // PTS error 2489 (60/32768) [RMS: 24.41] + { linearDistance, 1.22330308f, 0.933797896f, 2.83245254f, 0.0615176819f, 0.323831677f }, + // NP guessed + { exponentialDistance, 0.96f, 1.f, 2.5f, 1.1f, 1.2f }, + }, + { /* 8580 R5 4887 sampled by reFX-Mike */ + // TS error 741 (76/32768) [RMS: 53.74] + { exponentialDistance, 0.812351167f, 1.1727736f, 0.f, 1.87459648f, 2.31578159f }, + // PT error 7199 (192/32768) [RMS: 88.43] + { exponentialDistance, 0.917997837f, 1.f, 1.01248944f, 1.05761552f, 1.37529826f }, + // PS error 9856 (332/32768) [RMS: 86.29] + { quadraticDistance, 0.968754232f, 1.00669801f, 1.29909098f, 0.00962483883f, 0.146850556f }, + // PTS error 4809 (60/32768) [RMS: 45.37] + { exponentialDistance, 0.941834152f, 1.06401193f, 0.991132736f, 0.995310068f, 1.41105855f }, + // NP guessed + { exponentialDistance, 0.95f, 1.f, 1.15f, 1.f, 1.45f }, + }, +}; + +const CombinedWaveformConfig configStrong[2][5] = +{ + { /* 6581 R2 0384 sampled by Trurl */ + // TS error 20337 (1579/32768) [RMS: 88.57] + { exponentialDistance, 0.000637792516f, 1.56725872f, 0.f, 0.00036806846f, 1.51800942f }, + // PT error 5190 (238/32768) [RMS: 83.54] + { linearDistance, 0.924780309f, 1.f, 1.96809769f, 0.0888123438f, 0.234606609f }, + // PS error 31015 (2181/32768) [RMS: 114.99] + { linearDistance, 1.2328074f, 0.73079139f, 3.9719491f, 0.00156516861f, 0.314677745f }, + // PTS error 9874 (201/32768) [RMS: 52.30] + { linearDistance, 1.08558261f, 0.857638359f, 1.52781796f, 0.152927235f, 1.02657032f }, + // NP guessed + { exponentialDistance, 0.96f, 1.f, 2.5f, 1.1f, 1.2f }, + }, + { /* 8580 R5 1489 sampled by reFX-Mike */ + // TS error 4837 (388/32768) [RMS: 76.07] + { exponentialDistance, 0.89762634f, 56.7594185f, 0.f, 7.68995237f, 12.0754194f }, + // PT error 9266 (508/32768) [RMS: 127.83] + { exponentialDistance, 0.87147671f, 1.f, 1.44887495f, 1.05899632f, 1.43786001f }, + // PS error 13168 (718/32768) [RMS: 123.35] + { quadraticDistance, 0.89255774f, 1.2253896f, 1.75615835f, 0.0245045591f, 0.12982437f }, + // PTS error 6702 (300/32768) [RMS: 71.01] + { linearDistance, 0.91124934f, 0.963609755f, 0.909965038f, 1.07445884f, 1.82399702f }, + // NP guessed + { exponentialDistance, 0.95f, 1.f, 1.15f, 1.f, 1.45f }, + }, +}; /// Calculate triangle waveform static unsigned int triXor(unsigned int val) @@ -96,15 +184,17 @@ static unsigned int triXor(unsigned int val) * @param threshold * @param accumulator the high bits of the accumulator value */ -short calculatePulldown(float distancetable[], float pulsestrength, float threshold, unsigned int accumulator) +short calculatePulldown(float distancetable[], float topbit, float pulsestrength, float threshold, unsigned int accumulator) { - unsigned char bit[12]; + float bit[12]; for (unsigned int i = 0; i < 12; i++) { - bit[i] = (accumulator & (1u << i)) != 0 ? 1 : 0; + bit[i] = (accumulator & (1u << i)) != 0 ? 1.f : 0.f; } + bit[11] *= topbit; + float pulldown[12]; for (int sb = 0; sb < 12; sb++) @@ -117,7 +207,7 @@ short calculatePulldown(float distancetable[], float pulsestrength, float thresh if (cb == sb) continue; const float weight = distancetable[sb - cb + 12]; - avg += static_cast(1 - bit[cb]) * weight; + avg += (1.f - bit[cb]) * weight; n += weight; } @@ -131,7 +221,7 @@ short calculatePulldown(float distancetable[], float pulsestrength, float thresh for (unsigned int i = 0; i < 12; i++) { - const float bitValue = bit[i] != 0 ? 1.f - pulldown[i] : 0.f; + const float bitValue = bit[i] > 0.f ? 1.f - pulldown[i] : 0.f; if (bitValue > threshold) { value |= 1u << i; @@ -157,9 +247,26 @@ WaveformCalculator::WaveformCalculator() : } } -matrix_t* WaveformCalculator::buildPulldownTable(ChipModel model) +matrix_t* WaveformCalculator::buildPulldownTable(ChipModel model, CombinedWaveforms cws) { - const CombinedWaveformConfig* cfgArray = config[model == MOS6581 ? 0 : 1]; + std::lock_guard lock(PULLDOWN_CACHE_Lock); + + const int modelIdx = model == MOS6581 ? 0 : 1; + const CombinedWaveformConfig* cfgArray; + + switch (cws) + { + default: + case AVERAGE: + cfgArray = configAverage[modelIdx]; + break; + case WEAK: + cfgArray = configWeak[modelIdx]; + break; + case STRONG: + cfgArray = configStrong[modelIdx]; + break; + } cw_cache_t::iterator lb = PULLDOWN_CACHE.lower_bound(cfgArray); @@ -174,7 +281,7 @@ matrix_t* WaveformCalculator::buildPulldownTable(ChipModel model) { const CombinedWaveformConfig& cfg = cfgArray[wav]; - const distance_t distFunc = exponentialDistance; + const distance_t distFunc = cfg.distFunc; float distancetable[12 * 2 + 1]; distancetable[12] = 1.f; @@ -186,14 +293,11 @@ matrix_t* WaveformCalculator::buildPulldownTable(ChipModel model) for (unsigned int idx = 0; idx < (1u << 12); idx++) { - pdTable[wav][idx] = calculatePulldown(distancetable, cfg.pulsestrength, cfg.threshold, idx); + pdTable[wav][idx] = calculatePulldown(distancetable, cfg.topbit, cfg.pulsestrength, cfg.threshold, idx); } } -#ifdef HAVE_CXX11 + return &(PULLDOWN_CACHE.emplace_hint(lb, cw_cache_t::value_type(cfgArray, pdTable))->second); -#else - return &(PULLDOWN_CACHE.insert(lb, cw_cache_t::value_type(cfgArray, pdTable))->second); -#endif } } // namespace reSIDfp diff --git a/src/sound/resid-fp/WaveformCalculator.h b/src/sound/resid-fp/WaveformCalculator.h index 4ad677274..f6db00c7d 100644 --- a/src/sound/resid-fp/WaveformCalculator.h +++ b/src/sound/resid-fp/WaveformCalculator.h @@ -22,46 +22,33 @@ #ifndef WAVEFORMCALCULATOR_h #define WAVEFORMCALCULATOR_h -#include - #include "array.h" -#include "sidcxx11.h" + #include "siddefs-fp.h" namespace reSIDfp { -/** - * Combined waveform model parameters. - */ -typedef struct -{ - float threshold; - float pulsestrength; - float distance1; - float distance2; -} CombinedWaveformConfig; - /** * Combined waveform calculator for WaveformGenerator. * By combining waveforms, the bits of each waveform are effectively short - * circuited. A zero bit in one waveform will result in a zero output bit - * (thus the infamous claim that the waveforms are AND'ed). + * circuited, a zero bit in one waveform will result in a zero output bit, + * thus the claim that the waveforms are AND'ed. * However, a zero bit in one waveform may also affect the neighboring bits * in the output. * * Example: - * + * * 1 1 * Bit # 1 0 9 8 7 6 5 4 3 2 1 0 * ----------------------- * Sawtooth 0 0 0 1 1 1 1 1 1 0 0 0 - * + * * Triangle 0 0 1 1 1 1 1 1 0 0 0 0 - * + * * AND 0 0 0 1 1 1 1 1 0 0 0 0 - * + * * Output 0 0 0 0 1 1 1 0 0 0 0 0 * * @@ -98,14 +85,9 @@ typedef struct */ class WaveformCalculator { -private: - typedef std::map cw_cache_t; - private: matrix_t wftable; - cw_cache_t PULLDOWN_CACHE; - private: WaveformCalculator(); @@ -126,9 +108,10 @@ public: * Build pulldown table for use by WaveformGenerator. * * @param model Chip model to use + * @param cws strength of combined waveforms * @return Pulldown table */ - matrix_t* buildPulldownTable(ChipModel model); + matrix_t* buildPulldownTable(ChipModel model, CombinedWaveforms cws); }; } // namespace reSIDfp diff --git a/src/sound/resid-fp/WaveformGenerator.cpp b/src/sound/resid-fp/WaveformGenerator.cpp index 4c7a55b3d..be0738bba 100644 --- a/src/sound/resid-fp/WaveformGenerator.cpp +++ b/src/sound/resid-fp/WaveformGenerator.cpp @@ -40,13 +40,13 @@ namespace reSIDfp * and [VICE Bug #1128](http://sourceforge.net/p/vice-emu/bugs/1128/) */ // ~95ms -const unsigned int FLOATING_OUTPUT_TTL_6581R3 = 54000; -const unsigned int FLOATING_OUTPUT_FADE_6581R3 = 1400; +constexpr unsigned int FLOATING_OUTPUT_TTL_6581R3 = 54000; +constexpr unsigned int FLOATING_OUTPUT_FADE_6581R3 = 1400; // ~1s -//const unsigned int FLOATING_OUTPUT_TTL_6581R4 = 1000000; +constexpr unsigned int FLOATING_OUTPUT_TTL_6581R4 = 1000000; // ~1s -const unsigned int FLOATING_OUTPUT_TTL_8580R5 = 800000; -const unsigned int FLOATING_OUTPUT_FADE_8580R5 = 50000; +constexpr unsigned int FLOATING_OUTPUT_TTL_8580R5 = 800000; +constexpr unsigned int FLOATING_OUTPUT_FADE_8580R5 = 50000; /** * Number of cycles after which the shift register is reset @@ -58,15 +58,15 @@ const unsigned int FLOATING_OUTPUT_FADE_8580R5 = 50000; * only the big difference between the old and new models. */ // ~210ms -const unsigned int SHIFT_REGISTER_RESET_6581R3 = 50000; -const unsigned int SHIFT_REGISTER_FADE_6581R3 = 15000; +constexpr unsigned int SHIFT_REGISTER_RESET_6581R3 = 50000; +constexpr unsigned int SHIFT_REGISTER_FADE_6581R3 = 15000; // ~2.15s -//const unsigned int SHIFT_REGISTER_RESET_6581R4 = 2150000; +constexpr unsigned int SHIFT_REGISTER_RESET_6581R4 = 2150000; // ~2.8s -const unsigned int SHIFT_REGISTER_RESET_8580R5 = 986000; -const unsigned int SHIFT_REGISTER_FADE_8580R5 = 314300; +constexpr unsigned int SHIFT_REGISTER_RESET_8580R5 = 986000; +constexpr unsigned int SHIFT_REGISTER_FADE_8580R5 = 314300; -const unsigned int shift_mask = +constexpr unsigned int shift_mask = ~( (1u << 2) | // Bit 20 (1u << 4) | // Bit 18 @@ -107,15 +107,100 @@ const unsigned int shift_mask = * -----+-------+--------------+-------------- * phi1 | 1 | X --> X | A --> A <- shift phase 2 * phi2 | 1 | X <-> X | A <-> A + * + * + * Normal cycles + * ------------- + * Normally, when noise is selected along with another waveform, + * c1 and c2 are closed and the output bits pull down the corresponding + * shift register bits. + * + * noi_out_x noi_out_x+1 + * ^ ^ + * | | + * +-------------+ +-------------+ + * | | | | + * +---o<|---+ | +---o<|---+ | + * | | | | | | + * c2 | c1 | | c2 | c1 | | + * | | | | | | + * >---/---+---|>o---+ +---/---+---|>o---+ +---/---> + * LC LC LC + * + * + * Shift phase 1 + * ------------- + * During shift phase 1 c1 and c2 are open, the SR bits are floating + * and will be driven by the output of combined waveforms, + * or slowly turn high. + * + * noi_out_x noi_out_x+1 + * ^ ^ + * | | + * +-------------+ +-------------+ + * | | | | + * +---o<|---+ | +---o<|---+ | + * | | | | | | + * c2 / c1 / | c2 / c1 / | + * | | | | | | + * >-------+---|>o---+ +-------+---|>o---+ +-------> + * LC LC LC + * + * + * Shift phase 2 (phi1) + * -------------------- + * During the first half cycle of shift phase 2 c1 is closed + * so the value from of noi_out_x-1 enters the bit. + * + * noi_out_x noi_out_x+1 + * ^ ^ + * | | + * +-------------+ +-------------+ + * | | | | + * +---o<|---+ | +---o<|---+ | + * | | | | | | + * c2 / c1 | | c2 / c1 | | + * | | | | | | + * >---/---+---|>o---+ +---/---+---|>o---+ +---/---> + * LC LC LC + * + * + * Shift phase 2 (phi2) + * -------------------- + * On the second half of shift phase 2 c2 closes and + * we're back to normal cycles. */ inline bool do_writeback(unsigned int waveform_old, unsigned int waveform_new, bool is6581) { // no writeback without combined waveforms - if (waveform_new <= 8) - return false; + if (waveform_old <= 8) - return false; // fixes SID/noisewriteback/noise_writeback_test2-{old,new} + // fixes SID/noisewriteback/noise_writeback_test2-{old,new} + return false; + + if (waveform_new < 8) + return false; + + if ((waveform_new == 8) + // breaks noise_writeback_check_F_to_8_old + // but fixes simple and scan + && (waveform_old != 0xf)) + { + // fixes + // noise_writeback_check_9_to_8_old + // noise_writeback_check_A_to_8_old + // noise_writeback_check_B_to_8_old + // noise_writeback_check_D_to_8_old + // noise_writeback_check_E_to_8_old + // noise_writeback_check_F_to_8_old + // noise_writeback_check_9_to_8_new + // noise_writeback_check_A_to_8_new + // noise_writeback_check_D_to_8_new + // noise_writeback_check_E_to_8_new + // noise_writeback_test1-{old,new} + return false; + } // What's happening here? if (is6581 && @@ -190,8 +275,16 @@ void WaveformGenerator::write_shift_register() { if (unlikely(waveform > 0x8)) { +#if 0 + // FIXME this breaks SID/wf12nsr/wf12nsr if (waveform == 0xc) - return; // breaks SID/wf12nsr/wf12nsr + // fixes + // noise_writeback_check_8_to_C_old + // noise_writeback_check_9_to_C_old + // noise_writeback_check_A_to_C_old + // noise_writeback_check_C_to_C_old + return; +#endif // Write changes to the shift register output caused by combined waveforms // back into the shift register. diff --git a/src/sound/resid-fp/WaveformGenerator.h b/src/sound/resid-fp/WaveformGenerator.h index adca6c228..7bbccbc80 100644 --- a/src/sound/resid-fp/WaveformGenerator.h +++ b/src/sound/resid-fp/WaveformGenerator.h @@ -93,64 +93,64 @@ namespace reSIDfp class WaveformGenerator { private: - matrix_t* model_wave; - matrix_t* model_pulldown; + matrix_t* model_wave = nullptr; + matrix_t* model_pulldown = nullptr; - short* wave; - short* pulldown; + short* wave = nullptr; + short* pulldown = nullptr; // PWout = (PWn/40.95)% - unsigned int pw; + unsigned int pw = 0; - unsigned int shift_register; + unsigned int shift_register = 0; /// Shift register is latched when transitioning to shift phase 1. - unsigned int shift_latch; + unsigned int shift_latch = 0; /// Emulation of pipeline causing bit 19 to clock the shift register. - int shift_pipeline; + int shift_pipeline = 0; - unsigned int ring_msb_mask; - unsigned int no_noise; - unsigned int noise_output; - unsigned int no_noise_or_noise_output; - unsigned int no_pulse; - unsigned int pulse_output; + unsigned int ring_msb_mask = 0; + unsigned int no_noise = 0; + unsigned int noise_output = 0; + unsigned int no_noise_or_noise_output = 0; + unsigned int no_pulse = 0; + unsigned int pulse_output = 0; /// The control register right-shifted 4 bits; used for output function table lookup. - unsigned int waveform; + unsigned int waveform = 0; - unsigned int waveform_output; + unsigned int waveform_output = 0; /// Current accumulator value. - unsigned int accumulator; + unsigned int accumulator = 0x555555; // Accumulator's even bits are high on powerup // Fout = (Fn*Fclk/16777216)Hz - unsigned int freq; + unsigned int freq = 0; /// 8580 tri/saw pipeline - unsigned int tri_saw_pipeline; + unsigned int tri_saw_pipeline = 0x555; /// The OSC3 value - unsigned int osc3; + unsigned int osc3 = 0; /// Remaining time to fully reset shift register. - unsigned int shift_register_reset; + unsigned int shift_register_reset = 0; // The wave signal TTL when no waveform is selected. - unsigned int floating_output_ttl; + unsigned int floating_output_ttl = 0; /// The control register bits. Gate is handled by EnvelopeGenerator. //@{ - bool test; - bool sync; + bool test = false; + bool sync = false; //@} /// Test bit is latched at phi2 for the noise XOR. bool test_or_reset; /// Tell whether the accumulator MSB was set high on this cycle. - bool msb_rising; + bool msb_rising = false; bool is6581; //-V730_NOINIT this is initialized in the SID constructor @@ -160,7 +160,7 @@ private: void write_shift_register(); void set_noise_output(); - + void set_no_noise_or_noise_output(); void waveBitfade(); @@ -194,35 +194,6 @@ public: */ void synchronize(WaveformGenerator* syncDest, const WaveformGenerator* syncSource) const; - /** - * Constructor. - */ - WaveformGenerator() : - model_wave(nullptr), - model_pulldown(nullptr), - wave(nullptr), - pulldown(nullptr), - pw(0), - shift_register(0), - shift_pipeline(0), - ring_msb_mask(0), - no_noise(0), - noise_output(0), - no_noise_or_noise_output(0), - no_pulse(0), - pulse_output(0), - waveform(0), - waveform_output(0), - accumulator(0x555555), // Accumulator's even bits are high on powerup - freq(0), - tri_saw_pipeline(0x555), - osc3(0), - shift_register_reset(0), - floating_output_ttl(0), - test(false), - sync(false), - msb_rising(false) {} - /** * Write FREQ LO register. * @@ -397,13 +368,13 @@ unsigned int WaveformGenerator::output(const WaveformGenerator* ringModulator) { osc3 = waveform_output; } - // In the 6581 the top bit of the accumulator may be driven low by combined waveforms // when the sawtooth is selected - if (is6581 - && (waveform & 0x2) - && ((waveform_output & 0x800) == 0)) + if (is6581 && (waveform & 0x2) && ((waveform_output & 0x800) == 0)) + { + msb_rising = 0; accumulator &= 0x7fffff; + } write_shift_register(); } diff --git a/src/sound/resid-fp/array.h b/src/sound/resid-fp/array.h index a0d390966..58c4617ae 100644 --- a/src/sound/resid-fp/array.h +++ b/src/sound/resid-fp/array.h @@ -26,9 +26,7 @@ # include "config.h" #endif -#ifdef HAVE_CXX11 -# include -#endif +#include /** * Counter. @@ -36,11 +34,7 @@ class counter { private: -#ifndef HAVE_CXX11 - volatile unsigned int c; -#else std::atomic c; -#endif public: counter() : c(1) {} @@ -81,6 +75,6 @@ public: T const* operator[](unsigned int a) const { return &data[a * y]; } }; -typedef matrix matrix_t; +using matrix_t = matrix; #endif diff --git a/src/sound/resid-fp/config.h b/src/sound/resid-fp/config.h index 0eeba8dee..399003a55 100644 --- a/src/sound/resid-fp/config.h +++ b/src/sound/resid-fp/config.h @@ -1 +1 @@ -#define HAVE_CXX14 +#define HAVE_CXX17 diff --git a/src/sound/resid-fp/resample/Resampler.h b/src/sound/resid-fp/resample/Resampler.h index 904f65458..293fda6ce 100644 --- a/src/sound/resid-fp/resample/Resampler.h +++ b/src/sound/resid-fp/resample/Resampler.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2020 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,7 @@ #define RESAMPLER_H #include +#include #include "../sidcxx11.h" @@ -37,28 +38,45 @@ namespace reSIDfp */ class Resampler { -protected: - inline short softClip(int x) const +private: + template + static inline int clipper(int x) { + assert(x >= 0); constexpr int threshold = 28000; if (likely(x < threshold)) return x; - constexpr double t = threshold / 32768.; + constexpr double max_val = static_cast(m); + constexpr double t = threshold / max_val; constexpr double a = 1. - t; constexpr double b = 1. / a; - double value = static_cast(x - threshold) / 32768.; - value = t + a * tanh(b * value); - return static_cast(value * 32768.); + double value = static_cast(x - threshold) / max_val; + value = t + a * std::tanh(b * value); + return static_cast(value * max_val); } + /* + * Soft Clipping implementation, splitted for test. + */ + static inline int softClipImpl(int x) + { + return x < 0 ? -clipper<32768>(-x) : clipper<32767>(x); + } + +protected: + /* + * Soft Clipping into 16 bit range [-32768,32767] + */ + static inline short softClip(int x) { return static_cast(softClipImpl(x)); } + virtual int output() const = 0; Resampler() {} public: - virtual ~Resampler() {} + virtual ~Resampler() = default; /** * Input a sample into resampler. Output "true" when resampler is ready with new sample. @@ -73,9 +91,10 @@ public: * * @return resampled sample */ - short getOutput() const + inline short getOutput(int scaleFactor) const { - return softClip(output()); + const int out = (scaleFactor * output()) / 2; + return softClip(out); } virtual void reset() = 0; diff --git a/src/sound/resid-fp/resample/SincResampler.cpp b/src/sound/resid-fp/resample/SincResampler.cpp index df7d8af83..14ae13752 100644 --- a/src/sound/resid-fp/resample/SincResampler.cpp +++ b/src/sound/resid-fp/resample/SincResampler.cpp @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2020 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004 Dag Lem * @@ -22,11 +22,16 @@ #include "SincResampler.h" +#ifdef HAVE_CXX20 +# include +#endif + +#include +#include #include #include #include -#include -#include +#include #include "../siddefs-fp.h" @@ -34,10 +39,8 @@ # include "config.h" #endif -#ifdef HAVE_EMMINTRIN_H -# include -#elif defined HAVE_MMINTRIN_H -# include +#ifdef HAVE_SMMINTRIN_H +# include #elif defined(HAVE_ARM_NEON_H) # include #endif @@ -45,15 +48,10 @@ namespace reSIDfp { -typedef std::map fir_cache_t; - -/// Cache for the expensive FIR table computation results. -fir_cache_t FIR_CACHE; - /// Maximum error acceptable in I0 is 1e-6, or ~96 dB. -const double I0E = 1e-6; +constexpr double I0E = 1e-6; -const int BITS = 16; +constexpr int BITS = 16; /** * Compute the 0th order modified Bessel function of the first kind. @@ -90,7 +88,7 @@ double I0(double x) * @param bLength length of the sinc buffer * @return convolved result */ -int convolve(const short* a, const short* b, int bLength) +int convolve(const int* a, const short* b, int bLength) { #ifdef HAVE_EMMINTRIN_H int out = 0; @@ -102,7 +100,7 @@ int convolve(const short* a, const short* b, int bLength) { if (offset) { - const int l = (0x10 - offset)/2; + const int l = (0x10 - offset) / 2; for (int i = 0; i < l; i++) { @@ -208,9 +206,9 @@ int convolve(const short* a, const short* b, int bLength) bLength &= 3; #else int32x4_t acc = vdupq_n_s32(0); - + const int n = bLength / 4; - + for (int i = 0; i < n; i++) { const int16x4_t h_vec = vld1_s16(a); @@ -219,12 +217,12 @@ int convolve(const short* a, const short* b, int bLength) a += 4; b += 4; } - + int out = vgetq_lane_s32(acc, 0) + vgetq_lane_s32(acc, 1) + vgetq_lane_s32(acc, 2) + vgetq_lane_s32(acc, 3); - + bLength &= 3; #endif #else @@ -233,7 +231,7 @@ int convolve(const short* a, const short* b, int bLength) for (int i = 0; i < bLength; i++) { - out += *a++ * *b++; + out += a[i] * static_cast(b[i]); } return (out + (1 << 14)) >> 15; @@ -265,17 +263,27 @@ int SincResampler::fir(int subcycle) return v1 + (firTableOffset * (v2 - v1) >> 10); } -SincResampler::SincResampler(double clockFrequency, double samplingFrequency, double highestAccurateFrequency) : - sampleIndex(0), - cyclesPerSample(static_cast(clockFrequency / samplingFrequency * 1024.)), - sampleOffset(0), - outputValue(0) +SincResampler::SincResampler( + double clockFrequency, + double samplingFrequency, + double highestAccurateFrequency) : + cyclesPerSample(static_cast(clockFrequency / samplingFrequency * 1024.)) { +#if defined(HAVE_CXX20) && defined(__cpp_lib_constexpr_cmath) + constexpr double PI = std::numbers::pi; +#else +# ifdef M_PI + constexpr double PI = M_PI; +#else + constexpr double PI = 3.14159265358979323846; +# endif +#endif + // 16 bits -> -96dB stopband attenuation. - const double A = -20. * log10(1.0 / (1 << BITS)); + const double A = -20. * std::log10(1.0 / (1 << BITS)); // A fraction of the bandwidth is allocated to the transition band, which we double // because we design the filter to transition halfway at nyquist. - const double dw = (1. - 2.*highestAccurateFrequency / samplingFrequency) * M_PI * 2.; + const double dw = (1. - 2.*highestAccurateFrequency / samplingFrequency) * PI * 2.; // For calculation of beta and N see the reference for the kaiserord // function in the MATLAB Signal Processing Toolbox: @@ -283,6 +291,7 @@ SincResampler::SincResampler(double clockFrequency, double samplingFrequency, do const double beta = 0.1102 * (A - 8.7); const double I0beta = I0(beta); const double cyclesPerSampleD = clockFrequency / samplingFrequency; + const double inv_cyclesPerSampleD = samplingFrequency / clockFrequency; { // The filter order will maximally be 124 with the current constraints. @@ -302,40 +311,22 @@ SincResampler::SincResampler(double clockFrequency, double samplingFrequency, do assert(firN < RINGSIZE); // Error is bounded by err < 1.234 / L^2, so L = sqrt(1.234 / (2^-16)) = sqrt(1.234 * 2^16). - firRES = static_cast(ceil(sqrt(1.234 * (1 << BITS)) / cyclesPerSampleD)); + firRES = static_cast(std::ceil(std::sqrt(1.234 * (1 << BITS)) * inv_cyclesPerSampleD)); // firN*firRES represent the total resolution of the sinc sampling. JOS // recommends a length of 2^BITS, but we don't quite use that good a filter. // The filter test program indicates that the filter performs well, though. } - // Create the map key - std::ostringstream o; - o << firN << "," << firRES << "," << cyclesPerSampleD; - const std::string firKey = o.str(); - fir_cache_t::iterator lb = FIR_CACHE.lower_bound(firKey); - - // The FIR computation is expensive and we set sampling parameters often, but - // from a very small set of choices. Thus, caching is used to speed initialization. - if (lb != FIR_CACHE.end() && !(FIR_CACHE.key_comp()(firKey, lb->first))) - { - firTable = &(lb->second); - } - else { // Allocate memory for FIR tables. - matrix_t tempTable(firRES, firN); -#ifdef HAVE_CXX11 - firTable = &(FIR_CACHE.emplace_hint(lb, fir_cache_t::value_type(firKey, tempTable))->second); -#else - firTable = &(FIR_CACHE.insert(lb, fir_cache_t::value_type(firKey, tempTable))->second); -#endif + firTable = new matrix_t(firRES, firN); // The cutoff frequency is midway through the transition band, in effect the same as nyquist. - const double wc = M_PI; + const double wc = PI; // Calculate the sinc tables. - const double scale = 32768.0 * wc / cyclesPerSampleD / M_PI; + const double scale = 32768.0 * wc * inv_cyclesPerSampleD / PI; // we're not interested in the fractional part // so use int division before converting to double @@ -351,10 +342,10 @@ SincResampler::SincResampler(double clockFrequency, double samplingFrequency, do const double x = j - jPhase; const double xt = x / firN_2; - const double kaiserXt = fabs(xt) < 1. ? I0(beta * sqrt(1. - xt * xt)) / I0beta : 0.; + const double kaiserXt = std::fabs(xt) < 1. ? I0(beta * std::sqrt(1. - xt * xt)) / I0beta : 0.; - const double wt = wc * x / cyclesPerSampleD; - const double sincWt = fabs(wt) >= 1e-8 ? sin(wt) / wt : 1.; + const double wt = wc * x * inv_cyclesPerSampleD; + const double sincWt = std::fabs(wt) >= 1e-8 ? std::sin(wt) / wt : 1.; (*firTable)[i][j] = static_cast(scale * sincWt * kaiserXt); } @@ -362,18 +353,16 @@ SincResampler::SincResampler(double clockFrequency, double samplingFrequency, do } } +SincResampler::~SincResampler() +{ + delete firTable; +} + bool SincResampler::input(int input) { bool ready = false; - /* - * Clip the input as it may overflow the 16 bit range. - * - * Approximate measured input ranges: - * 6581: [-24262,+25080] (Kawasaki_Synthesizer_Demo) - * 8580: [-21514,+35232] (64_Forever, Drum_Fool) - */ - sample[sampleIndex] = sample[sampleIndex + RINGSIZE] = softClip(input); + sample[sampleIndex] = sample[sampleIndex + RINGSIZE] = input; sampleIndex = (sampleIndex + 1) & (RINGSIZE - 1); if (sampleOffset < 1024) @@ -390,7 +379,7 @@ bool SincResampler::input(int input) void SincResampler::reset() { - memset(sample, 0, sizeof(sample)); + std::fill(std::begin(sample), std::end(sample), 0); sampleOffset = 0; } diff --git a/src/sound/resid-fp/resample/SincResampler.h b/src/sound/resid-fp/resample/SincResampler.h index 7502d96fd..c3228171f 100644 --- a/src/sound/resid-fp/resample/SincResampler.h +++ b/src/sound/resid-fp/resample/SincResampler.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2013 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004 Dag Lem * @@ -25,13 +25,8 @@ #include "Resampler.h" -#include -#include - #include "../array.h" -#include "../sidcxx11.h" - namespace reSIDfp { @@ -54,13 +49,13 @@ class SincResampler final : public Resampler { private: /// Size of the ring buffer, must be a power of 2 - static const int RINGSIZE = 2048; + static constexpr int RINGSIZE = 2048; private: /// Table of the fir filter coefficients matrix_t* firTable; - int sampleIndex; + int sampleIndex = 0; /// Filter resolution int firRES; @@ -70,11 +65,11 @@ private: const int cyclesPerSample; - int sampleOffset; + int sampleOffset = 0; - int outputValue; + int outputValue = 0; - short sample[RINGSIZE * 2]; + int sample[RINGSIZE * 2]; private: int fir(int subcycle); @@ -82,25 +77,25 @@ private: public: /** * Use a clock freqency of 985248Hz for PAL C64, 1022730Hz for NTSC C64. - * The default end of passband frequency is pass_freq = 0.9*sample_freq/2 - * for sample frequencies up to ~ 44.1kHz, and 20kHz for higher sample frequencies. * - * For resampling, the ratio between the clock frequency and the sample frequency - * is limited as follows: 125*clock_freq/sample_freq < 16384 + * For resampling, the ratio between the clock frequency + * and the sample frequency is limited as follows: + * 125*clock_freq/sample_freq < 16384 + * * E.g. provided a clock frequency of ~ 1MHz, the sample frequency * can not be set lower than ~ 8kHz. - * A lower sample frequency would make the resampling code overfill its 16k sample ring buffer. - * - * The end of passband frequency is also limited: pass_freq <= 0.9*sample_freq/2 - * - * E.g. for a 44.1kHz sampling rate the end of passband frequency is limited - * to slightly below 20kHz. This constraint ensures that the FIR table is not overfilled. + * A lower sample frequency would make the resampling code overfill + * its 16k sample ring buffer. * * @param clockFrequency System clock frequency at Hz * @param samplingFrequency Desired output sampling rate - * @param highestAccurateFrequency + * @param highestAccurateFrequency passband frequency limit */ - SincResampler(double clockFrequency, double samplingFrequency, double highestAccurateFrequency); + SincResampler( + double clockFrequency, + double samplingFrequency, + double highestAccurateFrequency); + ~SincResampler() override; bool input(int input) override; diff --git a/src/sound/resid-fp/resample/TwoPassSincResampler.h b/src/sound/resid-fp/resample/TwoPassSincResampler.h index 81659193a..7ba28ea8e 100644 --- a/src/sound/resid-fp/resample/TwoPassSincResampler.h +++ b/src/sound/resid-fp/resample/TwoPassSincResampler.h @@ -51,14 +51,25 @@ private: public: // Named constructor - static TwoPassSincResampler* create(double clockFrequency, double samplingFrequency, double highestAccurateFrequency) + static TwoPassSincResampler* create(double clockFrequency, double samplingFrequency) { - // Calculation according to Laurent Ganier. It evaluates to about 120 kHz at typical settings. + // Set the passband frequency slightly below half sampling frequency + // pass_freq <= 0.9*sample_freq/2 + // + // This constraint ensures that the FIR table is not overfilled. + // For higher sampling frequencies we're fine with 20KHz + const double halfFreq = (samplingFrequency > 44000.) + ? 20000. : samplingFrequency * 0.45; + + // Calculation according to Laurent Ganier. + // It evaluates to about 120 kHz at typical settings. // Some testing around the chosen value seems to confirm that this does work. - double const intermediateFrequency = 2. * highestAccurateFrequency - + sqrt(2. * highestAccurateFrequency * clockFrequency - * (samplingFrequency - 2. * highestAccurateFrequency) / samplingFrequency); - return new TwoPassSincResampler(clockFrequency, samplingFrequency, highestAccurateFrequency, intermediateFrequency); + double const intermediateFrequency = 2. * halfFreq + + std::sqrt(2. * halfFreq * clockFrequency + * (samplingFrequency - 2. * halfFreq) / samplingFrequency); + + return new TwoPassSincResampler( + clockFrequency, samplingFrequency, halfFreq, intermediateFrequency); } bool input(int sample) override diff --git a/src/sound/resid-fp/resample/test.cpp b/src/sound/resid-fp/resample/test.cpp index b229e9e4d..d84e641d2 100644 --- a/src/sound/resid-fp/resample/test.cpp +++ b/src/sound/resid-fp/resample/test.cpp @@ -35,6 +35,10 @@ # define unique_ptr auto_ptr #endif +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + /** * Simple sin waveform in, power output measurement function. * It would be far better to use FFT. @@ -57,7 +61,7 @@ int main(int, const char*[]) for (int j = 0; j < RINGSIZE; j ++) { - int signal = static_cast(32768.0 * sin(k++ * omega) * sqrt(2)); + int signal = static_cast(32768.0 * std::sin(k++ * omega) * sqrt(2)); r->input(signal); } @@ -67,7 +71,7 @@ int main(int, const char*[]) /* Now, during measurement stage, put 100 cycles of waveform through filter. */ for (int j = 0; j < 100000; j ++) { - int signal = static_cast(32768.0 * sin(k++ * omega) * sqrt(2)); + int signal = static_cast(32768.0 * std::sin(k++ * omega) * std::sqrt(2)); if (r->input(signal)) { @@ -77,7 +81,7 @@ int main(int, const char*[]) } } - results.insert(std::make_pair(freq, 10 * log10(pwr / n))); + results.insert(std::make_pair(freq, 10 * std::log10(pwr / n))); } clock_t end = clock(); diff --git a/src/sound/resid-fp/sid.h b/src/sound/resid-fp/sid.h index 05ad83c3b..ef3dc71b3 100644 --- a/src/sound/resid-fp/sid.h +++ b/src/sound/resid-fp/sid.h @@ -1,7 +1,7 @@ /* * This file is part of libsidplayfp, a SID player engine. * - * Copyright 2011-2016 Leandro Nini + * Copyright 2011-2024 Leandro Nini * Copyright 2007-2010 Antti Lankila * Copyright 2004 Dag Lem * @@ -26,6 +26,9 @@ #include #include "siddefs-fp.h" +#include "ExternalFilter.h" +#include "Potentiometer.h" +#include "Voice.h" #include "sidcxx11.h" @@ -35,9 +38,6 @@ namespace reSIDfp class Filter; class Filter6581; class Filter8580; -class ExternalFilter; -class Potentiometer; -class Voice; class Resampler; /** @@ -64,28 +64,31 @@ private: Filter* filter; /// Filter used, if model is set to 6581 - std::unique_ptr const filter6581; + Filter6581* const filter6581; /// Filter used, if model is set to 8580 - std::unique_ptr const filter8580; + Filter8580* const filter8580; + + /// Resampler used by audio generation code. + std::unique_ptr resampler; /** * External filter that provides high-pass and low-pass filtering * to adjust sound tone slightly. */ - std::unique_ptr const externalFilter; - - /// Resampler used by audio generation code. - std::unique_ptr resampler; + ExternalFilter externalFilter; /// Paddle X register support - std::unique_ptr const potX; + Potentiometer potX; /// Paddle Y register support - std::unique_ptr const potY; + Potentiometer potY; /// SID voices - std::unique_ptr voice[3]; + Voice voice[3]; + + /// Used to amplify the output by x/2 to get an adequate playback volume + int scaleFactor; /// Time to live for the last written value int busValueTtl; @@ -99,12 +102,12 @@ private: /// Currently active chip model. ChipModel model; + /// Currently selected combined waveforms strength. + CombinedWaveforms cws; + /// Last written value unsigned char busValue; - /// Flags for muted channels - bool muted[3]; - /** * Emulated nonlinearity of the envelope DAC. * @@ -132,7 +135,7 @@ private: * * @return the output sample */ - int output() const; + int output(); /** * Calculate the numebr of cycles according to current parameters @@ -159,6 +162,14 @@ public: */ ChipModel getChipModel() const { return model; } + /** + * Set combined waveforms strength. + * + * @param cws strength of combined waveforms + * @throw SIDError + */ + void setCombinedWaveforms(CombinedWaveforms cws); + /** * SID reset. */ @@ -204,14 +215,6 @@ public: */ void write(int offset, unsigned char value); - /** - * SID voice muting. - * - * @param channel channel to modify - * @param enable is muted? - */ - void mute(int channel, bool enable) { muted[channel] = enable; } - /** * Setting of SID sampling parameters. * @@ -237,7 +240,11 @@ public: * @param highestAccurateFrequency * @throw SIDError */ - void setSamplingParameters(double clockFrequency, SamplingMethod method, double samplingFrequency, double highestAccurateFrequency); + void setSamplingParameters( + double clockFrequency, + SamplingMethod method, + double samplingFrequency + ); /** * Clock SID forward using chosen output sampling algorithm. @@ -267,6 +274,13 @@ public: */ void setFilter6581Curve(double filterCurve); + /** + * Set filter range parameter for 6581 model + * + * @see Filter6581::setFilterRange(double) + */ + void setFilter6581Range ( double adjustment ); + /** * Set filter curve parameter for 8580 model. * @@ -312,13 +326,22 @@ void SID::ageBusValue(unsigned int n) } RESID_INLINE -int SID::output() const +int SID::output() { - const int v1 = voice[0]->output(voice[2]->wave()); - const int v2 = voice[1]->output(voice[0]->wave()); - const int v3 = voice[2]->output(voice[1]->wave()); + const float o1 = voice[0].output(voice[2].wave()); + const float o2 = voice[1].output(voice[0].wave()); + const float o3 = voice[2].output(voice[1].wave()); - return externalFilter->clock(filter->clock(v1, v2, v3)); + const unsigned int env1 = voice[0].envelope()->output(); + const unsigned int env2 = voice[1].envelope()->output(); + const unsigned int env3 = voice[2].envelope()->output(); + + const int v1 = filter->getNormalizedVoice(o1, env1); + const int v2 = filter->getNormalizedVoice(o2, env2); + const int v3 = filter->getNormalizedVoice(o3, env3); + + const int input = static_cast(filter->clock(v1, v2, v3)); + return externalFilter.clock(input); } @@ -337,18 +360,18 @@ int SID::clock(unsigned int cycles, short* buf) for (unsigned int i = 0; i < delta_t; i++) { // clock waveform generators - voice[0]->wave()->clock(); - voice[1]->wave()->clock(); - voice[2]->wave()->clock(); + voice[0].wave()->clock(); + voice[1].wave()->clock(); + voice[2].wave()->clock(); // clock envelope generators - voice[0]->envelope()->clock(); - voice[1]->envelope()->clock(); - voice[2]->envelope()->clock(); + voice[0].envelope()->clock(); + voice[1].envelope()->clock(); + voice[2].envelope()->clock(); if (unlikely(resampler->input(output()))) { - buf[s++] = resampler->getOutput(); + buf[s++] = resampler->getOutput(scaleFactor); } } diff --git a/src/sound/resid-fp/siddefs-fp.h b/src/sound/resid-fp/siddefs-fp.h index 7061e3a85..9411b1694 100644 --- a/src/sound/resid-fp/siddefs-fp.h +++ b/src/sound/resid-fp/siddefs-fp.h @@ -26,10 +26,6 @@ // Compiler specifics. #define HAVE_BUILTIN_EXPECT true -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - // Branch prediction macros, lifted off the Linux kernel. #if RESID_BRANCH_HINTS && HAVE_BUILTIN_EXPECT # define likely(x) __builtin_expect(!!(x), 1) @@ -43,6 +39,8 @@ namespace reSIDfp { typedef enum { MOS6581=1, MOS8580 } ChipModel; +typedef enum { AVERAGE=1, WEAK, STRONG } CombinedWaveforms; + typedef enum { DECIMATE=1, RESAMPLE } SamplingMethod; } diff --git a/src/sound/resid-fp/siddefs-fp.h.in b/src/sound/resid-fp/siddefs-fp.h.in index 4c31ffb46..dfe543db5 100644 --- a/src/sound/resid-fp/siddefs-fp.h.in +++ b/src/sound/resid-fp/siddefs-fp.h.in @@ -26,10 +26,6 @@ // Compiler specifics. #define HAVE_BUILTIN_EXPECT @HAVE_BUILTIN_EXPECT@ -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - // Branch prediction macros, lifted off the Linux kernel. #if RESID_BRANCH_HINTS && HAVE_BUILTIN_EXPECT # define likely(x) __builtin_expect(!!(x), 1) @@ -43,6 +39,8 @@ namespace reSIDfp { typedef enum { MOS6581=1, MOS8580 } ChipModel; +typedef enum { AVERAGE=1, WEAK, STRONG } CombinedWaveforms; + typedef enum { DECIMATE=1, RESAMPLE } SamplingMethod; } diff --git a/src/sound/snd_opl.c b/src/sound/snd_opl.c index d98b3ccc2..cddc18119 100644 --- a/src/sound/snd_opl.c +++ b/src/sound/snd_opl.c @@ -58,7 +58,12 @@ fm_driver_get(int chip_id, fm_drv_t *drv) drv->priv = device_add_inst(&ymf262_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); } break; - +#ifdef USE_LIBSERIALPORT + case FM_OPL2BOARD: + *drv = ymfm_opl2board_drv; + drv->priv = device_add_inst(&ym_opl2board_device, fm_dev_inst[fm_driver][chip_id]++); + break; +#endif case FM_YMF289B: *drv = ymfm_drv; drv->priv = device_add_inst(&ymf289b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++); diff --git a/src/sound/snd_opl2board.c b/src/sound/snd_opl2board.c new file mode 100644 index 000000000..efd408b97 --- /dev/null +++ b/src/sound/snd_opl2board.c @@ -0,0 +1,194 @@ +/* + * 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. + * + * Interface to the OPL2Board External audio device (USB) + * + * + * Authors: Jose Phillips + * Fred N. van Kempen, + * Miran Grca, + * + * Copyright 2024 Jose Phillips. + * Copyright 2017-2020 Fred N. van Kempen. + * Copyright 2016-2020 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/mca.h> +#include <86box/sound.h> +#include <86box/timer.h> +#include <86box/snd_opl.h> +#include <86box/plat_unused.h> + + +#ifdef ENABLE_OPL2DEVICE_LOG +int opl2board_device_do_log = ENABLE_OPL2DEVICE_LOG; + +static void +opl2board_device_log(const char *fmt, ...) +{ + va_list ap; + + if (opl2board_device_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define opl2board_device_log(fmt, ...) +#endif + +typedef struct opl2board_device_t { + fm_drv_t opl; + uint8_t pos_regs[8]; +} opl2board_device_t; + +static void +opl2board_device_get_buffer(int32_t *buffer, int len, void *priv) +{ + opl2board_device_t *serial = (opl2board_device_t *) priv; + + const int32_t *opl_buf = serial->opl.update(serial->opl.priv); + + for (int c = 0; c < len * 2; c++) + buffer[c] += opl_buf[c]; + + serial->opl.reset_buffer(serial->opl.priv); +} + +uint8_t +opl2board_device_mca_read(int port, void *priv) +{ + const opl2board_device_t *serial = (opl2board_device_t *) priv; + + opl2board_device_log("opl2board_device_mca_read: port=%04x\n", port); + + return serial->pos_regs[port & 7]; +} + +void +opl2board_device_mca_write(int port, uint8_t val, void *priv) +{ + opl2board_device_t *serial = (opl2board_device_t *) priv; + + if (port < 0x102) + return; + + opl2board_device_log("opl2board_device_mca_write: port=%04x val=%02x\n", port, val); + + switch (port) { + case 0x102: + if ((serial->pos_regs[2] & 1) && !(val & 1)) + io_removehandler(0x0388, 0x0002, + serial->opl.read, NULL, NULL, + serial->opl.write, NULL, NULL, + serial->opl.priv); + if (!(serial->pos_regs[2] & 1) && (val & 1)) + io_sethandler(0x0388, 0x0002, + serial->opl.read, NULL, NULL, + serial->opl.write, NULL, NULL, + serial->opl.priv); + break; + + default: + break; + } + + serial->pos_regs[port & 7] = val; +} + +uint8_t +opl2board_device_mca_feedb(void *priv) +{ + const opl2board_device_t *serial = (opl2board_device_t *) priv; + + return (serial->pos_regs[2] & 1); +} + +void * +opl2board_device_init(UNUSED(const device_t *info)) +{ + opl2board_device_t *serial = malloc(sizeof(opl2board_device_t)); + memset(serial, 0, sizeof(opl2board_device_t)); + + opl2board_device_log("opl2board_device_init\n"); + fm_driver_get(FM_OPL2BOARD, &serial->opl); + io_sethandler(0x0388, 0x0002, + serial->opl.read, NULL, NULL, + serial->opl.write, NULL, NULL, + serial->opl.priv); + music_add_handler(opl2board_device_get_buffer, serial); + + return serial; +} + +void * +opl2board_device_mca_init(const device_t *info) +{ + opl2board_device_t *serial = opl2board_device_init(info); + + io_removehandler(0x0388, 0x0002, + serial->opl.read, NULL, NULL, + serial->opl.write, NULL, NULL, + serial->opl.priv); + mca_add(opl2board_device_mca_read, + opl2board_device_mca_write, + opl2board_device_mca_feedb, + NULL, + serial); + serial->pos_regs[0] = 0xd7; + serial->pos_regs[1] = 0x70; + + return serial; +} + +void +opl2board_device_close(void *priv) +{ + opl2board_device_t *serial = (opl2board_device_t *) priv; + free(serial); +} + + +static const device_config_t opl2board_config[] = { +{ + .name = "host_serial_path", + .description = "Host Serial Device", + .type = CONFIG_SERPORT, + .default_string = "", + .file_filter = NULL, + .spinner = {}, + .selection = {} + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +const device_t opl2board_device = { + .name = "OPL2Board (External Device)", + .internal_name = "opl2board_device", + .flags = DEVICE_ISA, + .local = 0, + .init = opl2board_device_init, + .close = opl2board_device_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = opl2board_config +}; diff --git a/src/sound/snd_opl_opl2board.cpp b/src/sound/snd_opl_opl2board.cpp new file mode 100644 index 000000000..91649f372 --- /dev/null +++ b/src/sound/snd_opl_opl2board.cpp @@ -0,0 +1,550 @@ +/* + * 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. + * + * Interface to the YMFM External audio device (USB) + * For OPL2Board arduino based. + * + * Authors: Jose Phillips, + * Adrien Moulin, + * + * Copyright 2024 Jose Phillips. + * Copyright 2022 Adrien Moulin. + */ +#include +#include +#include +#include +#include +#include "ymfm/ymfm_opl.h" +#include + + +extern "C" { +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/timer.h> +#include <86box/device.h> +#include <86box/sound.h> +#include <86box/snd_opl.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/plat_unused.h> +#include <86box/config.h> +#include <86box/ini.h> +#include <86box/device.h> + + +// Disable c99-designator to avoid the warnings in *_ymfm_device +#ifdef __clang__ +# if __has_warning("-Wc99-designator") +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wc99-designator" +# endif +#endif + +} + + +#define RSM_FRAC 10 + +#define OPL_FREQ FREQ_48000 + +enum { + FLAG_CYCLES = (1 << 0) +}; + +uint8_t lastval = 0x00; + + +class OPLBOARDChipBase { +public: + OPLBOARDChipBase(UNUSED(uint32_t clock), fm_type type, uint32_t samplerate) + : m_buf_pos(0) + , m_flags(0) + , m_type(type) + , m_samplerate(samplerate) + { + memset(m_buffer, 0, sizeof(m_buffer)); + } + + virtual ~OPLBOARDChipBase() + { + } + + fm_type type() const { return m_type; } + int8_t flags() const { return m_flags; } + void set_do_cycles(int8_t do_cycles) { do_cycles ? m_flags |= FLAG_CYCLES : m_flags &= ~FLAG_CYCLES; } + int32_t *buffer() const { return (int32_t *) m_buffer; } + void reset_buffer() { m_buf_pos = 0; } + + virtual uint32_t sample_rate() const = 0; + + virtual void write(uint16_t addr, uint8_t data) = 0; + virtual void generate(int32_t *data, uint32_t num_samples) = 0; + virtual int32_t *update() = 0; + virtual uint8_t read(uint16_t addr) = 0; + virtual void set_clock(uint32_t clock) = 0; + + +protected: + int32_t m_buffer[MUSICBUFLEN * 2]; + int m_buf_pos; + int *m_buf_pos_global; + int8_t m_flags; + fm_type m_type; + uint32_t m_samplerate; +}; + +template +class OPLBOARDChip : public OPLBOARDChipBase, public ymfm::ymfm_interface { +public: + OPLBOARDChip(uint32_t clock, fm_type type, uint32_t samplerate) + : OPLBOARDChipBase(clock, type, samplerate) + , m_chip(*this) + , m_clock(clock) + , m_samplerate(samplerate) + , m_samplecnt(0) + { + memset(m_samples, 0, sizeof(m_samples)); + memset(m_oldsamples, 0, sizeof(m_oldsamples)); + m_rateratio = (samplerate << RSM_FRAC) / m_chip.sample_rate(m_clock); + m_clock_us = 1000000.0 / (double) m_clock; + m_subtract[0] = 80.0; + m_subtract[1] = 320.0; + m_type = type; + m_buf_pos_global = (samplerate == FREQ_49716) ? &music_pos_global : &wavetable_pos_global; + + if (m_type == FM_YMF278B) { + if (rom_load_linear("roms/sound/yamaha/yrw801.rom", 0, 0x200000, 0, m_yrw801) == 0) { + fatal("YRW801 ROM image \"roms/sound/yamaha/yrw801.rom\" not found\n"); + } + } + + timer_add(&m_timers[0], OPLBOARDChip::timer1, this, 0); + timer_add(&m_timers[1], OPLBOARDChip::timer2, this, 0); + } + + virtual uint32_t sample_rate() const override + { + return m_chip.sample_rate(m_clock); + } + + virtual void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) override + { + if (tnum > 1) + return; + + m_duration_in_clocks[tnum] = duration_in_clocks; + pc_timer_t *timer = &m_timers[tnum]; + if (duration_in_clocks < 0) + timer_stop(timer); + else { + double period = m_clock_us * duration_in_clocks; + if (period < m_subtract[tnum]) + m_engine->engine_timer_expired(tnum); + else + timer_on_auto(timer, period); + } + } + + virtual void set_clock(uint32_t clock) override + { + m_clock = clock; + m_clock_us = 1000000.0 / (double) m_clock; + m_rateratio = (m_samplerate << RSM_FRAC) / m_chip.sample_rate(m_clock); + + ymfm_set_timer(0, m_duration_in_clocks[0]); + ymfm_set_timer(1, m_duration_in_clocks[1]); + } + + virtual void generate(int32_t *data, uint32_t num_samples) override + { + for (uint32_t i = 0; i < num_samples; i++) { + m_chip.generate(&m_output); + if ((m_type == FM_YMF278B) && (sizeof(m_output.data) > (4 * sizeof(int32_t)))) { + if (ChipType::OUTPUTS == 1) { + *data++ = m_output.data[4]; + *data++ = m_output.data[4]; + } else { + *data++ = m_output.data[4]; + *data++ = m_output.data[5]; + } + } else if (ChipType::OUTPUTS == 1) { + *data++ = m_output.data[0]; + *data++ = m_output.data[0]; + } else { + *data++ = m_output.data[0]; + *data++ = m_output.data[1 % ChipType::OUTPUTS]; + } + } + } + +#if 0 + virtual void generate_resampled(int32_t *data, uint32_t num_samples) override + { + if ((m_samplerate == FREQ_49716) || (m_samplerate == FREQ_44100)) { + generate(data, num_samples); + return; + } + + for (uint32_t i = 0; i < num_samples; i++) { + while (m_samplecnt >= m_rateratio) { + m_oldsamples[0] = m_samples[0]; + m_oldsamples[1] = m_samples[1]; + m_chip.generate(&m_output); + if ((m_type == FM_YMF278B) && (sizeof(m_output.data) > (4 * sizeof(int32_t)))) { + if (ChipType::OUTPUTS == 1) { + m_samples[0] = m_output.data[4]; + m_samples[1] = m_output.data[4]; + } else { + m_samples[0] = m_output.data[4]; + m_samples[1] = m_output.data[5]; + } + } else if (ChipType::OUTPUTS == 1) { + m_samples[0] = m_output.data[0]; + m_samples[1] = m_output.data[0]; + } else { + m_samples[0] = m_output.data[0]; + m_samples[1] = m_output.data[1 % ChipType::OUTPUTS]; + } + m_samplecnt -= m_rateratio; + } + + *data++ = ((int32_t) ((m_oldsamples[0] * (m_rateratio - m_samplecnt) + + m_samples[0] * m_samplecnt) + / m_rateratio)); + *data++ = ((int32_t) ((m_oldsamples[1] * (m_rateratio - m_samplecnt) + + m_samples[1] * m_samplecnt) + / m_rateratio)); + + m_samplecnt += 1 << RSM_FRAC; + } + } +#endif + + virtual int32_t *update() override + { + if (m_buf_pos >= *m_buf_pos_global) + return m_buffer; + + generate(&m_buffer[m_buf_pos * 2], *m_buf_pos_global - m_buf_pos); + + for (; m_buf_pos < *m_buf_pos_global; m_buf_pos++) { + m_buffer[m_buf_pos * 2] /= 2; + m_buffer[(m_buf_pos * 2) + 1] /= 2; + } + + return m_buffer; + } + + virtual void write(uint16_t addr, uint8_t data) override + { + + m_chip.write(addr, data); + } + + virtual uint8_t read(uint16_t addr) override + { + return m_chip.read(addr); + } + + virtual uint32_t get_special_flags(void) override + { + return ((m_type == FM_YMF262) || (m_type == FM_YMF289B) || (m_type == FM_YMF278B)) ? 0x8000 : 0x0000; + } + + static void timer1(void *priv) + { + OPLBOARDChip *drv = (OPLBOARDChip *) priv; + drv->m_engine->engine_timer_expired(0); + } + + static void timer2(void *priv) + { + OPLBOARDChip *drv = (OPLBOARDChip *) priv; + drv->m_engine->engine_timer_expired(1); + } + + virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address) override + { + if (type == ymfm::access_class::ACCESS_PCM && address < 0x200000) { + return m_yrw801[address]; + } + return 0xFF; + } + +private: + ChipType m_chip; + uint32_t m_clock; + double m_clock_us; + double m_subtract[2]; + typename ChipType::output_data m_output; + pc_timer_t m_timers[2]; + int32_t m_duration_in_clocks[2]; // Needed for clock switches. + uint32_t m_samplerate; + + // YRW801-M wavetable ROM. + uint8_t m_yrw801[0x200000]; + + // Resampling + int32_t m_rateratio; + int32_t m_samplecnt; + int32_t m_oldsamples[2]; + int32_t m_samples[2]; +}; + +extern "C" { +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H + +#include "cpu.h" +#include <86box/86box.h> +#include <86box/io.h> +#include <86box/snd_opl.h> +#include <86box/device.h> +#include <86box/config.h> +#include <86box/ini.h> + + +#ifdef ENABLE_OPL_LOG +int ymfm_do_log = ENABLE_OPL_LOG; + +static void +ymfm_log(const char *fmt, ...) +{ + va_list ap; + + if (ymfm_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define ymfm_log(fmt, ...) +#endif + +struct sp_port *port; + + + +void opl2board_init() { + + device_add(&opl2board_device); + const char* port_name = device_get_config_string("host_serial_path"); + device_context_restore(); + + enum sp_return result; + + result = sp_get_port_by_name(port_name, &port); + if (result != SP_OK) { + ymfm_log("Error: Cannot find port %s\n", port_name); + return; + } + + result = sp_open(port, SP_MODE_READ_WRITE); + if (result != SP_OK) { + ymfm_log ("Error: Cannot open port %s\n", port_name); + return; + } + + // Set port configuration this values are hardcoded. + sp_set_baudrate(port, 115200); + sp_set_bits(port, 8); + sp_set_parity(port, SP_PARITY_NONE); + sp_set_stopbits(port, 1); + sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE); + + + ymfm_log("OPL2Board Serial port %s initialized at 115200 baud.\n", port_name); + +} + + +void opl2board_write(uint8_t data) { + if (port == NULL) { + ymfm_log(stderr, "Error: OPL2Board Port not initialized.\n"); + + return; + } + + enum sp_return result = sp_blocking_write(port, &data, sizeof(data), 1000); + if (result < 0) { + ymfm_log(stderr, "Error: Failed to write to OPL2Board port.\n"); + } else { + ymfm_log("OPL2Board: data sent: %02X\n", data); + } +} + + +void opl2board_reset() { + + // Reset all voices to 0 + ymfm_log("Performing OPL2Board reset\n"); + for (uint8_t i = 0x00; i < 0xFF; i++) { + if (i >= 0x40 && i <= 0x55) { + opl2board_write(i); + opl2board_write(0x3F); + } else { + opl2board_write (i); + opl2board_write(0x00); + } } +} + +void opl2board_close() { + + if (port != NULL) { + opl2board_reset(); + sp_close(port); + sp_free_port(port); + port = NULL; + ymfm_log("OPL2Board port closed.\n"); + } +} + + +static void * +ymfm_opl2board_drv_init(const device_t *info) +{ + OPLBOARDChipBase *fm; + + switch (info->local) { + default: + case FM_OPL2BOARD: + fm = (OPLBOARDChipBase *) new OPLBOARDChip(3579545, FM_OPL2BOARD, FREQ_49716); + break; + } + fm->set_do_cycles(1); + + return fm; +} + +static void +ymfm_opl2board_drv_close(void *priv) +{ + OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv; + opl2board_close(); + if (drv != NULL) + delete drv; +} + +static uint8_t +ymfm_opl2board_drv_read(uint16_t port, void *priv) +{ + OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv; + + if ((port == 0x380) || (port == 0x381)) + port |= 4; + + /* Point to register read port. */ + if (drv->flags() & FLAG_CYCLES) + cycles -= ((int) (isa_timing * 8)); + + uint8_t ret = drv->read(port); + drv->update(); + + ymfm_log("YMFM read port %04x, status = %02x\n", port, ret); + return ret; +} + +static void +ymfm_opl2board_drv_write(uint16_t port, uint8_t val, void *priv) +{ + + OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv; + + ymfm_log("YMFM write port %04x value = %02x\n", port, val); + if ((port == 0x380) || (port == 0x381)) + port |= 4; + // Allow initialization of adlib + if ((val == 0x04 || val == 0x02) || (lastval == 0x04 || lastval == 0x02)) { + drv->write(port, val); + } + lastval = val; + opl2board_write(val); + drv->update(); +} + + +static int32_t * +ymfm_opl2board_drv_update(void *priv) +{ + if (port == NULL) { + opl2board_init(); + opl2board_reset(); + } + OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv; + + return drv->update(); +} + +static void +ymfm_opl2board_drv_reset_buffer(void *priv) +{ + OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv; + + drv->reset_buffer(); +} + +static void +ymfm_opl2board_drv_set_do_cycles(void *priv, int8_t do_cycles) +{ + OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv; + drv->set_do_cycles(do_cycles); +} + +static void +ymfm_opl2board_drv_generate(void *priv, int32_t *data, uint32_t num_samples) +{ + + OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv; + // drv->generate_resampled(data, num_samples); + drv->generate(data, num_samples); + +} + + + + +const device_t ym_opl2board_device = { + .name = "YMOPL2Board (External Device)", + .internal_name = "ym_opl2board_device", + .flags = 0, + .local = FM_OPL2BOARD, + .init = ymfm_opl2board_drv_init, + .close = ymfm_opl2board_drv_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const fm_drv_t ymfm_opl2board_drv { + &ymfm_opl2board_drv_read, + &ymfm_opl2board_drv_write, + &ymfm_opl2board_drv_update, + &ymfm_opl2board_drv_reset_buffer, + &ymfm_opl2board_drv_set_do_cycles, + NULL, + ymfm_opl2board_drv_generate + +}; + +#ifdef __clang__ +# if __has_warning("-Wc99-designator") +# pragma clang diagnostic pop +# endif +#endif + +} diff --git a/src/sound/snd_resid.cpp b/src/sound/snd_resid.cpp index d7082e47e..b9895cf7e 100644 --- a/src/sound/snd_resid.cpp +++ b/src/sound/snd_resid.cpp @@ -20,22 +20,21 @@ typedef struct psid_t { psid_t *psid; void * -sid_init(void) +sid_init(uint8_t type) { -#if 0 - psid_t *psid; -#endif - reSIDfp::SamplingMethod method = reSIDfp::DECIMATE; + reSIDfp::SamplingMethod method = reSIDfp::RESAMPLE; float cycles_per_sec = 14318180.0 / 16.0; - psid = new psid_t; -#if 0 - psid = (psid_t *) malloc(sizeof(sound_t)); -#endif + psid = new psid_t; psid->sid = new SID; - psid->sid->setChipModel(reSIDfp::MOS8580); - psid->sid->enableFilter(true); + switch (type) { + default: + case 0: + psid->sid->setChipModel(reSIDfp::MOS6581); + case 1: + psid->sid->setChipModel(reSIDfp::MOS8580); + } psid->sid->reset(); @@ -43,14 +42,13 @@ sid_init(void) psid->sid->write(c, 0); try { - psid->sid->setSamplingParameters(cycles_per_sec, method, (float) RESID_FREQ, 0.9 * (float) RESID_FREQ / 2.0); + psid->sid->setSamplingParameters(cycles_per_sec, method, (float) RESID_FREQ); } catch (reSIDfp::SIDError) { #if 0 printf("reSID failed!\n"); #endif } - psid->sid->setChipModel(reSIDfp::MOS6581); psid->sid->input(0); return (void *) psid; @@ -59,9 +57,6 @@ sid_init(void) void sid_close(UNUSED(void *priv)) { -#if 0 - psid_t *psid = (psid_t *) priv; -#endif delete psid->sid; #if 0 free(psid); @@ -71,10 +66,6 @@ sid_close(UNUSED(void *priv)) void sid_reset(UNUSED(void *priv)) { -#if 0 - psid_t *psid = (psid_t *) priv; -#endif - psid->sid->reset(); for (uint8_t c = 0; c < 32; c++) @@ -84,23 +75,12 @@ sid_reset(UNUSED(void *priv)) uint8_t sid_read(uint16_t addr, UNUSED(void *priv)) { -#if 0 - psid_t *psid = (psid_t *) priv; -#endif - return psid->sid->read(addr & 0x1f); -#if 0 - return 0xFF; -#endif } void sid_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) { -#if 0 - psid_t *psid = (psid_t *) priv; -#endif - psid->sid->write(addr & 0x1f, val); } @@ -118,9 +98,6 @@ fillbuf2(int &count, int16_t *buf, int len) void sid_fillbuf(int16_t *buf, int len, UNUSED(void *priv)) { -#if 0 - psid_t *psid = (psid_t *) priv; -#endif int x = CLOCK_DELTA(len); fillbuf2(x, buf, len); diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 6536071b0..0a01a4be5 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -1452,9 +1452,6 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) if (!(addr & 1)) { mixer->index = val; mixer->regs[0x01] = val; - if (val == 0x40) { - mixer->ess_id_str_pos = 0; - } } else { if (mixer->index == 0) { /* Reset */ @@ -1473,6 +1470,8 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) mixer->regs[0x3c] = 0x05; mixer->regs[0x3e] = 0x00; + mixer->regs[0x64] = 0x08; + sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2); } else { mixer->regs[mixer->index] = val; @@ -1525,6 +1524,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) break; case 0x1C: + mixer->regs[mixer->index] = val & 0x2f; if ((mixer->regs[0x1C] & 0x07) == 0x07) { mixer->input_selector = INPUT_MIXER_L | INPUT_MIXER_R; } else if ((mixer->regs[0x1C] & 0x07) == 0x06) { @@ -1556,12 +1556,12 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) break; case 0x64: - mixer->regs[mixer->index] = (mixer->regs[mixer->index] & 0xf7) | 0x20; - // mixer->regs[mixer->index] &= ~0x8; + if (ess->dsp.sb_subtype > SB_SUBTYPE_ESS_ES1688) + mixer->regs[mixer->index] = (mixer->regs[mixer->index] & 0xf7) | 0x20; break; case 0x40: - { + if (ess->dsp.sb_subtype >= SB_SUBTYPE_ESS_ES1688) { uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30); sb_log("mpu401_base_addr = %04X\n", mpu401_base_addr); @@ -1572,7 +1572,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); - if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) { + if (ess->dsp.sb_subtype > SB_SUBTYPE_ESS_ES1688) { /* Not on ES1688. */ io_removehandler(0x0388, 0x0004, ess->opl.read, NULL, NULL, @@ -1585,6 +1585,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) ess->opl.priv); } } + if (ess->mpu != NULL) switch ((mixer->regs[0x40] >> 5) & 0x7) { default: break; @@ -1627,11 +1628,12 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) ess_fm_midi_read, NULL, NULL, ess_fm_midi_write, NULL, NULL, ess); - break; } + break; default: - sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n", + mixer->index, mixer->regs[mixer->index]); break; } } @@ -1667,6 +1669,7 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x0c: case 0x0e: case 0x14: + case 0x1a: case 0x02: case 0x06: case 0x30: @@ -1685,20 +1688,48 @@ ess_mixer_read(uint16_t addr, void *priv) ret = mixer->regs[mixer->index] | 0x11; break; - case 0x40: - if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) - ret = mixer->regs[mixer->index]; - else - ret = 0x0a; + /* Bit 1 always set, bits 7-6 always clear on both the real ES688 and ES1688. */ + case 0x1c: + ret = mixer->regs[mixer->index] | 0x10; break; - case 0x48: - ret = mixer->regs[mixer->index]; - break; + /* + Real ES688: Always 0x00; + Real ES1688: Bit 2 always clear. + */ + case 0x40: + if (ess->dsp.sb_subtype > SB_SUBTYPE_ESS_ES1688) + ret = mixer->regs[mixer->index]; + else if (ess->dsp.sb_subtype >= SB_SUBTYPE_ESS_ES1688) + ret = mixer->regs[mixer->index] & 0xfb; + else + ret = 0x00; + break; - /* Return 0x00 so it has bit 3 clear, so NT 5.x drivers don't misdetect it as ES1788. */ + /* + Real ES688: Always 0x00; + Real ES1688: All bits writable. + */ + case 0x48: + if (ess->dsp.sb_subtype >= SB_SUBTYPE_ESS_ES1688) + ret = mixer->regs[mixer->index]; + else + ret = 0x00; + break; + + /* + Return 0x00 so it has bit 3 clear, so NT 5.x drivers don't misdetect it as ES1788. + Bit 3 set and writable: ESSCFG detects the card as ES1788 if register 70h is read-only, + otherwise, as ES1887. + Bit 3 set and read-only: ESSCFG detects the card as ES1788 if register 70h is read-only, + otherwise, as ES1888. + Real ES688 and ES1688: Always 0x00. + */ case 0x64: - ret = (mixer->regs[mixer->index] & 0xf7) | 0x20; + if (ess->dsp.sb_subtype > SB_SUBTYPE_ESS_ES1688) + ret = (mixer->regs[mixer->index] & 0xf7) | 0x20; + else + ret = 0x00; break; default: @@ -1706,7 +1737,7 @@ ess_mixer_read(uint16_t addr, void *priv) break; } - sb_log("[%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, addr, ret); + sb_log("[%04X:%08X] [R] %04X = %02X (%02X)\n", CS, cpu_state.pc, addr, ret, mixer->index); return ret; } @@ -2268,9 +2299,6 @@ ess_x688_pnp_config_changed(UNUSED(const uint8_t ld), isapnp_device_config_t *co ess_base_write, NULL, NULL, ess); - ess->mixer_ess.ess_id_str[2] = 0x00; - ess->mixer_ess.ess_id_str[3] = 0x00; - addr = ess->opl_pnp_addr; if (addr) { ess->opl_pnp_addr = 0; @@ -2337,9 +2365,6 @@ ess_x688_pnp_config_changed(UNUSED(const uint8_t ld), isapnp_device_config_t *co ess_base_read, NULL, NULL, ess_base_write, NULL, NULL, ess); - - ess->mixer_ess.ess_id_str[2] = (addr >> 8) & 0xff; - ess->mixer_ess.ess_id_str[3] = addr & 0xff; } addr = config->io[1].base; @@ -3748,11 +3773,6 @@ ess_x688_init(UNUSED(const device_t *info)) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp); if (info->local) { - ess->mixer_ess.ess_id_str[0] = 0x16; - ess->mixer_ess.ess_id_str[1] = 0x88; - ess->mixer_ess.ess_id_str[2] = (addr >> 8) & 0xff; - ess->mixer_ess.ess_id_str[3] = addr & 0xff; - ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); /* NOTE: The MPU is initialized disabled and with no IRQ assigned. * It will be later initialized by the guest OS's drivers. */ @@ -3818,12 +3838,6 @@ ess_x688_pnp_init(UNUSED(const device_t *info)) if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp); - /* Not on ES688. */ - ess->mixer_ess.ess_id_str[0] = 0x16; - ess->mixer_ess.ess_id_str[1] = 0x88; - ess->mixer_ess.ess_id_str[2] = 0x00; - ess->mixer_ess.ess_id_str[3] = 0x00; - ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); /* NOTE: The MPU is initialized disabled and with no IRQ assigned. * It will be later initialized by the guest OS's drivers. */ diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 4cfd2c7bb..0063f0ae7 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -1687,6 +1687,10 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0xE1: /* Get DSP version */ if (IS_ESS(dsp)) { + /* + 0x03 0x01 (Sound Blaster Pro compatibility) confirmed by both the + ES1888 datasheet and the probing of the real ES688 and ES1688 cards. + */ sb_add_data(dsp, 0x3); sb_add_data(dsp, 0x1); break; @@ -1722,9 +1726,12 @@ sb_exec_command(sb_dsp_t *dsp) while (sb16_copyright[c]) sb_add_data(dsp, sb16_copyright[c++]); sb_add_data(dsp, 0); - } else if (IS_ESS(dsp)) { - sb_add_data(dsp, 0); - } + } /* else if (IS_ESS(dsp)) + sb_add_data(dsp, 0); */ + /* + TODO: What ESS card returns 0x00 here? Probing of the real ES688 and ES1688 cards + revealed that they in fact return nothing on this command. + */ break; case 0xE4: /* Write test register */ dsp->sb_test = dsp->sb_data[0]; @@ -1736,12 +1743,26 @@ sb_exec_command(sb_dsp_t *dsp) break; case SB_SUBTYPE_ESS_ES688: sb_add_data(dsp, 0x68); - sb_add_data(dsp, 0x80 | 0x04); + /* + 80h: ESSCFG fails to detect the AudioDrive; + 81h-83h: ES??88, Windows 3.1 driver expects MPU-401 and gives a legacy mixer error; + 84h: ES688, Windows 3.1 driver expects MPU-401, returned by DOSBox-X; + 85h-87h: ES688, Windows 3.1 driver does not expect MPU-401: + 85h: Returned by MSDOS622's real ESS688, + 86h: Returned by Dizzy's real ES688. + We return 86h if MPU is absent, 84h otherwise, who knows what the actual + PnP ES688 returns here. + */ + sb_add_data(dsp, 0x80 | ((dsp->mpu != NULL) ? 0x04 : 0x06)); break; case SB_SUBTYPE_ESS_ES1688: - // Determined via Windows driver debugging. sb_add_data(dsp, 0x68); - sb_add_data(dsp, 0x80 | 0x09); + /* + 89h: ES1688, returned by DOSBox-X, determined via Windows driver + debugging; + 8Bh: ES1688, returned by both MSDOS622's and Dizzy's real ES1688's. + */ + sb_add_data(dsp, 0x80 | 0x0b); break; } } diff --git a/src/sound/snd_ssi2001.c b/src/sound/snd_ssi2001.c index 1f3c294ce..11a10473c 100644 --- a/src/sound/snd_ssi2001.c +++ b/src/sound/snd_ssi2001.c @@ -21,6 +21,10 @@ typedef struct ssi2001_t { int gameport_enabled; } ssi2001_t; +typedef struct entertainer_t { + uint8_t regs; +} entertainer_t; + static void ssi2001_update(ssi2001_t *ssi2001) { @@ -69,7 +73,7 @@ ssi2001_init(UNUSED(const device_t *info)) ssi2001_t *ssi2001 = malloc(sizeof(ssi2001_t)); memset(ssi2001, 0, sizeof(ssi2001_t)); - ssi2001->psid = sid_init(); + ssi2001->psid = sid_init(0); sid_reset(ssi2001->psid); uint16_t addr = device_get_config_hex16("base"); ssi2001->gameport_enabled = device_get_config_int("gameport"); @@ -90,6 +94,48 @@ ssi2001_close(void *priv) free(ssi2001); } +static uint8_t +entertainer_read(uint16_t addr, void *priv) +{ + return 0xa5; +} + +static void +entertainer_write(uint16_t addr, uint8_t val, void *priv) +{ + entertainer_t *entertainer = (entertainer_t *) priv; + entertainer->regs = val; +} + +void * +entertainer_init(UNUSED(const device_t *info)) +{ + ssi2001_t *ssi2001 = malloc(sizeof(ssi2001_t)); + entertainer_t *entertainer = malloc(sizeof(entertainer_t)); + memset(ssi2001, 0, sizeof(ssi2001_t)); + memset(entertainer, 0, sizeof(entertainer_t)); + + ssi2001->psid = sid_init(0); + sid_reset(ssi2001->psid); + ssi2001->gameport_enabled = device_get_config_int("gameport"); + io_sethandler(0x200, 0x0001, entertainer_read, NULL, NULL, entertainer_write, NULL, NULL, entertainer); + io_sethandler(0x280, 0x0020, ssi2001_read, NULL, NULL, ssi2001_write, NULL, NULL, ssi2001); + if (ssi2001->gameport_enabled) + gameport_remap(gameport_add(&gameport_201_device), 0x201); + sound_add_handler(ssi2001_get_buffer, ssi2001); + return ssi2001; +} + +void +entertainer_close(void *priv) +{ + ssi2001_t *ssi2001 = (ssi2001_t *) priv; + + sid_close(ssi2001->psid); + + free(ssi2001); +} + static const device_config_t ssi2001_config[] = { // clang-format off { @@ -125,17 +171,37 @@ static const device_config_t ssi2001_config[] = { // clang-format off }; -const device_t ssi2001_device = -{ - .name = "Innovation SSI-2001", - .internal_name = "ssi2001", - .flags = DEVICE_ISA, - .local = 0, - .init = ssi2001_init, - .close = ssi2001_close, - .reset = NULL, - { .available = NULL }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = ssi2001_config +static const device_config_t entertainer_config[] = { + // clang-format off + { "gameport", "Enable Game port", CONFIG_BINARY, "", 1 }, + { "", "", -1 } +// clang-format off +}; + +const device_t ssi2001_device = { + .name = "Innovation SSI-2001", + .internal_name = "ssi2001", + .flags = DEVICE_ISA, + .local = 0, + .init = ssi2001_init, + .close = ssi2001_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = ssi2001_config +}; + +const device_t entertainer_device = { + .name = "The Entertainer", + .internal_name = "Entertainer", + .flags = DEVICE_ISA, + .local = 1, + .init = entertainer_init, + .close = entertainer_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = entertainer_config }; diff --git a/src/sound/sound.c b/src/sound/sound.c index 254e529ca..d2d3dc313 100644 --- a/src/sound/sound.c +++ b/src/sound/sound.c @@ -132,6 +132,7 @@ static const SOUND_CARD sound_cards[] = { { &sb_vibra16s_device }, { &sb_vibra16xv_device }, { &ssi2001_device }, + { &entertainer_device }, { &pasplus_device }, { &pas16_device }, { &pas16d_device }, @@ -153,6 +154,9 @@ static const SOUND_CARD sound_cards[] = { { &ct5880_device }, { &ad1881_device }, { &cs4297a_device }, +#ifdef USE_LIBSERIALPORT /*The following devices required LIBSERIALPORT*/ + { &opl2board_device }, +#endif { NULL } // clang-format on }; diff --git a/src/sound/ymfm/ymfm.h b/src/sound/ymfm/ymfm.h index c5986d66b..062247a82 100644 --- a/src/sound/ymfm/ymfm.h +++ b/src/sound/ymfm/ymfm.h @@ -42,12 +42,11 @@ #include #include #include +#include #include #include #include -#define SNPRINTF_BUFFER_SIZE_CALC (256 - (end - &buffer[0])) - namespace ymfm { @@ -111,17 +110,6 @@ inline int32_t clamp(int32_t value, int32_t minval, int32_t maxval) } -//------------------------------------------------- -// array_size - return the size of an array -//------------------------------------------------- - -template -constexpr uint32_t array_size(ArrayType (&array)[ArraySize]) -{ - return ArraySize; -} - - //------------------------------------------------- // count_leading_zeros - return the number of // leading zeros in a 32-bit value; CPU-optimized @@ -256,7 +244,8 @@ inline int16_t roundtrip_fp(int32_t value) // apply the shift back and forth to zero out bits that are lost exponent -= 1; - return (value >> exponent) << exponent; + int32_t mask = (1 << exponent) - 1; + return value & ~mask; } @@ -352,7 +341,7 @@ public: { // create file char name[20]; - snprintf(name, sizeof(name), "wavlog-%02d.wav", m_index); + snprintf(&name[0], sizeof(name), "wavlog-%02d.wav", m_index); FILE *out = fopen(name, "wb"); // make the wav file header @@ -485,7 +474,7 @@ public: class ymfm_engine_callbacks { public: - virtual ~ymfm_engine_callbacks() = default; + virtual ~ymfm_engine_callbacks() = default; // timer callback; called by the interface when a timer fires virtual void engine_timer_expired(uint32_t tnum) = 0; @@ -509,6 +498,7 @@ class ymfm_interface public: virtual ~ymfm_interface() = default; + // the following functions must be implemented by any derived classes; the // default implementations are sufficient for some minimal operation, but will // likely need to be overridden to integrate with the outside world; they are diff --git a/src/sound/ymfm/ymfm_fm.h b/src/sound/ymfm/ymfm_fm.h index 81795f8fe..d40409fdd 100644 --- a/src/sound/ymfm/ymfm_fm.h +++ b/src/sound/ymfm/ymfm_fm.h @@ -267,7 +267,7 @@ public: // assign operators void assign(uint32_t index, fm_operator *op) { - assert(index < array_size(m_op)); + assert(index < m_op.size()); m_op[index] = op; if (op != nullptr) op->set_choffs(m_choffs); @@ -330,7 +330,7 @@ private: uint32_t m_choffs; // channel offset in registers int16_t m_feedback[2]; // feedback memory for operator 1 mutable int16_t m_feedback_in; // next input value for op 1 feedback (set in output) - fm_operator *m_op[4]; // up to 4 operators + std::array *, 4> m_op; // up to 4 operators RegisterType &m_regs; // direct reference to registers fm_engine_base &m_owner; // reference to the owning engine }; diff --git a/src/sound/ymfm/ymfm_fm.ipp b/src/sound/ymfm/ymfm_fm.ipp index a3ee8d333..2aa0a216b 100644 --- a/src/sound/ymfm/ymfm_fm.ipp +++ b/src/sound/ymfm/ymfm_fm.ipp @@ -839,12 +839,12 @@ void fm_channel::save_restore(ymfm_saved_state &state) template void fm_channel::keyonoff(uint32_t states, keyon_type type, uint32_t chnum) { - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) if (m_op[opnum] != nullptr) m_op[opnum]->keyonoff(bitfield(states, opnum), type); if (debug::LOG_KEYON_EVENTS && ((debug::GLOBAL_FM_CHANNEL_MASK >> chnum) & 1) != 0) - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) if (m_op[opnum] != nullptr) debug::log_keyon("%c%s\n", bitfield(states, opnum) ? '+' : '-', m_regs.log_keyon(m_choffs, m_op[opnum]->opoffs()).c_str()); } @@ -860,7 +860,7 @@ bool fm_channel::prepare() uint32_t active_mask = 0; // prepare all operators and determine if they are active - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) if (m_op[opnum] != nullptr) if (m_op[opnum]->prepare()) active_mask |= 1 << opnum; @@ -880,7 +880,7 @@ void fm_channel::clock(uint32_t env_counter, int32_t lfo_raw_pm) m_feedback[0] = m_feedback[1]; m_feedback[1] = m_feedback_in; - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) if (m_op[opnum] != nullptr) m_op[opnum]->clock(env_counter, lfo_raw_pm); @@ -888,7 +888,7 @@ void fm_channel::clock(uint32_t env_counter, int32_t lfo_raw_pm) useful temporary code for envelope debugging if (m_choffs == 0x101) { - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) { auto &op = *m_op[((opnum & 1) << 1) | ((opnum >> 1) & 1)]; printf(" %c%03X%c%c ", @@ -1473,14 +1473,11 @@ void fm_engine_base::assign_operators() template void fm_engine_base::update_timer(uint32_t tnum, uint32_t enable, int32_t delta_clocks) { - uint32_t subtract = !!(tnum >> 15); - tnum &= 0x7fff; - // if the timer is live, but not currently enabled, set the timer if (enable && !m_timer_running[tnum]) { // period comes from the registers, and is different for each - uint32_t period = (tnum == 0) ? (1024 - subtract - m_regs.timer_a_value()) : 16 * (256 - subtract - m_regs.timer_b_value()); + uint32_t period = (tnum == 0) ? (1024 - m_regs.timer_a_value()) : 16 * (256 - m_regs.timer_b_value()); // caller can also specify a delta to account for other effects period += delta_clocks; @@ -1507,6 +1504,8 @@ void fm_engine_base::update_timer(uint32_t tnum, uint32_t enable, template void fm_engine_base::engine_timer_expired(uint32_t tnum) { + assert(tnum == 0 || tnum == 1); + // update status if (tnum == 0 && m_regs.enable_timer_a()) set_reset_status(STATUS_TIMERA, 0); @@ -1522,11 +1521,8 @@ void fm_engine_base::engine_timer_expired(uint32_t tnum) m_modified_channels |= 1 << chnum; } - // Make sure the array does not go out of bounds to keep gcc happy - if ((tnum < 2) || (sizeof(m_timer_running) > (2 * sizeof(uint8_t)))) { - // reset - m_timer_running[tnum] = false; - } + // reset + m_timer_running[tnum] = false; update_timer(tnum, 1, 0); } diff --git a/src/sound/ymfm/ymfm_opl.cpp b/src/sound/ymfm/ymfm_opl.cpp index bb91c5dc0..8e8025fd9 100644 --- a/src/sound/ymfm/ymfm_opl.cpp +++ b/src/sound/ymfm/ymfm_opl.cpp @@ -386,9 +386,9 @@ std::string opl_registers_base::log_keyon(uint32_t choffs, uint32_t op uint32_t opnum = (opoffs & 31) - 2 * ((opoffs & 31) / 8) + 18 * bitfield(opoffs, 8); char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%2u.%02u freq=%04X fb=%u alg=%X mul=%X tl=%02X ksr=%u ns=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%2u.%02u freq=%04X fb=%u alg=%X mul=%X tl=%02X ksr=%u ns=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u", chnum, opnum, ch_block_freq(choffs), ch_feedback(choffs), @@ -405,25 +405,25 @@ std::string opl_registers_base::log_keyon(uint32_t choffs, uint32_t op op_eg_sustain(opoffs)); if (OUTPUTS > 1) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " out=%c%c%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, " out=%c%c%c%c", ch_output_0(choffs) ? 'L' : '-', ch_output_1(choffs) ? 'R' : '-', ch_output_2(choffs) ? '0' : '-', ch_output_3(choffs) ? '1' : '-'); if (op_lfo_am_enable(opoffs) != 0) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u", lfo_am_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", lfo_am_depth()); if (op_lfo_pm_enable(opoffs) != 0) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u", lfo_pm_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", lfo_pm_depth()); if (waveform_enable() && op_waveform(opoffs) != 0) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " wf=%u", op_waveform(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs)); if (is_rhythm(choffs)) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " rhy=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " rhy=1"); if (DYNAMIC_OPS) { operator_mapping map; operator_map(map); if (bitfield(map.chan[chnum], 16, 8) != 0xff) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " 4op"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " 4op"); } return buffer; @@ -685,9 +685,9 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) uint32_t opnum = opoffs; char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u freq=%04X inst=%X fb=%u mul=%X", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X inst=%X fb=%u mul=%X", chnum, opnum, ch_block_freq(choffs), ch_instrument(choffs), @@ -695,11 +695,11 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) op_multiple(opoffs)); if (bitfield(opoffs, 0) == 1 || (is_rhythm(choffs) && choffs >= 6)) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " vol=%X", op_volume(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " vol=%X", op_volume(opoffs)); else - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " tl=%02X", ch_total_level(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " tl=%02X", ch_total_level(choffs)); - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " ksr=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u/%u", + end += snprintf(&buffer[end], sizeof(buffer) - end, " ksr=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u/%u", op_ksr(opoffs), op_ksl(opoffs), op_attack_rate(opoffs), @@ -710,13 +710,13 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) ch_sustain(choffs)); if (op_lfo_am_enable(opoffs)) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=1"); if (op_lfo_pm_enable(opoffs)) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=1"); if (op_waveform(opoffs) != 0) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " wf=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=1"); if (is_rhythm(choffs)) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " rhy=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " rhy=1"); return buffer; } diff --git a/src/sound/ymfm/ymfm_opm.cpp b/src/sound/ymfm/ymfm_opm.cpp index c72badb57..03f54fb90 100644 --- a/src/sound/ymfm/ymfm_opm.cpp +++ b/src/sound/ymfm/ymfm_opm.cpp @@ -60,17 +60,17 @@ opm_registers::opm_registers() : { // waveform 0 is a sawtooth uint8_t am = index ^ 0xff; - int8_t pm = int8_t(index); + uint8_t pm = index; m_lfo_waveform[0][index] = am | (pm << 8); // waveform 1 is a square wave am = bitfield(index, 7) ? 0 : 0xff; - pm = int8_t(am ^ 0x80); + pm = am ^ 0x80; m_lfo_waveform[1][index] = am | (pm << 8); // waveform 2 is a triangle wave am = bitfield(index, 7) ? (index << 1) : ((index ^ 0xff) << 1); - pm = int8_t(bitfield(index, 6) ? am : ~am); + pm = bitfield(index, 6) ? am : ~am; m_lfo_waveform[2][index] = am | (pm << 8); // waveform 3 is noise; it is filled in dynamically @@ -330,7 +330,7 @@ uint32_t opm_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opd if (pm_sensitivity < 6) delta += lfo_raw_pm >> (6 - pm_sensitivity); else - delta += lfo_raw_pm << (pm_sensitivity - 5); + delta += uint32_t(lfo_raw_pm) << (pm_sensitivity - 5); } // apply delta and convert to a frequency number @@ -354,9 +354,9 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs) uint32_t opnum = opoffs; char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", chnum, opnum, ch_block_freq(choffs), op_detune2(opoffs), @@ -376,14 +376,14 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs) bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); if (am || pm) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); if (noise_enable() && opoffs == 31) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " noise=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1"); return buffer; } diff --git a/src/sound/ymfm/ymfm_opn.cpp b/src/sound/ymfm/ymfm_opn.cpp index 388162dfe..16ca3416c 100644 --- a/src/sound/ymfm/ymfm_opn.cpp +++ b/src/sound/ymfm/ymfm_opn.cpp @@ -409,9 +409,9 @@ std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opof } char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X", chnum, opnum, block_freq, op_detune(opoffs), @@ -427,21 +427,21 @@ std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opof op_sustain_level(opoffs)); if (OUTPUTS > 1) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " out=%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, " out=%c%c", ch_output_0(choffs) ? 'L' : '-', ch_output_1(choffs) ? 'R' : '-'); if (op_ssg_eg_enable(opoffs)) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " ssg=%X", op_ssg_eg_mode(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " ssg=%X", op_ssg_eg_mode(opoffs)); bool am = (op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); if (am) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u", ch_lfo_am_sens(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", ch_lfo_am_sens(choffs)); bool pm = (ch_lfo_pm_sens(choffs) != 0); if (pm) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u", ch_lfo_pm_sens(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs)); if (am || pm) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo=%02X", lfo_rate()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate()); if (multi_freq() && choffs == 2) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " multi=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " multi=1"); return buffer; } diff --git a/src/sound/ymfm/ymfm_opq.cpp b/src/sound/ymfm/ymfm_opq.cpp index e6f6fa5ea..78ae16164 100644 --- a/src/sound/ymfm/ymfm_opq.cpp +++ b/src/sound/ymfm/ymfm_opq.cpp @@ -339,9 +339,9 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs) uint32_t opnum = opoffs; char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u freq=%04X dt=%+2d fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt=%+2d fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", chnum, opnum, (opoffs & 1) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs), int32_t(op_detune(opoffs)) - 0x20, @@ -360,14 +360,14 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs) bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); if (am) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u", ch_lfo_am_sens(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", ch_lfo_am_sens(choffs)); bool pm = (lfo_enable() && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u", ch_lfo_pm_sens(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs)); if (am || pm) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo=%02X", lfo_rate()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate()); if (ch_reverb(choffs)) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " reverb"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " reverb"); return buffer; } diff --git a/src/sound/ymfm/ymfm_opz.cpp b/src/sound/ymfm/ymfm_opz.cpp index a5ec912aa..1178417bb 100644 --- a/src/sound/ymfm/ymfm_opz.cpp +++ b/src/sound/ymfm/ymfm_opz.cpp @@ -129,17 +129,17 @@ opz_registers::opz_registers() : { // waveform 0 is a sawtooth uint8_t am = index ^ 0xff; - int8_t pm = int8_t(index); + uint8_t pm = index; m_lfo_waveform[0][index] = am | (pm << 8); // waveform 1 is a square wave am = bitfield(index, 7) ? 0 : 0xff; - pm = int8_t(am ^ 0x80); + pm = am ^ 0x80; m_lfo_waveform[1][index] = am | (pm << 8); // waveform 2 is a triangle wave am = bitfield(index, 7) ? (index << 1) : ((index ^ 0xff) << 1); - pm = int8_t(bitfield(index, 6) ? am : ~am); + pm = bitfield(index, 6) ? am : ~am; m_lfo_waveform[2][index] = am | (pm << 8); // waveform 3 is noise; it is filled in dynamically @@ -555,16 +555,16 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs) uint32_t opnum = opoffs; char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u", chnum, opnum); + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u", chnum, opnum); if (op_fix_mode(opoffs)) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(opoffs)); else - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs)); - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", op_detune(opoffs), ch_feedback(choffs), ch_algorithm(choffs), @@ -580,32 +580,32 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs) ch_output_1(choffs) ? 'R' : '-'); if (op_eg_shift(opoffs) != 0) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " egshift=%u", op_eg_shift(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " egshift=%u", op_eg_shift(opoffs)); bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); if (am || pm) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); bool am2 = (lfo2_am_depth() != 0 && ch_lfo2_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am2) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth()); bool pm2 = (lfo2_pm_depth() != 0 && ch_lfo2_pm_sens(choffs) != 0); if (pm2) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth()); if (am2 || pm2) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]); if (op_reverb_rate(opoffs) != 0) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " rev=%u", op_reverb_rate(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " rev=%u", op_reverb_rate(opoffs)); if (op_waveform(opoffs) != 0) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " wf=%u", op_waveform(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs)); if (noise_enable() && opoffs == 31) - end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " noise=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1"); return buffer; } diff --git a/src/unix/dummy_cdrom_ioctl.c b/src/unix/dummy_cdrom_ioctl.c index ed19dfb1f..bf656177b 100644 --- a/src/unix/dummy_cdrom_ioctl.c +++ b/src/unix/dummy_cdrom_ioctl.c @@ -35,7 +35,9 @@ of the audio while audio still plays. With an absolute conversion, the counter is fine. */ #define MSFtoLBA(m, s, f) ((((m * 60) + s) * 75) + f) -static int toc_valid = 0; +typedef struct dummy_cdrom_ioctl_t { + int toc_valid; +} dummy_cdrom_ioctl_t; #ifdef ENABLE_DUMMY_CDROM_IOCTL_LOG int dummy_cdrom_ioctl_do_log = ENABLE_DUMMY_CDROM_IOCTL_LOG; @@ -56,30 +58,41 @@ dummy_cdrom_ioctl_log(const char *fmt, ...) #endif static int -plat_cdrom_open(void) +plat_cdrom_open(void *local) { return 0; } static int -plat_cdrom_load(void) +plat_cdrom_load(void *local) { return 0; } static void -plat_cdrom_read_toc(void) +plat_cdrom_read_toc(void *local) { - if (!toc_valid) - toc_valid = 1; + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + if (!ioctl->toc_valid) + ioctl->toc_valid = 1; +} + +void +plat_cdrom_get_raw_track_info(UNUSED(void *local), int *num, raw_track_info_t *rti) +{ + *num = 1; + memset(rti, 0x00, 11); } int -plat_cdrom_is_track_audio(uint32_t sector) +plat_cdrom_is_track_audio(void *local, uint32_t sector) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; - const int ret = 0; + plat_cdrom_read_toc(ioctl); + + const int ret = 0; dummy_cdrom_ioctl_log("plat_cdrom_is_track_audio(%08X): %i\n", sector, ret); @@ -87,9 +100,11 @@ plat_cdrom_is_track_audio(uint32_t sector) } int -plat_cdrom_is_track_pre(uint32_t sector) +plat_cdrom_is_track_pre(void *local, uint32_t sector) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); const int ret = 0; @@ -99,25 +114,30 @@ plat_cdrom_is_track_pre(uint32_t sector) } uint32_t -plat_cdrom_get_track_start(uint32_t sector, uint8_t *attr, uint8_t *track) +plat_cdrom_get_track_start(void *local, uint32_t sector, uint8_t *attr, uint8_t *track) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); return 0x00000000; } uint32_t -plat_cdrom_get_last_block(void) +plat_cdrom_get_last_block(void *local) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); return 0x00000000; } int -plat_cdrom_ext_medium_changed(void) +plat_cdrom_ext_medium_changed(void *local) { - int ret = 0; + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + int ret = 0; dummy_cdrom_ioctl_log("plat_cdrom_ext_medium_changed(): %i\n", ret); @@ -125,9 +145,11 @@ plat_cdrom_ext_medium_changed(void) } void -plat_cdrom_get_audio_tracks(int *st_track, int *end, TMSF *lead_out) +plat_cdrom_get_audio_tracks(void *local, int *st_track, int *end, TMSF *lead_out) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); *st_track = 1; *end = 1; @@ -141,9 +163,11 @@ plat_cdrom_get_audio_tracks(int *st_track, int *end, TMSF *lead_out) /* This replaces both Info and EndInfo, they are specified by a variable. */ int -plat_cdrom_get_audio_track_info(UNUSED(int end), int track, int *track_num, TMSF *start, uint8_t *attr) +plat_cdrom_get_audio_track_info(void *local, UNUSED(int end), int track, int *track_num, TMSF *start, uint8_t *attr) { - plat_cdrom_read_toc(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_read_toc(ioctl); if ((track < 1) || (track == 0xaa)) { dummy_cdrom_ioctl_log("plat_cdrom_get_audio_track_info(%02i)\n", track); @@ -165,7 +189,8 @@ plat_cdrom_get_audio_track_info(UNUSED(int end), int track, int *track_num, TMSF /* TODO: See if track start is adjusted by 150 or not. */ int -plat_cdrom_get_audio_sub(UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, uint8_t *index, TMSF *rel_pos, TMSF *abs_pos) +plat_cdrom_get_audio_sub(UNUSED(void *local), UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, uint8_t *index, + TMSF *rel_pos, TMSF *abs_pos) { *track = 1; *attr = 0x14; @@ -185,7 +210,7 @@ plat_cdrom_get_audio_sub(UNUSED(uint32_t sector), uint8_t *attr, uint8_t *track, } int -plat_cdrom_get_sector_size(UNUSED(uint32_t sector)) +plat_cdrom_get_sector_size(UNUSED(void *local), UNUSED(uint32_t sector)) { dummy_cdrom_ioctl_log("BytesPerSector=2048\n"); @@ -193,42 +218,56 @@ plat_cdrom_get_sector_size(UNUSED(uint32_t sector)) } int -plat_cdrom_read_sector(uint8_t *buffer, int raw, uint32_t sector) +plat_cdrom_read_sector(void *local, uint8_t *buffer, int raw, uint32_t sector) { - plat_cdrom_open(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; - if (raw) { - dummy_cdrom_ioctl_log("Raw\n"); + plat_cdrom_open(ioctl); + + if (raw) /* Raw */ - } else { - dummy_cdrom_ioctl_log("Cooked\n"); + dummy_cdrom_ioctl_log("Raw\n"); + else /* Cooked */ - } - plat_cdrom_close(); + dummy_cdrom_ioctl_log("Cooked\n"); + + plat_cdrom_close(ioctl); + dummy_cdrom_ioctl_log("ReadSector sector=%d.\n", sector); return 0; } void -plat_cdrom_eject(void) +plat_cdrom_eject(void *local) { - plat_cdrom_open(); - plat_cdrom_close(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; + + plat_cdrom_open(ioctl); + plat_cdrom_close(ioctl); } void -plat_cdrom_close(void) +plat_cdrom_close(UNUSED(void *local)) { } int -plat_cdrom_set_drive(const char *drv) +plat_cdrom_set_drive(void *local, const char *drv) { - plat_cdrom_close(); + dummy_cdrom_ioctl_t *ioctl = (dummy_cdrom_ioctl_t *) local; - toc_valid = 0; + plat_cdrom_close(ioctl); + + ioctl->toc_valid = 0; + + plat_cdrom_load(ioctl); - plat_cdrom_load(); return 1; } + +int +plat_cdrom_get_local_size(void) +{ + return sizeof(dummy_cdrom_ioctl_t); +}