mirror of
https://github.com/86Box/86Box.git
synced 2026-02-23 09:58:19 -07:00
Merge branch '86Box:master' into master
This commit is contained in:
@@ -72,6 +72,7 @@ AppDir:
|
||||
- libxkbcommon-x11-0 # if QT:BOOL=ON
|
||||
- qtwayland5 # if QT:BOOL=ON
|
||||
- zlib1g
|
||||
- libserialport0
|
||||
files:
|
||||
exclude:
|
||||
- etc
|
||||
|
||||
@@ -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
|
||||
@@ -622,7 +625,7 @@ else
|
||||
# ...and the ones we do want listed. Non-dev packages fill missing spots on the list.
|
||||
libpkgs=""
|
||||
longest_libpkg=0
|
||||
for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libxkbcommon-x11-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev libvdeplug-dev libfluidsynth-dev libsndfile1-dev
|
||||
for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libxkbcommon-x11-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev libvdeplug-dev libfluidsynth-dev libsndfile1-dev libserialport-dev
|
||||
do
|
||||
libpkgs="$libpkgs $pkg:$arch_deb"
|
||||
length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c)
|
||||
|
||||
@@ -16,3 +16,4 @@ ghostscript
|
||||
libslirp
|
||||
vde2
|
||||
libsndfile
|
||||
libserialport
|
||||
|
||||
@@ -14,3 +14,4 @@ qt5-static
|
||||
qt5-translations
|
||||
vulkan-headers
|
||||
libsndfile
|
||||
libserialport
|
||||
|
||||
1
.github/workflows/cmake_linux.yml
vendored
1
.github/workflows/cmake_linux.yml
vendored
@@ -82,6 +82,7 @@ jobs:
|
||||
libslirp-dev
|
||||
libfluidsynth-dev
|
||||
libvdeplug-dev
|
||||
libserialport-dev
|
||||
${{ matrix.ui.packages }}
|
||||
|
||||
- name: Checkout repository
|
||||
|
||||
4
.github/workflows/cmake_macos.yml
vendored
4
.github/workflows/cmake_macos.yml
vendored
@@ -82,6 +82,7 @@ jobs:
|
||||
fluidsynth
|
||||
libslirp
|
||||
vde
|
||||
libserialport
|
||||
${{ matrix.ui.packages }}
|
||||
|
||||
- name: Checkout repository
|
||||
@@ -102,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: |
|
||||
@@ -181,6 +183,7 @@ jobs:
|
||||
openal-soft
|
||||
fluidsynth
|
||||
libslirp
|
||||
libserialport
|
||||
${{ matrix.ui.packages }}
|
||||
|
||||
- name: Checkout repository
|
||||
@@ -201,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: |
|
||||
|
||||
1
.github/workflows/cmake_windows_msys2.yml
vendored
1
.github/workflows/cmake_windows_msys2.yml
vendored
@@ -97,6 +97,7 @@ jobs:
|
||||
rtmidi:p
|
||||
libslirp:p
|
||||
fluidsynth:p
|
||||
libserialport:p
|
||||
${{ matrix.ui.packages }}
|
||||
|
||||
- name: Checkout repository
|
||||
|
||||
1
.github/workflows/codeql_linux.yml
vendored
1
.github/workflows/codeql_linux.yml
vendored
@@ -85,6 +85,7 @@ jobs:
|
||||
libslirp-dev
|
||||
libfluidsynth-dev
|
||||
libvdeplug-dev
|
||||
libserialport-dev
|
||||
${{ matrix.ui.packages }}
|
||||
|
||||
- name: Checkout repository
|
||||
|
||||
2
.github/workflows/codeql_macos.yml
vendored
2
.github/workflows/codeql_macos.yml
vendored
@@ -76,6 +76,7 @@ jobs:
|
||||
fluidsynth
|
||||
libslirp
|
||||
vde
|
||||
libserialport
|
||||
${{ matrix.ui.packages }}
|
||||
|
||||
- name: Checkout repository
|
||||
@@ -97,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
|
||||
|
||||
1
.github/workflows/codeql_windows_msys2.yml
vendored
1
.github/workflows/codeql_windows_msys2.yml
vendored
@@ -102,6 +102,7 @@ jobs:
|
||||
rtmidi:p
|
||||
libslirp:p
|
||||
fluidsynth:p
|
||||
libserialport:p
|
||||
${{ matrix.ui.packages }}
|
||||
|
||||
- name: Checkout repository
|
||||
|
||||
@@ -69,7 +69,8 @@
|
||||
"Qt5_DIR": "/opt/homebrew/opt/qt@5/lSib/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"
|
||||
},
|
||||
@@ -87,6 +88,7 @@
|
||||
"MOLTENVK_DIR": "/opt/homebrew/opt/molten-vk",
|
||||
"Qt5LinguistTools_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5LinguistTools",
|
||||
"OpenAL_ROOT": "/opt/homebrew/opt/openal-soft",
|
||||
"LIBSERIALPORT_ROOT": "/opt/homebrew/opt/libserialport",
|
||||
"CMAKE_CXX_FLAGS_DEBUG": "-g -O0 -DENABLE_VDE_LOG",
|
||||
"CMAKE_C_FLAGS_DEBUG": "-g -O0 -DENABLE_VDE_LOG"
|
||||
},
|
||||
|
||||
3
debian/control
vendored
3
debian/control
vendored
@@ -16,7 +16,8 @@ Build-Depends: cmake (>= 3.21),
|
||||
libsndfile-dev,
|
||||
ninja-build,
|
||||
qttools5-dev,
|
||||
qtbase5-private-dev
|
||||
qtbase5-private-dev,
|
||||
libserialport-dev
|
||||
Standards-Version: 4.6.0
|
||||
Homepage: https://86box.net/
|
||||
#Vcs-Browser: https://salsa.debian.org/debian/86box
|
||||
|
||||
@@ -652,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);
|
||||
@@ -682,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;
|
||||
@@ -699,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);
|
||||
@@ -735,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;
|
||||
|
||||
@@ -2911,6 +2911,7 @@ d86f_decompose_encoded_buffer(int drive, int side)
|
||||
uint16_t temp2;
|
||||
uint32_t len;
|
||||
const uint16_t *dst = dev->track_encoded_data[side];
|
||||
const uint16_t *dst_s = dev->track_surface_data[side];
|
||||
uint16_t *src1 = dev->thin_track_encoded_data[0][side];
|
||||
uint16_t *src1_s = dev->thin_track_surface_data[0][side];
|
||||
uint16_t *src2 = dev->thin_track_encoded_data[1][side];
|
||||
@@ -2922,12 +2923,13 @@ d86f_decompose_encoded_buffer(int drive, int side)
|
||||
if (d86f_has_surface_desc(drive)) {
|
||||
/* Source image has surface description data, so we have some more handling to do.
|
||||
We need hole masks for both buffers. Holes have data bit clear and surface bit set. */
|
||||
temp = ~src1[i] & src1_s[i];
|
||||
temp2 = ~src2[i] & src2_s[i];
|
||||
src1[i] = dst[i] & ~temp;
|
||||
src1_s[i] = temp;
|
||||
src2[i] = dst[i] & ~temp2;
|
||||
src2_s[i] = temp2;
|
||||
src1_s[i] = src2_s[i] = dst_s[i]; /* Write the new holes and weak bits. */
|
||||
temp = ~src1[i] & src1_s[i]; /* Bits that are clear in data and set in surface are holes. */
|
||||
temp2 = ~src2[i] & src2_s[i]; /* Bits that are clear in data and set in surface are holes. */
|
||||
src1[i] = dst[i] & ~temp; /* Make sure the holes' bits are cleared in the decomposed buffer. */
|
||||
src1_s[i] |= temp; /* Make sure the holes' bits are set in the decomposed surface. */
|
||||
src2[i] = dst[i] & ~temp2; /* Make sure the holes' bits are cleared in the decomposed buffer. */
|
||||
src2_s[i] |= temp2; /* Make sure the holes' bits are set in the decomposed surface. */
|
||||
} else
|
||||
src1[i] = src2[i] = dst[i];
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
* Sarah Walker, <https://pcem-emulator.co.uk/>
|
||||
* RichardG, <richardg867@gmail.com>
|
||||
* Jasmine Iwanek, <jriwanek@gmail.com>
|
||||
*
|
||||
* Copyright 2016-2018 Miran Grca.
|
||||
* Copyright 2016-2022 Miran Grca.
|
||||
* Copyright 2008-2018 Sarah Walker.
|
||||
* Copyright 2021 RichardG.
|
||||
* Copyright 2021-2024 Jasmine Iwanek.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
@@ -468,7 +470,7 @@ const device_t gameport_device = {
|
||||
.name = "Game port",
|
||||
.internal_name = "gameport",
|
||||
.flags = 0,
|
||||
.local = 0x080200,
|
||||
.local = GAMEPORT_8ADDR | 0x0200,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -482,7 +484,7 @@ const device_t gameport_201_device = {
|
||||
.name = "Game port (Port 201h only)",
|
||||
.internal_name = "gameport_201",
|
||||
.flags = 0,
|
||||
.local = 0x010201,
|
||||
.local = GAMEPORT_1ADDR | 0x0201,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -496,7 +498,7 @@ const device_t gameport_203_device = {
|
||||
.name = "Game port (Port 203h only)",
|
||||
.internal_name = "gameport_203",
|
||||
.flags = 0,
|
||||
.local = 0x010203,
|
||||
.local = GAMEPORT_1ADDR | 0x0203,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -510,7 +512,7 @@ const device_t gameport_205_device = {
|
||||
.name = "Game port (Port 205h only)",
|
||||
.internal_name = "gameport_205",
|
||||
.flags = 0,
|
||||
.local = 0x010205,
|
||||
.local = GAMEPORT_1ADDR | 0x0205,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -524,7 +526,7 @@ const device_t gameport_207_device = {
|
||||
.name = "Game port (Port 207h only)",
|
||||
.internal_name = "gameport_207",
|
||||
.flags = 0,
|
||||
.local = 0x010207,
|
||||
.local = GAMEPORT_1ADDR | 0x0207,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -538,7 +540,7 @@ const device_t gameport_208_device = {
|
||||
.name = "Game port (Port 208h-20fh)",
|
||||
.internal_name = "gameport_208",
|
||||
.flags = 0,
|
||||
.local = 0x080208,
|
||||
.local = GAMEPORT_8ADDR | 0x0208,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -552,7 +554,7 @@ const device_t gameport_209_device = {
|
||||
.name = "Game port (Port 209h only)",
|
||||
.internal_name = "gameport_209",
|
||||
.flags = 0,
|
||||
.local = 0x010209,
|
||||
.local = GAMEPORT_1ADDR | 0x0209,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -566,7 +568,7 @@ const device_t gameport_20b_device = {
|
||||
.name = "Game port (Port 20Bh only)",
|
||||
.internal_name = "gameport_20b",
|
||||
.flags = 0,
|
||||
.local = 0x01020B,
|
||||
.local = GAMEPORT_1ADDR | 0x020B,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -580,7 +582,7 @@ const device_t gameport_20d_device = {
|
||||
.name = "Game port (Port 20Dh only)",
|
||||
.internal_name = "gameport_20d",
|
||||
.flags = 0,
|
||||
.local = 0x01020D,
|
||||
.local = GAMEPORT_1ADDR | 0x020D,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -594,7 +596,7 @@ const device_t gameport_20f_device = {
|
||||
.name = "Game port (Port 20Fh only)",
|
||||
.internal_name = "gameport_20f",
|
||||
.flags = 0,
|
||||
.local = 0x01020F,
|
||||
.local = GAMEPORT_1ADDR | 0x020F,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -662,7 +664,7 @@ const device_t gameport_pnp_device = {
|
||||
.name = "Game port (Plug and Play only)",
|
||||
.internal_name = "gameport_pnp",
|
||||
.flags = 0,
|
||||
.local = 0x080000,
|
||||
.local = GAMEPORT_8ADDR,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -676,7 +678,7 @@ const device_t gameport_pnp_6io_device = {
|
||||
.name = "Game port (Plug and Play only, 6 I/O ports)",
|
||||
.internal_name = "gameport_pnp_6io",
|
||||
.flags = 0,
|
||||
.local = 0x060000,
|
||||
.local = GAMEPORT_6ADDR,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -690,7 +692,7 @@ const device_t gameport_sio_device = {
|
||||
.name = "Game port (Super I/O)",
|
||||
.internal_name = "gameport_sio",
|
||||
.flags = 0,
|
||||
.local = 0x1080000,
|
||||
.local = GAMEPORT_SIO | GAMEPORT_8ADDR,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
@@ -704,7 +706,7 @@ const device_t gameport_sio_1io_device = {
|
||||
.name = "Game port (Super I/O, 1 I/O port)",
|
||||
.internal_name = "gameport_sio",
|
||||
.flags = 0,
|
||||
.local = 0x1010000,
|
||||
.local = GAMEPORT_SIO | GAMEPORT_1ADDR,
|
||||
.init = gameport_init,
|
||||
.close = gameport_close,
|
||||
.reset = NULL,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -23,7 +23,8 @@ enum fm_type {
|
||||
FM_YMF289B = 2, /* OPL3-L */
|
||||
FM_YMF278B = 3, /* OPL 4 */
|
||||
FM_ESFM = 4, /* ESFM */
|
||||
FM_MAX = 5
|
||||
FM_MAX = 5,
|
||||
FM_OPL2BOARD = 6 /* OPL2BOARD (External Device)*/
|
||||
};
|
||||
|
||||
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*/
|
||||
|
||||
@@ -209,6 +209,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*/
|
||||
|
||||
@@ -170,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)
|
||||
|
||||
@@ -58,7 +58,12 @@ fm_driver_get(int chip_id, fm_drv_t *drv)
|
||||
drv->priv = device_add_inst(&ymf262_ymfm_device, fm_dev_inst[fm_driver][chip_id]++);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef USE_LIBSERIALPORT
|
||||
case FM_OPL2BOARD:
|
||||
*drv = ymfm_opl2board_drv;
|
||||
drv->priv = device_add_inst(&ym_opl2board_device, fm_dev_inst[fm_driver][chip_id]++);
|
||||
break;
|
||||
#endif
|
||||
case FM_YMF289B:
|
||||
*drv = ymfm_drv;
|
||||
drv->priv = device_add_inst(&ymf289b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++);
|
||||
|
||||
203
src/sound/snd_opl2board.c
Normal file
203
src/sound/snd_opl2board.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* Interface to the OPL2Board External audio device (USB)
|
||||
*
|
||||
*
|
||||
* Authors: Jose Phillips <jose@hddlive.net>
|
||||
* Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
*
|
||||
* Copyright 2024 Jose Phillips.
|
||||
* Copyright 2017-2020 Fred N. van Kempen.
|
||||
* Copyright 2016-2020 Miran Grca.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/mca.h>
|
||||
#include <86box/sound.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/snd_opl.h>
|
||||
#include <86box/plat_unused.h>
|
||||
|
||||
|
||||
#ifdef ENABLE_OPL2DEVICE_LOG
|
||||
int opl2board_device_do_log = ENABLE_OPL2DEVICE_LOG;
|
||||
|
||||
static void
|
||||
opl2board_device_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (opl2board_device_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define opl2board_device_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
typedef struct opl2board_device_t {
|
||||
fm_drv_t opl;
|
||||
|
||||
uint8_t pos_regs[8];
|
||||
|
||||
|
||||
} opl2board_device_t;
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
opl2board_device_get_buffer(int32_t *buffer, int len, void *priv)
|
||||
{
|
||||
opl2board_device_t *serial = (opl2board_device_t *) priv;
|
||||
|
||||
const int32_t *opl_buf = serial->opl.update(serial->opl.priv);
|
||||
|
||||
for (int c = 0; c < len * 2; c++)
|
||||
buffer[c] += opl_buf[c];
|
||||
|
||||
serial->opl.reset_buffer(serial->opl.priv);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
opl2board_device_mca_read(int port, void *priv)
|
||||
{
|
||||
const opl2board_device_t *serial = (opl2board_device_t *) priv;
|
||||
|
||||
opl2board_device_log("opl2board_device_mca_read: port=%04x\n", port);
|
||||
|
||||
return serial->pos_regs[port & 7];
|
||||
}
|
||||
|
||||
void
|
||||
opl2board_device_mca_write(int port, uint8_t val, void *priv)
|
||||
{
|
||||
opl2board_device_t *serial = (opl2board_device_t *) priv;
|
||||
|
||||
if (port < 0x102)
|
||||
return;
|
||||
|
||||
opl2board_device_log("opl2board_device_mca_write: port=%04x val=%02x\n", port, val);
|
||||
|
||||
switch (port) {
|
||||
case 0x102:
|
||||
if ((serial->pos_regs[2] & 1) && !(val & 1))
|
||||
io_removehandler(0x0388, 0x0002,
|
||||
serial->opl.read, NULL, NULL,
|
||||
serial->opl.write, NULL, NULL,
|
||||
serial->opl.priv);
|
||||
if (!(serial->pos_regs[2] & 1) && (val & 1))
|
||||
io_sethandler(0x0388, 0x0002,
|
||||
serial->opl.read, NULL, NULL,
|
||||
serial->opl.write, NULL, NULL,
|
||||
serial->opl.priv);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
serial->pos_regs[port & 7] = val;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
opl2board_device_mca_feedb(void *priv)
|
||||
{
|
||||
const opl2board_device_t *serial = (opl2board_device_t *) priv;
|
||||
|
||||
return (serial->pos_regs[2] & 1);
|
||||
}
|
||||
|
||||
void *
|
||||
opl2board_device_init(UNUSED(const device_t *info))
|
||||
{
|
||||
opl2board_device_t *serial = malloc(sizeof(opl2board_device_t));
|
||||
memset(serial, 0, sizeof(opl2board_device_t));
|
||||
|
||||
|
||||
opl2board_device_log("opl2board_device_init\n");
|
||||
fm_driver_get(FM_OPL2BOARD, &serial->opl);
|
||||
io_sethandler(0x0388, 0x0002,
|
||||
serial->opl.read, NULL, NULL,
|
||||
serial->opl.write, NULL, NULL,
|
||||
serial->opl.priv);
|
||||
music_add_handler(opl2board_device_get_buffer, serial);
|
||||
|
||||
|
||||
return serial;
|
||||
|
||||
}
|
||||
|
||||
void *
|
||||
opl2board_device_mca_init(const device_t *info)
|
||||
{
|
||||
opl2board_device_t *serial = opl2board_device_init(info);
|
||||
|
||||
io_removehandler(0x0388, 0x0002,
|
||||
serial->opl.read, NULL, NULL,
|
||||
serial->opl.write, NULL, NULL,
|
||||
serial->opl.priv);
|
||||
mca_add(opl2board_device_mca_read,
|
||||
opl2board_device_mca_write,
|
||||
opl2board_device_mca_feedb,
|
||||
NULL,
|
||||
serial);
|
||||
serial->pos_regs[0] = 0xd7;
|
||||
serial->pos_regs[1] = 0x70;
|
||||
|
||||
return serial;
|
||||
}
|
||||
|
||||
void
|
||||
opl2board_device_close(void *priv)
|
||||
{
|
||||
opl2board_device_t *serial = (opl2board_device_t *) priv;
|
||||
free(serial);
|
||||
}
|
||||
|
||||
|
||||
static const device_config_t opl2board_config[] = {
|
||||
|
||||
{
|
||||
.name = "host_serial_path",
|
||||
.description = "Host Serial Device",
|
||||
.type = CONFIG_SERPORT,
|
||||
.default_string = "",
|
||||
.file_filter = NULL,
|
||||
.spinner = {},
|
||||
.selection = {}
|
||||
},
|
||||
{ .name = "", .description = "", .type = CONFIG_END }
|
||||
|
||||
};
|
||||
const device_t opl2board_device = {
|
||||
.name = "OPL2Board (External Device)",
|
||||
.internal_name = "opl2board_device",
|
||||
.flags = DEVICE_ISA,
|
||||
.local = 0,
|
||||
.init = opl2board_device_init,
|
||||
.close = opl2board_device_close,
|
||||
.reset = NULL,
|
||||
{ .available = NULL },
|
||||
.speed_changed = NULL,
|
||||
.force_redraw = NULL,
|
||||
.config = opl2board_config
|
||||
};
|
||||
550
src/sound/snd_opl_opl2board.cpp
Normal file
550
src/sound/snd_opl_opl2board.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* Interface to the YMFM External audio device (USB)
|
||||
* For OPL2Board arduino based.
|
||||
*
|
||||
* Authors: Jose Phillips, <jose@hddlive.net>
|
||||
* Adrien Moulin, <adrien@elyosh.org>
|
||||
*
|
||||
* Copyright 2024 Jose Phillips.
|
||||
* Copyright 2022 Adrien Moulin.
|
||||
*/
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include "ymfm/ymfm_opl.h"
|
||||
#include <libserialport.h>
|
||||
|
||||
|
||||
extern "C" {
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/sound.h>
|
||||
#include <86box/snd_opl.h>
|
||||
#include <86box/mem.h>
|
||||
#include <86box/rom.h>
|
||||
#include <86box/plat_unused.h>
|
||||
#include <86box/config.h>
|
||||
#include <86box/ini.h>
|
||||
#include <86box/device.h>
|
||||
|
||||
|
||||
// Disable c99-designator to avoid the warnings in *_ymfm_device
|
||||
#ifdef __clang__
|
||||
# if __has_warning("-Wc99-designator")
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wc99-designator"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
#define RSM_FRAC 10
|
||||
|
||||
#define OPL_FREQ FREQ_48000
|
||||
|
||||
enum {
|
||||
FLAG_CYCLES = (1 << 0)
|
||||
};
|
||||
|
||||
uint8_t lastval = 0x00;
|
||||
|
||||
|
||||
class OPLBOARDChipBase {
|
||||
public:
|
||||
OPLBOARDChipBase(UNUSED(uint32_t clock), fm_type type, uint32_t samplerate)
|
||||
: m_buf_pos(0)
|
||||
, m_flags(0)
|
||||
, m_type(type)
|
||||
, m_samplerate(samplerate)
|
||||
{
|
||||
memset(m_buffer, 0, sizeof(m_buffer));
|
||||
}
|
||||
|
||||
virtual ~OPLBOARDChipBase()
|
||||
{
|
||||
}
|
||||
|
||||
fm_type type() const { return m_type; }
|
||||
int8_t flags() const { return m_flags; }
|
||||
void set_do_cycles(int8_t do_cycles) { do_cycles ? m_flags |= FLAG_CYCLES : m_flags &= ~FLAG_CYCLES; }
|
||||
int32_t *buffer() const { return (int32_t *) m_buffer; }
|
||||
void reset_buffer() { m_buf_pos = 0; }
|
||||
|
||||
virtual uint32_t sample_rate() const = 0;
|
||||
|
||||
virtual void write(uint16_t addr, uint8_t data) = 0;
|
||||
virtual void generate(int32_t *data, uint32_t num_samples) = 0;
|
||||
virtual int32_t *update() = 0;
|
||||
virtual uint8_t read(uint16_t addr) = 0;
|
||||
virtual void set_clock(uint32_t clock) = 0;
|
||||
|
||||
|
||||
protected:
|
||||
int32_t m_buffer[MUSICBUFLEN * 2];
|
||||
int m_buf_pos;
|
||||
int *m_buf_pos_global;
|
||||
int8_t m_flags;
|
||||
fm_type m_type;
|
||||
uint32_t m_samplerate;
|
||||
};
|
||||
|
||||
template <typename ChipType>
|
||||
class OPLBOARDChip : public OPLBOARDChipBase, public ymfm::ymfm_interface {
|
||||
public:
|
||||
OPLBOARDChip(uint32_t clock, fm_type type, uint32_t samplerate)
|
||||
: OPLBOARDChipBase(clock, type, samplerate)
|
||||
, m_chip(*this)
|
||||
, m_clock(clock)
|
||||
, m_samplerate(samplerate)
|
||||
, m_samplecnt(0)
|
||||
{
|
||||
memset(m_samples, 0, sizeof(m_samples));
|
||||
memset(m_oldsamples, 0, sizeof(m_oldsamples));
|
||||
m_rateratio = (samplerate << RSM_FRAC) / m_chip.sample_rate(m_clock);
|
||||
m_clock_us = 1000000.0 / (double) m_clock;
|
||||
m_subtract[0] = 80.0;
|
||||
m_subtract[1] = 320.0;
|
||||
m_type = type;
|
||||
m_buf_pos_global = (samplerate == FREQ_49716) ? &music_pos_global : &wavetable_pos_global;
|
||||
|
||||
if (m_type == FM_YMF278B) {
|
||||
if (rom_load_linear("roms/sound/yamaha/yrw801.rom", 0, 0x200000, 0, m_yrw801) == 0) {
|
||||
fatal("YRW801 ROM image \"roms/sound/yamaha/yrw801.rom\" not found\n");
|
||||
}
|
||||
}
|
||||
|
||||
timer_add(&m_timers[0], OPLBOARDChip::timer1, this, 0);
|
||||
timer_add(&m_timers[1], OPLBOARDChip::timer2, this, 0);
|
||||
}
|
||||
|
||||
virtual uint32_t sample_rate() const override
|
||||
{
|
||||
return m_chip.sample_rate(m_clock);
|
||||
}
|
||||
|
||||
virtual void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) override
|
||||
{
|
||||
if (tnum > 1)
|
||||
return;
|
||||
|
||||
m_duration_in_clocks[tnum] = duration_in_clocks;
|
||||
pc_timer_t *timer = &m_timers[tnum];
|
||||
if (duration_in_clocks < 0)
|
||||
timer_stop(timer);
|
||||
else {
|
||||
double period = m_clock_us * duration_in_clocks;
|
||||
if (period < m_subtract[tnum])
|
||||
m_engine->engine_timer_expired(tnum);
|
||||
else
|
||||
timer_on_auto(timer, period);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set_clock(uint32_t clock) override
|
||||
{
|
||||
m_clock = clock;
|
||||
m_clock_us = 1000000.0 / (double) m_clock;
|
||||
m_rateratio = (m_samplerate << RSM_FRAC) / m_chip.sample_rate(m_clock);
|
||||
|
||||
ymfm_set_timer(0, m_duration_in_clocks[0]);
|
||||
ymfm_set_timer(1, m_duration_in_clocks[1]);
|
||||
}
|
||||
|
||||
virtual void generate(int32_t *data, uint32_t num_samples) override
|
||||
{
|
||||
for (uint32_t i = 0; i < num_samples; i++) {
|
||||
m_chip.generate(&m_output);
|
||||
if ((m_type == FM_YMF278B) && (sizeof(m_output.data) > (4 * sizeof(int32_t)))) {
|
||||
if (ChipType::OUTPUTS == 1) {
|
||||
*data++ = m_output.data[4];
|
||||
*data++ = m_output.data[4];
|
||||
} else {
|
||||
*data++ = m_output.data[4];
|
||||
*data++ = m_output.data[5];
|
||||
}
|
||||
} else if (ChipType::OUTPUTS == 1) {
|
||||
*data++ = m_output.data[0];
|
||||
*data++ = m_output.data[0];
|
||||
} else {
|
||||
*data++ = m_output.data[0];
|
||||
*data++ = m_output.data[1 % ChipType::OUTPUTS];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
virtual void generate_resampled(int32_t *data, uint32_t num_samples) override
|
||||
{
|
||||
if ((m_samplerate == FREQ_49716) || (m_samplerate == FREQ_44100)) {
|
||||
generate(data, num_samples);
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_samples; i++) {
|
||||
while (m_samplecnt >= m_rateratio) {
|
||||
m_oldsamples[0] = m_samples[0];
|
||||
m_oldsamples[1] = m_samples[1];
|
||||
m_chip.generate(&m_output);
|
||||
if ((m_type == FM_YMF278B) && (sizeof(m_output.data) > (4 * sizeof(int32_t)))) {
|
||||
if (ChipType::OUTPUTS == 1) {
|
||||
m_samples[0] = m_output.data[4];
|
||||
m_samples[1] = m_output.data[4];
|
||||
} else {
|
||||
m_samples[0] = m_output.data[4];
|
||||
m_samples[1] = m_output.data[5];
|
||||
}
|
||||
} else if (ChipType::OUTPUTS == 1) {
|
||||
m_samples[0] = m_output.data[0];
|
||||
m_samples[1] = m_output.data[0];
|
||||
} else {
|
||||
m_samples[0] = m_output.data[0];
|
||||
m_samples[1] = m_output.data[1 % ChipType::OUTPUTS];
|
||||
}
|
||||
m_samplecnt -= m_rateratio;
|
||||
}
|
||||
|
||||
*data++ = ((int32_t) ((m_oldsamples[0] * (m_rateratio - m_samplecnt)
|
||||
+ m_samples[0] * m_samplecnt)
|
||||
/ m_rateratio));
|
||||
*data++ = ((int32_t) ((m_oldsamples[1] * (m_rateratio - m_samplecnt)
|
||||
+ m_samples[1] * m_samplecnt)
|
||||
/ m_rateratio));
|
||||
|
||||
m_samplecnt += 1 << RSM_FRAC;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual int32_t *update() override
|
||||
{
|
||||
if (m_buf_pos >= *m_buf_pos_global)
|
||||
return m_buffer;
|
||||
|
||||
generate(&m_buffer[m_buf_pos * 2], *m_buf_pos_global - m_buf_pos);
|
||||
|
||||
for (; m_buf_pos < *m_buf_pos_global; m_buf_pos++) {
|
||||
m_buffer[m_buf_pos * 2] /= 2;
|
||||
m_buffer[(m_buf_pos * 2) + 1] /= 2;
|
||||
}
|
||||
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
virtual void write(uint16_t addr, uint8_t data) override
|
||||
{
|
||||
|
||||
m_chip.write(addr, data);
|
||||
}
|
||||
|
||||
virtual uint8_t read(uint16_t addr) override
|
||||
{
|
||||
return m_chip.read(addr);
|
||||
}
|
||||
|
||||
virtual uint32_t get_special_flags(void) override
|
||||
{
|
||||
return ((m_type == FM_YMF262) || (m_type == FM_YMF289B) || (m_type == FM_YMF278B)) ? 0x8000 : 0x0000;
|
||||
}
|
||||
|
||||
static void timer1(void *priv)
|
||||
{
|
||||
OPLBOARDChip<ChipType> *drv = (OPLBOARDChip<ChipType> *) priv;
|
||||
drv->m_engine->engine_timer_expired(0);
|
||||
}
|
||||
|
||||
static void timer2(void *priv)
|
||||
{
|
||||
OPLBOARDChip<ChipType> *drv = (OPLBOARDChip<ChipType> *) priv;
|
||||
drv->m_engine->engine_timer_expired(1);
|
||||
}
|
||||
|
||||
virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address) override
|
||||
{
|
||||
if (type == ymfm::access_class::ACCESS_PCM && address < 0x200000) {
|
||||
return m_yrw801[address];
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
private:
|
||||
ChipType m_chip;
|
||||
uint32_t m_clock;
|
||||
double m_clock_us;
|
||||
double m_subtract[2];
|
||||
typename ChipType::output_data m_output;
|
||||
pc_timer_t m_timers[2];
|
||||
int32_t m_duration_in_clocks[2]; // Needed for clock switches.
|
||||
uint32_t m_samplerate;
|
||||
|
||||
// YRW801-M wavetable ROM.
|
||||
uint8_t m_yrw801[0x200000];
|
||||
|
||||
// Resampling
|
||||
int32_t m_rateratio;
|
||||
int32_t m_samplecnt;
|
||||
int32_t m_oldsamples[2];
|
||||
int32_t m_samples[2];
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
|
||||
#include "cpu.h"
|
||||
#include <86box/86box.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/snd_opl.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/config.h>
|
||||
#include <86box/ini.h>
|
||||
|
||||
|
||||
#ifdef ENABLE_OPL_LOG
|
||||
int ymfm_do_log = ENABLE_OPL_LOG;
|
||||
|
||||
static void
|
||||
ymfm_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (ymfm_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define ymfm_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
struct sp_port *port;
|
||||
|
||||
|
||||
|
||||
void opl2board_init() {
|
||||
|
||||
device_add(&opl2board_device);
|
||||
const char* port_name = device_get_config_string("host_serial_path");
|
||||
device_context_restore();
|
||||
|
||||
enum sp_return result;
|
||||
|
||||
result = sp_get_port_by_name(port_name, &port);
|
||||
if (result != SP_OK) {
|
||||
ymfm_log("Error: Cannot find port %s\n", port_name);
|
||||
return;
|
||||
}
|
||||
|
||||
result = sp_open(port, SP_MODE_READ_WRITE);
|
||||
if (result != SP_OK) {
|
||||
ymfm_log ("Error: Cannot open port %s\n", port_name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set port configuration this values are hardcoded.
|
||||
sp_set_baudrate(port, 115200);
|
||||
sp_set_bits(port, 8);
|
||||
sp_set_parity(port, SP_PARITY_NONE);
|
||||
sp_set_stopbits(port, 1);
|
||||
sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE);
|
||||
|
||||
|
||||
ymfm_log("OPL2Board Serial port %s initialized at 115200 baud.\n", port_name);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void opl2board_write(uint8_t data) {
|
||||
if (port == NULL) {
|
||||
ymfm_log(stderr, "Error: OPL2Board Port not initialized.\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
enum sp_return result = sp_blocking_write(port, &data, sizeof(data), 1000);
|
||||
if (result < 0) {
|
||||
ymfm_log(stderr, "Error: Failed to write to OPL2Board port.\n");
|
||||
} else {
|
||||
ymfm_log("OPL2Board: data sent: %02X\n", data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void opl2board_reset() {
|
||||
|
||||
// Reset all voices to 0
|
||||
ymfm_log("Performing OPL2Board reset\n");
|
||||
for (uint8_t i = 0x00; i < 0xFF; i++) {
|
||||
if (i >= 0x40 && i <= 0x55) {
|
||||
opl2board_write(i);
|
||||
opl2board_write(0x3F);
|
||||
} else {
|
||||
opl2board_write (i);
|
||||
opl2board_write(0x00);
|
||||
} }
|
||||
}
|
||||
|
||||
void opl2board_close() {
|
||||
|
||||
if (port != NULL) {
|
||||
opl2board_reset();
|
||||
sp_close(port);
|
||||
sp_free_port(port);
|
||||
port = NULL;
|
||||
ymfm_log("OPL2Board port closed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ymfm_opl2board_drv_init(const device_t *info)
|
||||
{
|
||||
OPLBOARDChipBase *fm;
|
||||
|
||||
switch (info->local) {
|
||||
default:
|
||||
case FM_OPL2BOARD:
|
||||
fm = (OPLBOARDChipBase *) new OPLBOARDChip<ymfm::ym3812>(3579545, FM_OPL2BOARD, FREQ_49716);
|
||||
break;
|
||||
}
|
||||
fm->set_do_cycles(1);
|
||||
|
||||
return fm;
|
||||
}
|
||||
|
||||
static void
|
||||
ymfm_opl2board_drv_close(void *priv)
|
||||
{
|
||||
OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv;
|
||||
opl2board_close();
|
||||
if (drv != NULL)
|
||||
delete drv;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
ymfm_opl2board_drv_read(uint16_t port, void *priv)
|
||||
{
|
||||
OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv;
|
||||
|
||||
if ((port == 0x380) || (port == 0x381))
|
||||
port |= 4;
|
||||
|
||||
/* Point to register read port. */
|
||||
if (drv->flags() & FLAG_CYCLES)
|
||||
cycles -= ((int) (isa_timing * 8));
|
||||
|
||||
uint8_t ret = drv->read(port);
|
||||
drv->update();
|
||||
|
||||
ymfm_log("YMFM read port %04x, status = %02x\n", port, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
ymfm_opl2board_drv_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
|
||||
OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv;
|
||||
|
||||
ymfm_log("YMFM write port %04x value = %02x\n", port, val);
|
||||
if ((port == 0x380) || (port == 0x381))
|
||||
port |= 4;
|
||||
// Allow initialization of adlib
|
||||
if ((val == 0x04 || val == 0x02) || (lastval == 0x04 || lastval == 0x02)) {
|
||||
drv->write(port, val);
|
||||
}
|
||||
lastval = val;
|
||||
opl2board_write(val);
|
||||
drv->update();
|
||||
}
|
||||
|
||||
|
||||
static int32_t *
|
||||
ymfm_opl2board_drv_update(void *priv)
|
||||
{
|
||||
if (port == NULL) {
|
||||
opl2board_init();
|
||||
opl2board_reset();
|
||||
}
|
||||
OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv;
|
||||
|
||||
return drv->update();
|
||||
}
|
||||
|
||||
static void
|
||||
ymfm_opl2board_drv_reset_buffer(void *priv)
|
||||
{
|
||||
OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv;
|
||||
|
||||
drv->reset_buffer();
|
||||
}
|
||||
|
||||
static void
|
||||
ymfm_opl2board_drv_set_do_cycles(void *priv, int8_t do_cycles)
|
||||
{
|
||||
OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv;
|
||||
drv->set_do_cycles(do_cycles);
|
||||
}
|
||||
|
||||
static void
|
||||
ymfm_opl2board_drv_generate(void *priv, int32_t *data, uint32_t num_samples)
|
||||
{
|
||||
|
||||
OPLBOARDChipBase *drv = (OPLBOARDChipBase *) priv;
|
||||
// drv->generate_resampled(data, num_samples);
|
||||
drv->generate(data, num_samples);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const device_t ym_opl2board_device = {
|
||||
.name = "YMOPL2Board (External Device)",
|
||||
.internal_name = "ym_opl2board_device",
|
||||
.flags = 0,
|
||||
.local = FM_OPL2BOARD,
|
||||
.init = ymfm_opl2board_drv_init,
|
||||
.close = ymfm_opl2board_drv_close,
|
||||
.reset = NULL,
|
||||
{ .available = NULL },
|
||||
.speed_changed = NULL,
|
||||
.force_redraw = NULL,
|
||||
.config = NULL
|
||||
};
|
||||
|
||||
const fm_drv_t ymfm_opl2board_drv {
|
||||
&ymfm_opl2board_drv_read,
|
||||
&ymfm_opl2board_drv_write,
|
||||
&ymfm_opl2board_drv_update,
|
||||
&ymfm_opl2board_drv_reset_buffer,
|
||||
&ymfm_opl2board_drv_set_do_cycles,
|
||||
NULL,
|
||||
ymfm_opl2board_drv_generate
|
||||
|
||||
};
|
||||
|
||||
#ifdef __clang__
|
||||
# if __has_warning("-Wc99-designator")
|
||||
# pragma clang diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1452,9 +1452,6 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
if (!(addr & 1)) {
|
||||
mixer->index = val;
|
||||
mixer->regs[0x01] = val;
|
||||
if (val == 0x40) {
|
||||
mixer->ess_id_str_pos = 0;
|
||||
}
|
||||
} else {
|
||||
if (mixer->index == 0) {
|
||||
/* Reset */
|
||||
@@ -1473,6 +1470,8 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
mixer->regs[0x3c] = 0x05;
|
||||
mixer->regs[0x3e] = 0x00;
|
||||
|
||||
mixer->regs[0x64] = 0x08;
|
||||
|
||||
sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2);
|
||||
} else {
|
||||
mixer->regs[mixer->index] = val;
|
||||
@@ -1525,6 +1524,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
break;
|
||||
|
||||
case 0x1C:
|
||||
mixer->regs[mixer->index] = val & 0x2f;
|
||||
if ((mixer->regs[0x1C] & 0x07) == 0x07) {
|
||||
mixer->input_selector = INPUT_MIXER_L | INPUT_MIXER_R;
|
||||
} else if ((mixer->regs[0x1C] & 0x07) == 0x06) {
|
||||
@@ -1556,12 +1556,12 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
break;
|
||||
|
||||
case 0x64:
|
||||
mixer->regs[mixer->index] = (mixer->regs[mixer->index] & 0xf7) | 0x20;
|
||||
// mixer->regs[mixer->index] &= ~0x8;
|
||||
if (ess->dsp.sb_subtype > SB_SUBTYPE_ESS_ES1688)
|
||||
mixer->regs[mixer->index] = (mixer->regs[mixer->index] & 0xf7) | 0x20;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
{
|
||||
if (ess->dsp.sb_subtype >= SB_SUBTYPE_ESS_ES1688) {
|
||||
uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30);
|
||||
sb_log("mpu401_base_addr = %04X\n", mpu401_base_addr);
|
||||
|
||||
@@ -1572,7 +1572,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
|
||||
gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200);
|
||||
|
||||
if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) {
|
||||
if (ess->dsp.sb_subtype > SB_SUBTYPE_ESS_ES1688) {
|
||||
/* Not on ES1688. */
|
||||
io_removehandler(0x0388, 0x0004,
|
||||
ess->opl.read, NULL, NULL,
|
||||
@@ -1585,6 +1585,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
ess->opl.priv);
|
||||
}
|
||||
}
|
||||
|
||||
if (ess->mpu != NULL) switch ((mixer->regs[0x40] >> 5) & 0x7) {
|
||||
default:
|
||||
break;
|
||||
@@ -1627,11 +1628,12 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
ess_fm_midi_read, NULL, NULL,
|
||||
ess_fm_midi_write, NULL, NULL,
|
||||
ess);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
|
||||
sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n",
|
||||
mixer->index, mixer->regs[mixer->index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1667,6 +1669,7 @@ ess_mixer_read(uint16_t addr, void *priv)
|
||||
case 0x0c:
|
||||
case 0x0e:
|
||||
case 0x14:
|
||||
case 0x1a:
|
||||
case 0x02:
|
||||
case 0x06:
|
||||
case 0x30:
|
||||
@@ -1685,20 +1688,48 @@ ess_mixer_read(uint16_t addr, void *priv)
|
||||
ret = mixer->regs[mixer->index] | 0x11;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688)
|
||||
ret = mixer->regs[mixer->index];
|
||||
else
|
||||
ret = 0x0a;
|
||||
/* Bit 1 always set, bits 7-6 always clear on both the real ES688 and ES1688. */
|
||||
case 0x1c:
|
||||
ret = mixer->regs[mixer->index] | 0x10;
|
||||
break;
|
||||
|
||||
case 0x48:
|
||||
ret = mixer->regs[mixer->index];
|
||||
break;
|
||||
/*
|
||||
Real ES688: Always 0x00;
|
||||
Real ES1688: Bit 2 always clear.
|
||||
*/
|
||||
case 0x40:
|
||||
if (ess->dsp.sb_subtype > SB_SUBTYPE_ESS_ES1688)
|
||||
ret = mixer->regs[mixer->index];
|
||||
else if (ess->dsp.sb_subtype >= SB_SUBTYPE_ESS_ES1688)
|
||||
ret = mixer->regs[mixer->index] & 0xfb;
|
||||
else
|
||||
ret = 0x00;
|
||||
break;
|
||||
|
||||
/* Return 0x00 so it has bit 3 clear, so NT 5.x drivers don't misdetect it as ES1788. */
|
||||
/*
|
||||
Real ES688: Always 0x00;
|
||||
Real ES1688: All bits writable.
|
||||
*/
|
||||
case 0x48:
|
||||
if (ess->dsp.sb_subtype >= SB_SUBTYPE_ESS_ES1688)
|
||||
ret = mixer->regs[mixer->index];
|
||||
else
|
||||
ret = 0x00;
|
||||
break;
|
||||
|
||||
/*
|
||||
Return 0x00 so it has bit 3 clear, so NT 5.x drivers don't misdetect it as ES1788.
|
||||
Bit 3 set and writable: ESSCFG detects the card as ES1788 if register 70h is read-only,
|
||||
otherwise, as ES1887.
|
||||
Bit 3 set and read-only: ESSCFG detects the card as ES1788 if register 70h is read-only,
|
||||
otherwise, as ES1888.
|
||||
Real ES688 and ES1688: Always 0x00.
|
||||
*/
|
||||
case 0x64:
|
||||
ret = (mixer->regs[mixer->index] & 0xf7) | 0x20;
|
||||
if (ess->dsp.sb_subtype > SB_SUBTYPE_ESS_ES1688)
|
||||
ret = (mixer->regs[mixer->index] & 0xf7) | 0x20;
|
||||
else
|
||||
ret = 0x00;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1706,7 +1737,7 @@ ess_mixer_read(uint16_t addr, void *priv)
|
||||
break;
|
||||
}
|
||||
|
||||
sb_log("[%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, addr, ret);
|
||||
sb_log("[%04X:%08X] [R] %04X = %02X (%02X)\n", CS, cpu_state.pc, addr, ret, mixer->index);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -2268,9 +2299,6 @@ ess_x688_pnp_config_changed(UNUSED(const uint8_t ld), isapnp_device_config_t *co
|
||||
ess_base_write, NULL, NULL,
|
||||
ess);
|
||||
|
||||
ess->mixer_ess.ess_id_str[2] = 0x00;
|
||||
ess->mixer_ess.ess_id_str[3] = 0x00;
|
||||
|
||||
addr = ess->opl_pnp_addr;
|
||||
if (addr) {
|
||||
ess->opl_pnp_addr = 0;
|
||||
@@ -2337,9 +2365,6 @@ ess_x688_pnp_config_changed(UNUSED(const uint8_t ld), isapnp_device_config_t *co
|
||||
ess_base_read, NULL, NULL,
|
||||
ess_base_write, NULL, NULL,
|
||||
ess);
|
||||
|
||||
ess->mixer_ess.ess_id_str[2] = (addr >> 8) & 0xff;
|
||||
ess->mixer_ess.ess_id_str[3] = addr & 0xff;
|
||||
}
|
||||
|
||||
addr = config->io[1].base;
|
||||
@@ -3748,11 +3773,6 @@ ess_x688_init(UNUSED(const device_t *info))
|
||||
midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp);
|
||||
|
||||
if (info->local) {
|
||||
ess->mixer_ess.ess_id_str[0] = 0x16;
|
||||
ess->mixer_ess.ess_id_str[1] = 0x88;
|
||||
ess->mixer_ess.ess_id_str[2] = (addr >> 8) & 0xff;
|
||||
ess->mixer_ess.ess_id_str[3] = addr & 0xff;
|
||||
|
||||
ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t));
|
||||
/* NOTE: The MPU is initialized disabled and with no IRQ assigned.
|
||||
* It will be later initialized by the guest OS's drivers. */
|
||||
@@ -3818,12 +3838,6 @@ ess_x688_pnp_init(UNUSED(const device_t *info))
|
||||
if (device_get_config_int("receive_input"))
|
||||
midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp);
|
||||
|
||||
/* Not on ES688. */
|
||||
ess->mixer_ess.ess_id_str[0] = 0x16;
|
||||
ess->mixer_ess.ess_id_str[1] = 0x88;
|
||||
ess->mixer_ess.ess_id_str[2] = 0x00;
|
||||
ess->mixer_ess.ess_id_str[3] = 0x00;
|
||||
|
||||
ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t));
|
||||
/* NOTE: The MPU is initialized disabled and with no IRQ assigned.
|
||||
* It will be later initialized by the guest OS's drivers. */
|
||||
|
||||
@@ -1687,6 +1687,10 @@ sb_exec_command(sb_dsp_t *dsp)
|
||||
break;
|
||||
case 0xE1: /* Get DSP version */
|
||||
if (IS_ESS(dsp)) {
|
||||
/*
|
||||
0x03 0x01 (Sound Blaster Pro compatibility) confirmed by both the
|
||||
ES1888 datasheet and the probing of the real ES688 and ES1688 cards.
|
||||
*/
|
||||
sb_add_data(dsp, 0x3);
|
||||
sb_add_data(dsp, 0x1);
|
||||
break;
|
||||
@@ -1722,9 +1726,12 @@ sb_exec_command(sb_dsp_t *dsp)
|
||||
while (sb16_copyright[c])
|
||||
sb_add_data(dsp, sb16_copyright[c++]);
|
||||
sb_add_data(dsp, 0);
|
||||
} else if (IS_ESS(dsp)) {
|
||||
sb_add_data(dsp, 0);
|
||||
}
|
||||
} /* else if (IS_ESS(dsp))
|
||||
sb_add_data(dsp, 0); */
|
||||
/*
|
||||
TODO: What ESS card returns 0x00 here? Probing of the real ES688 and ES1688 cards
|
||||
revealed that they in fact return nothing on this command.
|
||||
*/
|
||||
break;
|
||||
case 0xE4: /* Write test register */
|
||||
dsp->sb_test = dsp->sb_data[0];
|
||||
@@ -1736,12 +1743,26 @@ sb_exec_command(sb_dsp_t *dsp)
|
||||
break;
|
||||
case SB_SUBTYPE_ESS_ES688:
|
||||
sb_add_data(dsp, 0x68);
|
||||
sb_add_data(dsp, 0x80 | 0x04);
|
||||
/*
|
||||
80h: ESSCFG fails to detect the AudioDrive;
|
||||
81h-83h: ES??88, Windows 3.1 driver expects MPU-401 and gives a legacy mixer error;
|
||||
84h: ES688, Windows 3.1 driver expects MPU-401, returned by DOSBox-X;
|
||||
85h-87h: ES688, Windows 3.1 driver does not expect MPU-401:
|
||||
85h: Returned by MSDOS622's real ESS688,
|
||||
86h: Returned by Dizzy's real ES688.
|
||||
We return 86h if MPU is absent, 84h otherwise, who knows what the actual
|
||||
PnP ES688 returns here.
|
||||
*/
|
||||
sb_add_data(dsp, 0x80 | ((dsp->mpu != NULL) ? 0x04 : 0x06));
|
||||
break;
|
||||
case SB_SUBTYPE_ESS_ES1688:
|
||||
// Determined via Windows driver debugging.
|
||||
sb_add_data(dsp, 0x68);
|
||||
sb_add_data(dsp, 0x80 | 0x09);
|
||||
/*
|
||||
89h: ES1688, returned by DOSBox-X, determined via Windows driver
|
||||
debugging;
|
||||
8Bh: ES1688, returned by both MSDOS622's and Dizzy's real ES1688's.
|
||||
*/
|
||||
sb_add_data(dsp, 0x80 | 0x0b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,9 @@ static const SOUND_CARD sound_cards[] = {
|
||||
{ &ct5880_device },
|
||||
{ &ad1881_device },
|
||||
{ &cs4297a_device },
|
||||
#ifdef USE_LIBSERIALPORT /*The following devices required LIBSERIALPORT*/
|
||||
{ &opl2board_device },
|
||||
#endif
|
||||
{ NULL }
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
@@ -42,12 +42,11 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define SNPRINTF_BUFFER_SIZE_CALC (256 - (end - &buffer[0]))
|
||||
|
||||
namespace ymfm
|
||||
{
|
||||
|
||||
@@ -111,17 +110,6 @@ inline int32_t clamp(int32_t value, int32_t minval, int32_t maxval)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// array_size - return the size of an array
|
||||
//-------------------------------------------------
|
||||
|
||||
template<typename ArrayType, int ArraySize>
|
||||
constexpr uint32_t array_size(ArrayType (&array)[ArraySize])
|
||||
{
|
||||
return ArraySize;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// count_leading_zeros - return the number of
|
||||
// leading zeros in a 32-bit value; CPU-optimized
|
||||
@@ -256,7 +244,8 @@ inline int16_t roundtrip_fp(int32_t value)
|
||||
|
||||
// apply the shift back and forth to zero out bits that are lost
|
||||
exponent -= 1;
|
||||
return (value >> exponent) << exponent;
|
||||
int32_t mask = (1 << exponent) - 1;
|
||||
return value & ~mask;
|
||||
}
|
||||
|
||||
|
||||
@@ -352,7 +341,7 @@ public:
|
||||
{
|
||||
// create file
|
||||
char name[20];
|
||||
snprintf(name, sizeof(name), "wavlog-%02d.wav", m_index);
|
||||
snprintf(&name[0], sizeof(name), "wavlog-%02d.wav", m_index);
|
||||
FILE *out = fopen(name, "wb");
|
||||
|
||||
// make the wav file header
|
||||
@@ -485,7 +474,7 @@ public:
|
||||
class ymfm_engine_callbacks
|
||||
{
|
||||
public:
|
||||
virtual ~ymfm_engine_callbacks() = default;
|
||||
virtual ~ymfm_engine_callbacks() = default;
|
||||
|
||||
// timer callback; called by the interface when a timer fires
|
||||
virtual void engine_timer_expired(uint32_t tnum) = 0;
|
||||
@@ -509,6 +498,7 @@ class ymfm_interface
|
||||
|
||||
public:
|
||||
virtual ~ymfm_interface() = default;
|
||||
|
||||
// the following functions must be implemented by any derived classes; the
|
||||
// default implementations are sufficient for some minimal operation, but will
|
||||
// likely need to be overridden to integrate with the outside world; they are
|
||||
|
||||
@@ -267,7 +267,7 @@ public:
|
||||
// assign operators
|
||||
void assign(uint32_t index, fm_operator<RegisterType> *op)
|
||||
{
|
||||
assert(index < array_size(m_op));
|
||||
assert(index < m_op.size());
|
||||
m_op[index] = op;
|
||||
if (op != nullptr)
|
||||
op->set_choffs(m_choffs);
|
||||
@@ -330,7 +330,7 @@ private:
|
||||
uint32_t m_choffs; // channel offset in registers
|
||||
int16_t m_feedback[2]; // feedback memory for operator 1
|
||||
mutable int16_t m_feedback_in; // next input value for op 1 feedback (set in output)
|
||||
fm_operator<RegisterType> *m_op[4]; // up to 4 operators
|
||||
std::array<fm_operator<RegisterType> *, 4> m_op; // up to 4 operators
|
||||
RegisterType &m_regs; // direct reference to registers
|
||||
fm_engine_base<RegisterType> &m_owner; // reference to the owning engine
|
||||
};
|
||||
|
||||
@@ -839,12 +839,12 @@ void fm_channel<RegisterType>::save_restore(ymfm_saved_state &state)
|
||||
template<class RegisterType>
|
||||
void fm_channel<RegisterType>::keyonoff(uint32_t states, keyon_type type, uint32_t chnum)
|
||||
{
|
||||
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||||
for (uint32_t opnum = 0; opnum < m_op.size(); opnum++)
|
||||
if (m_op[opnum] != nullptr)
|
||||
m_op[opnum]->keyonoff(bitfield(states, opnum), type);
|
||||
|
||||
if (debug::LOG_KEYON_EVENTS && ((debug::GLOBAL_FM_CHANNEL_MASK >> chnum) & 1) != 0)
|
||||
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||||
for (uint32_t opnum = 0; opnum < m_op.size(); opnum++)
|
||||
if (m_op[opnum] != nullptr)
|
||||
debug::log_keyon("%c%s\n", bitfield(states, opnum) ? '+' : '-', m_regs.log_keyon(m_choffs, m_op[opnum]->opoffs()).c_str());
|
||||
}
|
||||
@@ -860,7 +860,7 @@ bool fm_channel<RegisterType>::prepare()
|
||||
uint32_t active_mask = 0;
|
||||
|
||||
// prepare all operators and determine if they are active
|
||||
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||||
for (uint32_t opnum = 0; opnum < m_op.size(); opnum++)
|
||||
if (m_op[opnum] != nullptr)
|
||||
if (m_op[opnum]->prepare())
|
||||
active_mask |= 1 << opnum;
|
||||
@@ -880,7 +880,7 @@ void fm_channel<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
|
||||
m_feedback[0] = m_feedback[1];
|
||||
m_feedback[1] = m_feedback_in;
|
||||
|
||||
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||||
for (uint32_t opnum = 0; opnum < m_op.size(); opnum++)
|
||||
if (m_op[opnum] != nullptr)
|
||||
m_op[opnum]->clock(env_counter, lfo_raw_pm);
|
||||
|
||||
@@ -888,7 +888,7 @@ void fm_channel<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
|
||||
useful temporary code for envelope debugging
|
||||
if (m_choffs == 0x101)
|
||||
{
|
||||
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||||
for (uint32_t opnum = 0; opnum < m_op.size(); opnum++)
|
||||
{
|
||||
auto &op = *m_op[((opnum & 1) << 1) | ((opnum >> 1) & 1)];
|
||||
printf(" %c%03X%c%c ",
|
||||
@@ -1473,14 +1473,11 @@ void fm_engine_base<RegisterType>::assign_operators()
|
||||
template<class RegisterType>
|
||||
void fm_engine_base<RegisterType>::update_timer(uint32_t tnum, uint32_t enable, int32_t delta_clocks)
|
||||
{
|
||||
uint32_t subtract = !!(tnum >> 15);
|
||||
tnum &= 0x7fff;
|
||||
|
||||
// if the timer is live, but not currently enabled, set the timer
|
||||
if (enable && !m_timer_running[tnum])
|
||||
{
|
||||
// period comes from the registers, and is different for each
|
||||
uint32_t period = (tnum == 0) ? (1024 - subtract - m_regs.timer_a_value()) : 16 * (256 - subtract - m_regs.timer_b_value());
|
||||
uint32_t period = (tnum == 0) ? (1024 - m_regs.timer_a_value()) : 16 * (256 - m_regs.timer_b_value());
|
||||
|
||||
// caller can also specify a delta to account for other effects
|
||||
period += delta_clocks;
|
||||
@@ -1507,6 +1504,8 @@ void fm_engine_base<RegisterType>::update_timer(uint32_t tnum, uint32_t enable,
|
||||
template<class RegisterType>
|
||||
void fm_engine_base<RegisterType>::engine_timer_expired(uint32_t tnum)
|
||||
{
|
||||
assert(tnum == 0 || tnum == 1);
|
||||
|
||||
// update status
|
||||
if (tnum == 0 && m_regs.enable_timer_a())
|
||||
set_reset_status(STATUS_TIMERA, 0);
|
||||
@@ -1522,11 +1521,8 @@ void fm_engine_base<RegisterType>::engine_timer_expired(uint32_t tnum)
|
||||
m_modified_channels |= 1 << chnum;
|
||||
}
|
||||
|
||||
// Make sure the array does not go out of bounds to keep gcc happy
|
||||
if ((tnum < 2) || (sizeof(m_timer_running) > (2 * sizeof(uint8_t)))) {
|
||||
// reset
|
||||
m_timer_running[tnum] = false;
|
||||
}
|
||||
// reset
|
||||
m_timer_running[tnum] = false;
|
||||
update_timer(tnum, 1, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -386,9 +386,9 @@ std::string opl_registers_base<Revision>::log_keyon(uint32_t choffs, uint32_t op
|
||||
uint32_t opnum = (opoffs & 31) - 2 * ((opoffs & 31) / 8) + 18 * bitfield(opoffs, 8);
|
||||
|
||||
char buffer[256];
|
||||
char *end = &buffer[0];
|
||||
int end = 0;
|
||||
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%2u.%02u freq=%04X fb=%u alg=%X mul=%X tl=%02X ksr=%u ns=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, "%2u.%02u freq=%04X fb=%u alg=%X mul=%X tl=%02X ksr=%u ns=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u",
|
||||
chnum, opnum,
|
||||
ch_block_freq(choffs),
|
||||
ch_feedback(choffs),
|
||||
@@ -405,25 +405,25 @@ std::string opl_registers_base<Revision>::log_keyon(uint32_t choffs, uint32_t op
|
||||
op_eg_sustain(opoffs));
|
||||
|
||||
if (OUTPUTS > 1)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " out=%c%c%c%c",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " out=%c%c%c%c",
|
||||
ch_output_0(choffs) ? 'L' : '-',
|
||||
ch_output_1(choffs) ? 'R' : '-',
|
||||
ch_output_2(choffs) ? '0' : '-',
|
||||
ch_output_3(choffs) ? '1' : '-');
|
||||
if (op_lfo_am_enable(opoffs) != 0)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u", lfo_am_depth());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", lfo_am_depth());
|
||||
if (op_lfo_pm_enable(opoffs) != 0)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u", lfo_pm_depth());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", lfo_pm_depth());
|
||||
if (waveform_enable() && op_waveform(opoffs) != 0)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " wf=%u", op_waveform(opoffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs));
|
||||
if (is_rhythm(choffs))
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " rhy=1");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " rhy=1");
|
||||
if (DYNAMIC_OPS)
|
||||
{
|
||||
operator_mapping map;
|
||||
operator_map(map);
|
||||
if (bitfield(map.chan[chnum], 16, 8) != 0xff)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " 4op");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " 4op");
|
||||
}
|
||||
|
||||
return buffer;
|
||||
@@ -685,9 +685,9 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
uint32_t opnum = opoffs;
|
||||
|
||||
char buffer[256];
|
||||
char *end = &buffer[0];
|
||||
int end = 0;
|
||||
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u freq=%04X inst=%X fb=%u mul=%X",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X inst=%X fb=%u mul=%X",
|
||||
chnum, opnum,
|
||||
ch_block_freq(choffs),
|
||||
ch_instrument(choffs),
|
||||
@@ -695,11 +695,11 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
op_multiple(opoffs));
|
||||
|
||||
if (bitfield(opoffs, 0) == 1 || (is_rhythm(choffs) && choffs >= 6))
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " vol=%X", op_volume(opoffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " vol=%X", op_volume(opoffs));
|
||||
else
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " tl=%02X", ch_total_level(choffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " tl=%02X", ch_total_level(choffs));
|
||||
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " ksr=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u/%u",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " ksr=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u/%u",
|
||||
op_ksr(opoffs),
|
||||
op_ksl(opoffs),
|
||||
op_attack_rate(opoffs),
|
||||
@@ -710,13 +710,13 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
ch_sustain(choffs));
|
||||
|
||||
if (op_lfo_am_enable(opoffs))
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=1");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " am=1");
|
||||
if (op_lfo_pm_enable(opoffs))
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=1");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=1");
|
||||
if (op_waveform(opoffs) != 0)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " wf=1");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=1");
|
||||
if (is_rhythm(choffs))
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " rhy=1");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " rhy=1");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -60,17 +60,17 @@ opm_registers::opm_registers() :
|
||||
{
|
||||
// waveform 0 is a sawtooth
|
||||
uint8_t am = index ^ 0xff;
|
||||
int8_t pm = int8_t(index);
|
||||
uint8_t pm = index;
|
||||
m_lfo_waveform[0][index] = am | (pm << 8);
|
||||
|
||||
// waveform 1 is a square wave
|
||||
am = bitfield(index, 7) ? 0 : 0xff;
|
||||
pm = int8_t(am ^ 0x80);
|
||||
pm = am ^ 0x80;
|
||||
m_lfo_waveform[1][index] = am | (pm << 8);
|
||||
|
||||
// waveform 2 is a triangle wave
|
||||
am = bitfield(index, 7) ? (index << 1) : ((index ^ 0xff) << 1);
|
||||
pm = int8_t(bitfield(index, 6) ? am : ~am);
|
||||
pm = bitfield(index, 6) ? am : ~am;
|
||||
m_lfo_waveform[2][index] = am | (pm << 8);
|
||||
|
||||
// waveform 3 is noise; it is filled in dynamically
|
||||
@@ -330,7 +330,7 @@ uint32_t opm_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opd
|
||||
if (pm_sensitivity < 6)
|
||||
delta += lfo_raw_pm >> (6 - pm_sensitivity);
|
||||
else
|
||||
delta += lfo_raw_pm << (pm_sensitivity - 5);
|
||||
delta += uint32_t(lfo_raw_pm) << (pm_sensitivity - 5);
|
||||
}
|
||||
|
||||
// apply delta and convert to a frequency number
|
||||
@@ -354,9 +354,9 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
uint32_t opnum = opoffs;
|
||||
|
||||
char buffer[256];
|
||||
char *end = &buffer[0];
|
||||
int end = 0;
|
||||
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
|
||||
chnum, opnum,
|
||||
ch_block_freq(choffs),
|
||||
op_detune2(opoffs),
|
||||
@@ -376,14 +376,14 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
|
||||
bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0);
|
||||
if (am)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth());
|
||||
bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0);
|
||||
if (pm)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth());
|
||||
if (am || pm)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]);
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]);
|
||||
if (noise_enable() && opoffs == 31)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " noise=1");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -409,9 +409,9 @@ std::string opn_registers_base<IsOpnA>::log_keyon(uint32_t choffs, uint32_t opof
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
char *end = &buffer[0];
|
||||
int end = 0;
|
||||
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X",
|
||||
chnum, opnum,
|
||||
block_freq,
|
||||
op_detune(opoffs),
|
||||
@@ -427,21 +427,21 @@ std::string opn_registers_base<IsOpnA>::log_keyon(uint32_t choffs, uint32_t opof
|
||||
op_sustain_level(opoffs));
|
||||
|
||||
if (OUTPUTS > 1)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " out=%c%c",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " out=%c%c",
|
||||
ch_output_0(choffs) ? 'L' : '-',
|
||||
ch_output_1(choffs) ? 'R' : '-');
|
||||
if (op_ssg_eg_enable(opoffs))
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " ssg=%X", op_ssg_eg_mode(opoffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " ssg=%X", op_ssg_eg_mode(opoffs));
|
||||
bool am = (op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0);
|
||||
if (am)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u", ch_lfo_am_sens(choffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", ch_lfo_am_sens(choffs));
|
||||
bool pm = (ch_lfo_pm_sens(choffs) != 0);
|
||||
if (pm)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u", ch_lfo_pm_sens(choffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs));
|
||||
if (am || pm)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo=%02X", lfo_rate());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate());
|
||||
if (multi_freq() && choffs == 2)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " multi=1");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " multi=1");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -339,9 +339,9 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
uint32_t opnum = opoffs;
|
||||
|
||||
char buffer[256];
|
||||
char *end = &buffer[0];
|
||||
int end = 0;
|
||||
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u freq=%04X dt=%+2d fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt=%+2d fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
|
||||
chnum, opnum,
|
||||
(opoffs & 1) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs),
|
||||
int32_t(op_detune(opoffs)) - 0x20,
|
||||
@@ -360,14 +360,14 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
|
||||
bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0);
|
||||
if (am)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u", ch_lfo_am_sens(choffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", ch_lfo_am_sens(choffs));
|
||||
bool pm = (lfo_enable() && ch_lfo_pm_sens(choffs) != 0);
|
||||
if (pm)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u", ch_lfo_pm_sens(choffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs));
|
||||
if (am || pm)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo=%02X", lfo_rate());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate());
|
||||
if (ch_reverb(choffs))
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " reverb");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " reverb");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -129,17 +129,17 @@ opz_registers::opz_registers() :
|
||||
{
|
||||
// waveform 0 is a sawtooth
|
||||
uint8_t am = index ^ 0xff;
|
||||
int8_t pm = int8_t(index);
|
||||
uint8_t pm = index;
|
||||
m_lfo_waveform[0][index] = am | (pm << 8);
|
||||
|
||||
// waveform 1 is a square wave
|
||||
am = bitfield(index, 7) ? 0 : 0xff;
|
||||
pm = int8_t(am ^ 0x80);
|
||||
pm = am ^ 0x80;
|
||||
m_lfo_waveform[1][index] = am | (pm << 8);
|
||||
|
||||
// waveform 2 is a triangle wave
|
||||
am = bitfield(index, 7) ? (index << 1) : ((index ^ 0xff) << 1);
|
||||
pm = int8_t(bitfield(index, 6) ? am : ~am);
|
||||
pm = bitfield(index, 6) ? am : ~am;
|
||||
m_lfo_waveform[2][index] = am | (pm << 8);
|
||||
|
||||
// waveform 3 is noise; it is filled in dynamically
|
||||
@@ -555,16 +555,16 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
uint32_t opnum = opoffs;
|
||||
|
||||
char buffer[256];
|
||||
char *end = &buffer[0];
|
||||
int end = 0;
|
||||
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, "%u.%02u", chnum, opnum);
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u", chnum, opnum);
|
||||
|
||||
if (op_fix_mode(opoffs))
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(opoffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(opoffs));
|
||||
else
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs));
|
||||
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
|
||||
op_detune(opoffs),
|
||||
ch_feedback(choffs),
|
||||
ch_algorithm(choffs),
|
||||
@@ -580,32 +580,32 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
|
||||
ch_output_1(choffs) ? 'R' : '-');
|
||||
|
||||
if (op_eg_shift(opoffs) != 0)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " egshift=%u", op_eg_shift(opoffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " egshift=%u", op_eg_shift(opoffs));
|
||||
|
||||
bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0);
|
||||
if (am)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth());
|
||||
bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0);
|
||||
if (pm)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth());
|
||||
if (am || pm)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]);
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]);
|
||||
|
||||
bool am2 = (lfo2_am_depth() != 0 && ch_lfo2_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0);
|
||||
if (am2)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth());
|
||||
bool pm2 = (lfo2_pm_depth() != 0 && ch_lfo2_pm_sens(choffs) != 0);
|
||||
if (pm2)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth());
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth());
|
||||
if (am2 || pm2)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]);
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]);
|
||||
|
||||
if (op_reverb_rate(opoffs) != 0)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " rev=%u", op_reverb_rate(opoffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " rev=%u", op_reverb_rate(opoffs));
|
||||
if (op_waveform(opoffs) != 0)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " wf=%u", op_waveform(opoffs));
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs));
|
||||
if (noise_enable() && opoffs == 31)
|
||||
end += snprintf(end, SNPRINTF_BUFFER_SIZE_CALC, " noise=1");
|
||||
end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user