Merge branch 'master' into pc98x1

This commit is contained in:
TC1995
2024-12-13 14:03:24 +01:00
126 changed files with 8491 additions and 2184 deletions

View File

@@ -72,6 +72,7 @@ AppDir:
- libxkbcommon-x11-0 # if QT:BOOL=ON
- qtwayland5 # if QT:BOOL=ON
- zlib1g
- libserialport0
files:
exclude:
- etc

View File

@@ -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)

View File

@@ -16,3 +16,4 @@ ghostscript
libslirp
vde2
libsndfile
libserialport

View File

@@ -14,3 +14,4 @@ qt5-static
qt5-translations
vulkan-headers
libsndfile
libserialport

View File

@@ -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: >-

View File

@@ -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: |

View File

@@ -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: >-

View File

@@ -84,6 +84,8 @@ jobs:
libopenal-dev
libslirp-dev
libfluidsynth-dev
libvdeplug-dev
libserialport-dev
${{ matrix.ui.packages }}
- name: Checkout repository

View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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]);
}
}

View File

@@ -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]);
}

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;

View File

@@ -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"

View File

@@ -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

View File

@@ -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];
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;

View File

@@ -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*/

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 *);

View File

@@ -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
}

View File

@@ -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*/

View File

@@ -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);

View File

@@ -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*/

View File

@@ -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

View File

@@ -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
View 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;
}

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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. */
{

View File

@@ -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"

View File

@@ -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

File diff suppressed because it is too large Load Diff

2134
src/qt/languages/nl-NL.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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)"

View File

@@ -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"

View File

@@ -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 监测器 "

View File

@@ -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 "

View File

@@ -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)" } },

View File

@@ -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;

View File

@@ -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())));
}
}

View File

@@ -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>

View 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);
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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];
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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; }
};

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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 = &params[i];
c = &param;
break;
}
}

View File

@@ -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

View File

@@ -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();
}
};

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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