mirror of
https://github.com/86Box/86Box.git
synced 2026-02-22 09:35:32 -07:00
Merge branch 'master' into pc98x1
This commit is contained in:
@@ -72,6 +72,7 @@ AppDir:
|
||||
- libxkbcommon-x11-0 # if QT:BOOL=ON
|
||||
- qtwayland5 # if QT:BOOL=ON
|
||||
- zlib1g
|
||||
- libserialport0
|
||||
files:
|
||||
exclude:
|
||||
- etc
|
||||
|
||||
11
.ci/build.sh
11
.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)
|
||||
|
||||
@@ -16,3 +16,4 @@ ghostscript
|
||||
libslirp
|
||||
vde2
|
||||
libsndfile
|
||||
libserialport
|
||||
|
||||
@@ -14,3 +14,4 @@ qt5-static
|
||||
qt5-translations
|
||||
vulkan-headers
|
||||
libsndfile
|
||||
libserialport
|
||||
|
||||
4
.github/workflows/cmake_linux.yml
vendored
4
.github/workflows/cmake_linux.yml
vendored
@@ -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: >-
|
||||
|
||||
9
.github/workflows/cmake_macos.yml
vendored
9
.github/workflows/cmake_macos.yml
vendored
@@ -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: |
|
||||
|
||||
9
.github/workflows/cmake_windows_msys2.yml
vendored
9
.github/workflows/cmake_windows_msys2.yml
vendored
@@ -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: >-
|
||||
|
||||
2
.github/workflows/codeql_linux.yml
vendored
2
.github/workflows/codeql_linux.yml
vendored
@@ -84,6 +84,8 @@ jobs:
|
||||
libopenal-dev
|
||||
libslirp-dev
|
||||
libfluidsynth-dev
|
||||
libvdeplug-dev
|
||||
libserialport-dev
|
||||
${{ matrix.ui.packages }}
|
||||
|
||||
- name: Checkout repository
|
||||
|
||||
3
.github/workflows/codeql_macos.yml
vendored
3
.github/workflows/codeql_macos.yml
vendored
@@ -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
|
||||
|
||||
7
.github/workflows/codeql_windows_msys2.yml
vendored
7
.github/workflows/codeql_windows_msys2.yml
vendored
@@ -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
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
3
debian/control
vendored
3
debian/control
vendored
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
src/config.c
24
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]);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
227
src/floppy/fdc.c
227
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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
* Sarah Walker, <https://pcem-emulator.co.uk/>
|
||||
* RichardG, <richardg867@gmail.com>
|
||||
* Jasmine Iwanek, <jriwanek@gmail.com>
|
||||
*
|
||||
* Copyright 2016-2018 Miran Grca.
|
||||
* Copyright 2016-2022 Miran Grca.
|
||||
* Copyright 2008-2018 Sarah Walker.
|
||||
* Copyright 2021 RichardG.
|
||||
* Copyright 2021-2024 Jasmine Iwanek.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
* Sarah Walker, <https://pcem-emulator.co.uk/>
|
||||
* RichardG, <richardg867@gmail.com>
|
||||
* Jasmine Iwanek, <jriwanek@gmail.com>
|
||||
*
|
||||
* 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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
354
src/machine/m_at_grid.c
Normal file
354
src/machine/m_at_grid.c
Normal file
@@ -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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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. */
|
||||
{
|
||||
|
||||
@@ -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 " <file>${qt_translation_file_dest}</file>\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 " <file>${qt_translation_file_dest}</file>\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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
2134
src/qt/languages/86box.pot
Normal file
2134
src/qt/languages/86box.pot
Normal file
File diff suppressed because it is too large
Load Diff
2134
src/qt/languages/nl-NL.po
Normal file
2134
src/qt/languages/nl-NL.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 监测器 "
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -460,6 +460,7 @@ QMap<uint32_t, QPair<QString, QString>> 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)" } },
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<file>86box_it-IT.qm</file>
|
||||
<file>86box_ja-JP.qm</file>
|
||||
<file>86box_ko-KR.qm</file>
|
||||
<file>86box_nl-NL.qm</file>
|
||||
<file>86box_pl-PL.qm</file>
|
||||
<file>86box_pt-BR.qm</file>
|
||||
<file>86box_pt-PT.qm</file>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2016 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004,2010 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<unsigned int>(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;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2013 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2017 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2015 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004,2010 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -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<int>(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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2022 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004,2010 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -23,12 +23,9 @@
|
||||
#ifndef FILTER6581_H
|
||||
#define FILTER6581_H
|
||||
|
||||
#include "siddefs-fp.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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<Integrator6581> const hpIntegrator;
|
||||
Integrator6581 hpIntegrator;
|
||||
|
||||
/// VCR + associated capacitor connected to bandpass output.
|
||||
std::unique_ptr<Integrator6581> 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2019 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004,2010 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2022 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004,2010 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -23,10 +23,6 @@
|
||||
#ifndef FILTER8580_H
|
||||
#define FILTER8580_H
|
||||
|
||||
#include "siddefs-fp.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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<Integrator8580> const hpIntegrator;
|
||||
Integrator8580 hpIntegrator;
|
||||
|
||||
/// VCR + associated capacitor connected to bandpass output.
|
||||
std::unique_ptr<Integrator8580> 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2022 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* 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<Spline::Point> 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2023 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004,2010 Dag Lem
|
||||
*
|
||||
@@ -24,8 +24,10 @@
|
||||
#define FILTERMODELCONFIG_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <cassert>
|
||||
|
||||
#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<double> 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<int>((norm * ((1 << 11) - 1)) * voice_voltage_range); }
|
||||
|
||||
/**
|
||||
* The "zero" output level of the voices.
|
||||
*/
|
||||
int getNormalizedVoiceDC() const { return static_cast<int>(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<unsigned short>(tmp + 0.5);
|
||||
assert(tmp >= 0. && tmp <= 65535.);
|
||||
return static_cast<unsigned short>(tmp + rnd.getNoise());
|
||||
}
|
||||
|
||||
inline unsigned short getNormalizedCurrentFactor(double wl) const
|
||||
@@ -153,11 +276,17 @@ public:
|
||||
return static_cast<unsigned short>(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<unsigned short>(tmp + 0.5);
|
||||
}
|
||||
|
||||
inline int getNormalizedVoice(float value, unsigned int env) const
|
||||
{
|
||||
return static_cast<int>(getNormalizedValue(getVoiceVoltage(value, env)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace reSIDfp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2023 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2010 Dag Lem
|
||||
*
|
||||
@@ -22,28 +22,20 @@
|
||||
|
||||
#include "FilterModelConfig6581.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "Integrator6581.h"
|
||||
#include "OpAmp.h"
|
||||
|
||||
#include "sidcxx11.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <cmath>
|
||||
|
||||
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> FilterModelConfig6581::instance(nullptr);
|
||||
|
||||
std::mutex Instance6581_Lock;
|
||||
|
||||
FilterModelConfig6581* FilterModelConfig6581::getInstance()
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<Spline::Point>(
|
||||
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<Spline::Point>(
|
||||
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<Spline::Point>(
|
||||
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<Spline::Point>(
|
||||
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<Spline::Point>(
|
||||
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<double>(i << 16));
|
||||
assert(tmp > -0.5 && tmp < 65535.5);
|
||||
vcr_nVg[i] = static_cast<unsigned short>(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<unsigned short>(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<Spline::Point>(
|
||||
std::begin(opamp_voltage),
|
||||
std::end(opamp_voltage)),
|
||||
Vddt,
|
||||
vmin,
|
||||
vmax);
|
||||
|
||||
buildSummerTable(opampModel);
|
||||
};
|
||||
|
||||
auto filterMixer = [this]
|
||||
{
|
||||
OpAmp opampModel(
|
||||
std::vector<Spline::Point>(
|
||||
std::begin(opamp_voltage),
|
||||
std::end(opamp_voltage)),
|
||||
Vddt,
|
||||
vmin,
|
||||
vmax);
|
||||
|
||||
buildMixerTable(opampModel, 8.0 / 6.0);
|
||||
};
|
||||
|
||||
auto filterGain = [this]
|
||||
{
|
||||
OpAmp opampModel(
|
||||
std::vector<Spline::Point>(
|
||||
std::begin(opamp_voltage),
|
||||
std::end(opamp_voltage)),
|
||||
Vddt,
|
||||
vmin,
|
||||
vmax);
|
||||
|
||||
buildVolumeTable(opampModel, 12.0);
|
||||
};
|
||||
|
||||
auto filterResonance = [this]
|
||||
{
|
||||
OpAmp opampModel(
|
||||
std::vector<Spline::Point>(
|
||||
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<double>(i << 16));
|
||||
assert(tmp > -0.5 && tmp < 65535.5);
|
||||
vcr_nVg[i] = static_cast<unsigned short>(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<Integrator6581> FilterModelConfig6581::buildIntegrator()
|
||||
{
|
||||
return MAKE_UNIQUE(Integrator6581, this, WL_snake);
|
||||
}
|
||||
|
||||
} // namespace reSIDfp
|
||||
|
||||
@@ -41,17 +41,18 @@ class Integrator6581;
|
||||
*/
|
||||
class FilterModelConfig6581 final : public FilterModelConfig
|
||||
{
|
||||
private:
|
||||
static const unsigned int DAC_BITS = 11;
|
||||
|
||||
private:
|
||||
static std::unique_ptr<FilterModelConfig6581> instance;
|
||||
// This allows access to the private constructor
|
||||
#ifdef HAVE_CXX11
|
||||
friend std::unique_ptr<FilterModelConfig6581>::deleter_type;
|
||||
#else
|
||||
friend class std::auto_ptr<FilterModelConfig6581>;
|
||||
#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<Integrator6581> 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<unsigned short>(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; }
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#include "Integrator8580.h"
|
||||
#include "OpAmp.h"
|
||||
|
||||
#include "sidcxx11.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
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> FilterModelConfig8580::instance(nullptr);
|
||||
|
||||
std::mutex Instance8580_Lock;
|
||||
|
||||
FilterModelConfig8580* FilterModelConfig8580::getInstance()
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<Spline::Point>(
|
||||
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<Spline::Point>(
|
||||
std::begin(opamp_voltage),
|
||||
std::end(opamp_voltage)),
|
||||
Vddt,
|
||||
vmin,
|
||||
vmax);
|
||||
OpAmp opampModel(
|
||||
std::vector<Spline::Point>(
|
||||
std::begin(opamp_voltage),
|
||||
std::end(opamp_voltage)),
|
||||
Vddt,
|
||||
vmin,
|
||||
vmax);
|
||||
|
||||
buildSummerTable(opampModel);
|
||||
};
|
||||
|
||||
auto filterMixer = [this]
|
||||
{
|
||||
OpAmp opampModel(
|
||||
std::vector<Spline::Point>(
|
||||
std::begin(opamp_voltage),
|
||||
std::end(opamp_voltage)),
|
||||
Vddt,
|
||||
vmin,
|
||||
vmax);
|
||||
|
||||
buildMixerTable(opampModel, 8.0 / 5.0);
|
||||
};
|
||||
|
||||
auto filterGain = [this]
|
||||
{
|
||||
OpAmp opampModel(
|
||||
std::vector<Spline::Point>(
|
||||
std::begin(opamp_voltage),
|
||||
std::end(opamp_voltage)),
|
||||
Vddt,
|
||||
vmin,
|
||||
vmax);
|
||||
|
||||
buildVolumeTable(opampModel, 16.0);
|
||||
};
|
||||
|
||||
auto filterResonance = [this]
|
||||
{
|
||||
OpAmp opampModel(
|
||||
std::vector<Spline::Point>(
|
||||
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<Spline::Point>(
|
||||
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<Spline::Point>(
|
||||
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<Spline::Point>(
|
||||
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<Integrator8580> FilterModelConfig8580::buildIntegrator()
|
||||
{
|
||||
return MAKE_UNIQUE(Integrator8580, this);
|
||||
}
|
||||
|
||||
} // namespace reSIDfp
|
||||
|
||||
@@ -42,25 +42,30 @@ class FilterModelConfig8580 final : public FilterModelConfig
|
||||
private:
|
||||
static std::unique_ptr<FilterModelConfig8580> instance;
|
||||
// This allows access to the private constructor
|
||||
#ifdef HAVE_CXX11
|
||||
friend std::unique_ptr<FilterModelConfig8580>::deleter_type;
|
||||
#else
|
||||
friend class std::auto_ptr<FilterModelConfig8580>;
|
||||
#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<Integrator8580> buildIntegrator();
|
||||
inline constexpr double getVref() const { return Vref * VOLTAGE_SKEW; }
|
||||
};
|
||||
|
||||
} // namespace reSIDfp
|
||||
|
||||
47
src/sound/resid-fp/Integrator.h
Normal file
47
src/sound/resid-fp/Integrator.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004, 2010 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
* 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
|
||||
@@ -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 <cmath>
|
||||
# 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<int>(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<int>(fmc.getVcr_nVg((nVddt_Vw_2 + (Vgdt_2 >> 1)) >> 16));
|
||||
#ifdef SLOPE_FACTOR
|
||||
const double nVp = static_cast<double>(nVg - nVt) / n; // Pinch-off voltage
|
||||
const int kVgt = static_cast<int>(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<unsigned int>(fmc.getVcr_n_Ids_term(kVgt_Vs)) << 15;
|
||||
const unsigned int Ir = static_cast<unsigned int>(fmc.getVcr_n_Ids_term(kVgt_Vd)) << 15;
|
||||
#ifdef SLOPE_FACTOR
|
||||
const double iVcr = static_cast<double>(If - Ir);
|
||||
const int n_I_vcr = static_cast<int>(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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2022 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2023 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004, 2010 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -23,6 +23,7 @@
|
||||
#ifndef INTEGRATOR6581_H
|
||||
#define INTEGRATOR6581_H
|
||||
|
||||
#include "Integrator.h"
|
||||
#include "FilterModelConfig6581.h"
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -33,10 +34,6 @@
|
||||
// actually produces worse results, needs investigation
|
||||
//#define SLOPE_FACTOR
|
||||
|
||||
#ifdef SLOPE_FACTOR
|
||||
# include <cmath>
|
||||
#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<int>(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<int>(fmc->getVcr_nVg((nVddt_Vw_2 + (Vgdt_2 >> 1)) >> 16));
|
||||
#ifdef SLOPE_FACTOR
|
||||
const double nVp = static_cast<double>(nVg - nVt) / n; // Pinch-off voltage
|
||||
const int kVgt = static_cast<int>(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<unsigned int>(fmc->getVcr_n_Ids_term(kVgt_Vs)) << 15;
|
||||
const unsigned int Ir = static_cast<unsigned int>(fmc->getVcr_n_Ids_term(kVgt_Vd)) << 15;
|
||||
#ifdef SLOPE_FACTOR
|
||||
const double iVcr = static_cast<double>(If - Ir);
|
||||
const int n_I_vcr = static_cast<int>(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
|
||||
|
||||
@@ -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<int>(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
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#ifndef INTEGRATOR8580_H
|
||||
#define INTEGRATOR8580_H
|
||||
|
||||
#include "Integrator.h"
|
||||
#include "FilterModelConfig8580.h"
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -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<v<2
|
||||
// Ua = Ue * v = 4.75v 1<v<2
|
||||
assert(v > 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<int>(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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2015 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2023 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004,2010 Dag Lem
|
||||
*
|
||||
@@ -23,7 +23,6 @@
|
||||
#ifndef OPAMP_H
|
||||
#define OPAMP_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#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<Spline> const opamp;
|
||||
Spline opamp;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -89,14 +88,13 @@ public:
|
||||
* @param vmin
|
||||
* @param vmax
|
||||
*/
|
||||
OpAmp(const std::vector<Spline::Point> &opamp, double Vddt,
|
||||
OpAmp(const std::vector<Spline::Point> &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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of libsidplayfp, a SID player engine.
|
||||
*
|
||||
* Copyright 2011-2016 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
|
||||
* Copyright 2007-2010 Antti Lankila
|
||||
* Copyright 2004 Dag Lem <resid@nimrod.no>
|
||||
*
|
||||
@@ -26,11 +26,12 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Param> ParamVector;
|
||||
using ParamVector = std::vector<Param>;
|
||||
|
||||
private:
|
||||
/// Interpolation parameters
|
||||
|
||||
@@ -23,14 +23,10 @@
|
||||
#ifndef VOICE_H
|
||||
#define VOICE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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<WaveformGenerator> const waveformGenerator;
|
||||
WaveformGenerator waveformGenerator;
|
||||
|
||||
std::unique_ptr<EnvelopeGenerator> 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<int>(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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,66 +21,154 @@
|
||||
|
||||
#include "WaveformCalculator.h"
|
||||
|
||||
#include "sidcxx11.h"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <cmath>
|
||||
|
||||
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<const CombinedWaveformConfig*, matrix_t>;
|
||||
|
||||
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<float>(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<std::mutex> 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
|
||||
|
||||
@@ -22,46 +22,33 @@
|
||||
#ifndef WAVEFORMCALCULATOR_h
|
||||
#define WAVEFORMCALCULATOR_h
|
||||
|
||||
#include <map>
|
||||
|
||||
#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<const CombinedWaveformConfig*, matrix_t> 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -26,9 +26,7 @@
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CXX11
|
||||
# include <atomic>
|
||||
#endif
|
||||
#include <atomic>
|
||||
|
||||
/**
|
||||
* Counter.
|
||||
@@ -36,11 +34,7 @@
|
||||
class counter
|
||||
{
|
||||
private:
|
||||
#ifndef HAVE_CXX11
|
||||
volatile unsigned int c;
|
||||
#else
|
||||
std::atomic<unsigned int> c;
|
||||
#endif
|
||||
|
||||
public:
|
||||
counter() : c(1) {}
|
||||
@@ -81,6 +75,6 @@ public:
|
||||
T const* operator[](unsigned int a) const { return &data[a * y]; }
|
||||
};
|
||||
|
||||
typedef matrix<short> matrix_t;
|
||||
using matrix_t = matrix<short>;
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user