summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/ymfm
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/ymfm')
-rw-r--r--3rdparty/ymfm/.editorconfig9
-rw-r--r--3rdparty/ymfm/.gitignore40
-rw-r--r--3rdparty/ymfm/GeneralInfo.md282
-rw-r--r--3rdparty/ymfm/LICENSE29
-rw-r--r--3rdparty/ymfm/README.md125
-rw-r--r--3rdparty/ymfm/examples/buildall/buildall.cpp114
-rw-r--r--3rdparty/ymfm/examples/vgmrender/.gitignore1
-rw-r--r--3rdparty/ymfm/examples/vgmrender/em_inflate.cpp1185
-rw-r--r--3rdparty/ymfm/examples/vgmrender/em_inflate.h49
-rw-r--r--3rdparty/ymfm/examples/vgmrender/vgmrender.cpp1439
-rw-r--r--3rdparty/ymfm/src/ymfm.h566
-rw-r--r--3rdparty/ymfm/src/ymfm_adpcm.cpp807
-rw-r--r--3rdparty/ymfm/src/ymfm_adpcm.h411
-rw-r--r--3rdparty/ymfm/src/ymfm_fm.h463
-rw-r--r--3rdparty/ymfm/src/ymfm_fm.ipp1592
-rw-r--r--3rdparty/ymfm/src/ymfm_misc.cpp175
-rw-r--r--3rdparty/ymfm/src/ymfm_misc.h93
-rw-r--r--3rdparty/ymfm/src/ymfm_opl.cpp2220
-rw-r--r--3rdparty/ymfm/src/ymfm_opl.h902
-rw-r--r--3rdparty/ymfm/src/ymfm_opm.cpp539
-rw-r--r--3rdparty/ymfm/src/ymfm_opm.h322
-rw-r--r--3rdparty/ymfm/src/ymfm_opn.cpp2485
-rw-r--r--3rdparty/ymfm/src/ymfm_opn.h802
-rw-r--r--3rdparty/ymfm/src/ymfm_opq.cpp480
-rw-r--r--3rdparty/ymfm/src/ymfm_opq.h293
-rw-r--r--3rdparty/ymfm/src/ymfm_opx.h290
-rw-r--r--3rdparty/ymfm/src/ymfm_opz.cpp808
-rw-r--r--3rdparty/ymfm/src/ymfm_opz.h332
-rw-r--r--3rdparty/ymfm/src/ymfm_pcm.cpp714
-rw-r--r--3rdparty/ymfm/src/ymfm_pcm.h347
-rw-r--r--3rdparty/ymfm/src/ymfm_ssg.cpp279
-rw-r--r--3rdparty/ymfm/src/ymfm_ssg.h207
32 files changed, 18400 insertions, 0 deletions
diff --git a/3rdparty/ymfm/.editorconfig b/3rdparty/ymfm/.editorconfig
new file mode 100644
index 00000000000..29f634bbe5e
--- /dev/null
+++ b/3rdparty/ymfm/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_size = 4
+indent_style = tab
+insert_final_newline = true
+tab_width = 4
+trim_trailing_whitespace = true
diff --git a/3rdparty/ymfm/.gitignore b/3rdparty/ymfm/.gitignore
new file mode 100644
index 00000000000..c1ef5b25572
--- /dev/null
+++ b/3rdparty/ymfm/.gitignore
@@ -0,0 +1,40 @@
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# Symbol/intermediate files
+*.pdb
+*.ilk
+
+# VS Code stuff
+.vs/
+reference/
diff --git a/3rdparty/ymfm/GeneralInfo.md b/3rdparty/ymfm/GeneralInfo.md
new file mode 100644
index 00000000000..52cdbf5e81b
--- /dev/null
+++ b/3rdparty/ymfm/GeneralInfo.md
@@ -0,0 +1,282 @@
+# ymfm: One FM core to rule them all
+
+The ymfm emulator ws written from the ground-up using the analysis and deduction by Nemesis as a starting point, particularly in [this thread](https://gendev.spritesmind.net/forum/viewtopic.php?f=24&t=386).
+
+The core assumption is that these details apply to all FM variants unless otherwise proven incorrect.
+
+The fine details of this implementation have also been cross-checked against Nemesis' implementation in his [Exodus emulator](https://www.exodusemulator.com/), as well as Alexey Khokholov's ["Nuked" implementations](https://github.com/nukeykt/Nuked-OPN2) based off die shots.
+
+Operator and channel summing/mixing code for OPM and OPN is largely based off of research done by [David Viens](https://twitter.com/plgDavid) and Hubert Lamontagne.
+
+## Families
+
+The Yamaha FM chips can be broadly categoried into families:
+
+* OPM (YM2151)
+ * OPP (YM2164)
+* OPN (YM2203)
+ * OPNA/OPNB/OPN2 (YM2608, YM2610, YM2610B, YM2612, YM3438, YMF276, YMF288)
+* OPL (YM3526)
+ * OPL2 (YM3812)
+ * OPLL (YM2413, YM2423, YMF281, DS1001, and others)
+ * OPL3 (YMF262, YMF289B)
+ * OPL4 (YMF278)
+
+Additionally, several lesser-documented variants exist exclusively in the employ of Yamaha synthesizers:
+
+* OPQ (YM3608)
+* OPZ (YM2414)
+
+All of these families are very closely related, and the ymfm engine is designed to be universal to work across all of
+these families.
+
+Of course, each variant has its own register maps, features, and implementation details which need to be sorted out.
+Thus, each significant variant listed above is represented by a register class.
+The register class contains:
+
+* constants describing core parameters and features
+* mappers between operators and channels
+* generic fetchers that return normalized values across families
+* family-specific implementations of LFO and phase calculations
+
+## Family History
+
+This history outlines the progress of adding/removing features across the three main families (OPM, OPN, OPL):
+
+OPM started it all off, featuring:
+* 8 FM channels, 4 operators each
+* LFO and noise support
+* Stereo output
+
+OPM -> OPN changes:
+* Reduced to 3 FM channels, 4 operators each
+* Removed LFO and noise support
+* Mono output
+* Integrated AY-8910 compatible PSG
+* Added SSG-EG envelope mode
+* Added multi-frequency mode: ch. 3 operators can have separate frequencies
+* Software controlled clock divider
+
+OPN -> OPNA changes:
+* Increased to 6 FM channels, 4 operators each
+* Added back (a cut-down) LFO
+* Stereo output again
+* Removed software controlled divider on later versions (OPNB/OPN2)
+* Removed PSG on OPN2 models
+
+OPNA -> OPL changes:
+* Increased to 9 FM channels, but only 2 operators each
+* Even more simplified LFO
+* Mono output
+* Removed PSG
+* Removed SSG-EG envelope modes
+* Removed multi-frequency modes
+* Fixed clock divider
+* Built-in ryhthm generation
+
+OPL -> OPL2 changes:
+* Added 4 selectable waveforms
+
+OPL2 -> OPLL changes:
+* Vastly simplified register map
+* 15 built-in instruments, plus built-in rhythm instruments
+* 1 user-controlled instrument
+
+OPL2 -> OPL3 changes:
+* Increased to 18 FM channels, 2 operators each
+* 4 output channels
+* Increased to 8 selectable waveforms
+* 6 channels can be configured to use 4 operators
+
+## Channels and Operators
+
+The polyphony of a given chip is determined by the number of channels it supports.
+This number ranges from as low as 3 to as high as 18.
+Each channel has either 2 or 4 operators that can be combined in a myriad of ways.
+On most chips the number of operators per channel is fixed; however, some later OPL chips allow this to be toggled between 2 and 4 at runtime.
+
+The base ymfm engine class maintains an array of channels and operators, while the relationship between the two is described by the register class.
+
+## Registers
+
+Registers on the Yamaha chips are generally write-only, and can be divided into three distinct categories:
+
+* system-wide registers
+* channel-specific registers
+* operator-specific registers
+
+For maximum flexibility, most parameters can be configured at the operator level, with channel-level registers controlling details such as how to combine the operators into the final output.
+System-wide registers are used to control chip-wide modes and manage onboard timer functions.
+
+Note that since registers are write-only, some ymfm register classes will use "holes" in the register space to store additional values that may be needed.
+
+## Attenuation
+
+Most of the computations of the FM engines are done in terms of attenuation, and thus are logarithmic in nature.
+The maximum resolution used internally is 12 bits, as returned by the sin table:
+
+Bit | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+----|----|----|----|----|----|----|-----|------|-------|--------|---------|---------
+ dB | -96| -48| -24| -12| -6| -3| -1.5| -0.75| -0.375| -0.1875| -0.09375| -0.046875
+
+The envelope generator internally uses 10 bits:
+
+Bit | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+----|----|----|----|----|----|-----|------|-------|--------|---------|
+ dB | -48| -24| -12| -6| -3| -1.5| -0.75| -0.375| -0.1875| -0.09375|
+
+Total level for operators is usually represented by 7 bits:
+
+Bit | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+----|----|----|----|----|----|-----|------|
+ dB | -48| -24| -12| -6| -3| -1.5| -0.75|
+
+Sustain level in the envelope generator is usually represented by 4 bits:
+
+Bit | 3 | 2 | 1 | 0 |
+----|----|----|----|----|
+ dB | -24| -12| -6| -3|
+
+## Status and Timers
+
+Generically, all chips (except OPLL) support two timers that can be programmed to fire and signal IRQs.
+These timers also set bits in the status register.
+The behavior of these bits is shared across all implementations, even if the exact bit positions shift (this is controlled by constants in the registers class).
+
+In addition, several chips incorporate ADPCM decoders which also may set bits in the same status register.
+For this reason, it is possible to control various bits in the status register via the `set_reset_status()` function directly.
+Any active bits that are set and which are not masked (mask is controlled by `set_irq_mask()`), lead to an IRQ being signalled.
+
+Thus, it is possible for the chip-specific implementations to set the mask and control the status register bits such that IRQs are signalled via the same mechanism as timer signals.
+
+In addition, the OPM and OPN families have a "busy" flag, which is set after each write, indicating that another write should not be performed.
+Historically, the duration of this flag was constant and had nothing to do with the internals of the chip.
+However, since the details can potentially vary chip-to-chip, it is the chip's responsibility to insert the busy flag into the status before returning it to the caller.
+
+## Clocking
+
+Each of the Yamaha chips works by cycling through all operators one at a time.
+Thus, the effective output rate of the chips is related to the input clock divided by the number of operators.
+In addition, the input clock is prescaled by an amount.
+Generally, this is a fixed value, though some early OPN chips allow this to be selected at runtime from a small
+number of values.
+
+## Channel Frequencies
+
+One major difference between OPM and later families is in how frequencies are specified.
+OPM specifies frequency via a 3-bit 'block' (aka octave), combined with a 4-bit 'key code' (note number) and a 6-bit 'key fraction'.
+The key code and fraction are converted on the chip into an x.11 fixed-point value and then shifted by the block to produce the final step value for the phase.
+
+Later families, on the other hand, specify frequencies via a 3-bit 'block' just as on OPM, but combined with a 9-12-bit 'frequency number' or 'fnum', which is directly shifted by the block to produce the step value.
+So essentially, later chips make the user do the conversion from note value to phase increment, while OPM is programmed in a more 'musical' way, specifying notes and cents.
+
+Internally, this is abstracted away into a 'block_freq' value, which is a 16-bit value containing the block and frequency info concatenated together as follows:
+
+* OPM: `[3-bit block]:[4-bit keycode]:[6-bit fraction] = 13 bits total`
+
+* OPZ: `[3-bit block]:[12-bit fnum] = 15 bits total`
+* OPN: `[3-bit block]:[11-bit fnum] 0 = 15 bits total`
+* OPL: `[3-bit block]:[10-bit fnum]:00 = 15 bits total`
+* OPLL: `[3-bit block]:[ 9-bit fnum]:000 = 15 bits total`
+
+The register classes handle the raw format directly and convert it into a phase increment which can be used by the generic engine.
+
+## Low Frequency Oscillator (LFO)
+
+The LFO engines are different in several key ways.
+The OPM LFO engine is fairly intricate.
+It has a 4.4 floating-point rate which allows for a huge range of frequencies, and can select between four different waveforms (sawtooth, square, triangle, or noise).
+Separate 7-bit depth controls for AM and PM control the amount of modulation applied in each case.
+This global LFO value is then further controlled at the channel level by a 2-bit AM sensitivity and a 3-bit PM sensitivity, and each operator has a 1-bit AM on/off switch.
+
+For OPN the LFO engine was removed entirely, but a limited version was put back in OPNA and later chips.
+This stripped-down version offered only a 3-bit rate setting (versus the 4.4 floating-point rate in OPN), and no
+global depth control.
+It did bring back the channel-level sensitivity controls and the operator-level on/off control.
+
+For OPL, the LFO is simplified again, with AM and PM running at fixed frequencies, and simple enable flags at the operator level for each controlling their application.
+
+## Differences Between Families
+
+The table below provides some high level functional differences between the differnet families:
+
+subfamily: | OPM | OPN | OPNA | OPL | OPL2 | OPLL | OPL3 |
+------------:|:------:|:------:|:------:|:------:|:------:|:------:|:------:|
+outputs: | 2 | 1 | 2 | 1 | 1 | 1 | 4 |
+channels: | 8 | 3 | 6 | 9 | 9 | 9 | 18 |
+operators: | 32 | 12 | 24 | 18 | 18 | 18 | 36 |
+waveforms: | 1 | 1 | 1 | 1 | 4 | 2 | 8 |
+instruments: | no | no | no | yes | yes | yes | yes |
+ryhthm: | no | no | no | no | no | yes | no |
+dynamic ops: | no | no | no | no | no | no | yes |
+prescale: | 2 | 2/3/6 | 2/3/6 | 4 | 4 | 4 | 8 |
+EG divider: | 3 | 3 | 3 | 1 | 1 | 1 | 1 |
+EG DP: | no | no | no | no | no | yes | no |
+EG SSG: | no | yes | yes | no | no | no | no |
+mod delay: | no | no | no | yes | yes | yes? | no |
+CSM: | yes | ch 2 | ch 2 | yes | yes | yes | no |
+LFO: | yes | no | yes | yes | yes | yes | yes |
+noise: | yes | no | no | no | no | no | no |
+
+* Outputs represents the number of output channels: 1=mono, 2=stereo, 4=stereo+.
+* Channels represents the number of independent FM channels.
+* Operators represents the number of operators, or "slots" which are assembled into the channels.
+* Waveforms represents the number of different sine-derived waveforms available.
+* Instruments indicates whether the family has built-in instruments.
+* Rhythm indicates whether the family has a built-in rhythm
+* Dynamic ops indicates whether it is possible to switch between 2-operator and 4-operator modes dynamically.
+* Prescale specifies the default clock divider; some chips allow this to be controlled via register writes.
+* EG divider represents the divider applied to the envelope generator clock.
+* EG DP indicates whether the envelope generator includes a DP (depress?) phase at the beginning of each key on.
+* SSG EG indicates whether the envelope generator has SSG-style support.
+* Mod delay indicates whether the connection to the first modulator's input is delayed by 1 sample.
+* CSM indicates whether CSM mode is supported, triggered by timer A.
+* LFO indicates whether LFO is supported.
+* Noise indicates whether one of the operators can be replaced with a noise source.
+
+## Chip Specifics
+
+While OPM is its own thing, the OPN and OPL families have quite a few specific
+implementations, with many differing details beyond the core FM parts. Here are
+some details on the OPN family:
+
+chip ID: | YM2203 | YM2608 | YMF288 | YM2610 | YM2610B | YM2612 | YM3438 | YMF276 |
+---------:|:------:|:------:|:------:|:------:|:-------:|:------:|:------:|:------:|
+aka: | OPN | OPNA | OPN3L | OPNB | OPNB2 | OPN2 | OPN2C | OPN2L |
+FM: | 3 | 6 | 6 | 4 | 6 | 6 | 6 | 6 |
+AY-8910: | 3 | 1 | 1 | 1 | 1 | - | - | - |
+ADPCM-A: | - | 6 int | 6 int | 6 ext | 6 ext | - | - | - |
+ADPCM-B: | - | 1 ext | - | 1 ext | 1 ext | - | - | - |
+DAC: | no | no | no | no | no | yes | yes | yes |
+output: | 10.3fp | 16-bit | 16-bit | 16-bit | 16-bit | 9-bit | 9-bit | 16-bit |
+summing: | adder | adder | adder | adder | adder | muxer | muxer | adder |
+
+* FM represents the number of FM channels available.
+* AY-8910 represents the number of AY-8910-compatible outputs.
+* ADPCM-A represents the number of internal/external ADPCM-A channels present.
+* ADPCM-B represents the number of internal/external ADPCM-B channels present.
+* DAC indicates if a directly-accessible DAC output exists, replacing one channel.
+* Output indicates the output format to the final DAC.
+* Summing indicates whether channels are added or time divided in the output.
+
+OPL has a similar trove of chip variants:
+
+chip ID: | YM3526 | Y8950 | YM3812 | YM2413 | YMF262 | YMF289B | YMF278B |
+------------:|:------:|:-------:|:------:|:------:|:------:|:-------:|:-------:|
+aka: | OPL |MSX-AUDIO| OPL2 | OPLL | OPL3 | OPL3L | OPL4 |
+FM: | 9 | 9 | 9 | 9 | 18 | 18 | 18 |
+ADPCM-B: | - | 1 ext | - | - | - | - | - |
+wavetable: | - | - | - | - | - | - | 24 |
+instruments: | no | no | no | yes | no | no | no |
+output: | 10.3fp | 10.3fp | 10.3fp | 9-bit | 16-bit | 16-bit | 16-bit |
+summing: | adder | adder | adder | muxer | adder | adder | adder |
+
+* FM represents the number of FM channels available.
+* ADPCM-B represents the number of external ADPCM-B channels present.
+* Wavetable indicates the number of wavetable channels present.
+* Instruments indicates that the chip has built-in instrument selection.
+* Output indicates the output format to the final DAC.
+* Summing indicates whether channels are added or time divided in the output.
+
+There are several close variants of the YM2413 with different sets of built-in instruments.
+These include the YM2423, YMF281, and DS1001 (aka Konami VRC7).
diff --git a/3rdparty/ymfm/LICENSE b/3rdparty/ymfm/LICENSE
new file mode 100644
index 00000000000..f73e841aa27
--- /dev/null
+++ b/3rdparty/ymfm/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2021, Aaron Giles
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/3rdparty/ymfm/README.md b/3rdparty/ymfm/README.md
new file mode 100644
index 00000000000..29f0d87dca9
--- /dev/null
+++ b/3rdparty/ymfm/README.md
@@ -0,0 +1,125 @@
+# ymfm
+
+<div style='text-align:center;margin:auto'>
+<img src='https://aarongiles.com/img/icon-ymfm.png' width='128px'>
+</div>
+
+[ymfm](https://github.com/aaronsgiles/ymfm) is a collection of BSD-licensed Yamaha FM sound cores (OPM, OPN, OPL, and others), written by [Aaron Giles](https://aarongiles.com)
+
+## Supported environments
+
+This code should compile cleanly in any environment that has C++14 support.
+It has been tested on gcc, clang, and Microsoft Visual C++ 2019.
+
+## Supported chip families
+
+Currently, support is present for the following chips (organized by header file):
+
+* ymfm_misc.h:
+ * YM2149 (SSG) [1983: MSX; Atari ST]
+* ymfm_opm.h:
+ * YM2151 (OPM) [1983: Sharp X1, X68000; MSX; synths: DX21, DX27, DX100]
+ * YM2164 (OPP) [1985: FB-01 MIDI Expander; IBM Music Feature Card; MSX; synths: Korg DS-8, 707]
+* ymfm_opn.h:
+ * YM2203 (OPN) [1984: NEC PC-88, PC-98, NEC PC-6001mkII SR, PC-6601 SR]
+ * YM2608 (OPNA) [1985: NEC PC-88, PC-98]
+ * YM2610 (OPNB) [1987: Neo Geo]
+ * YM2610B (OPNB2)
+ * YM2612 (OPN2) [1988: Sega Mega Drive/Genesis; FM Towns]
+ * YM3438 (OPN2C)
+ * YMF276 (OPN2L)
+ * YMF288 (OPN3L) [1995: NEC PC-98]
+* ymfm_opl.h:
+ * YM3526 (OPL) [1984: C64 SFX Sound Expander]
+ * Y8950 (MSX-Audio) [1984: MSX]
+ * YM3812 (OPL2) [1985: AdLib, Sound Blaster; synths: some Portasound keyboards]
+ * YMF262 (OPL3) [1988: Sound Blaster Pro 2.0, SB16]
+ * YMF289B (OPL3L)
+ * YMF278B (OPL4) [1993: MSX Moonsound cartridge]
+ * YM2413 (OPLL) [1986: Sega Master System, Mark III; MSX; synths: Portasound PSS-140, PSS-170, PSS-270]
+ * YM2423 (OPLL-X)
+ * YMF281 (OPLLP)
+ * DS1001 (Konami 053982/VRC7) [1991: Famicom cartridge Lagrange Point]
+* ymfm_opq.h:
+ * YM3806 (OPQ) [synths: PSR-60/70]
+* ymfm_opz.h:
+ * YM2414 (OPZ) [1987: synths: TX81Z, DX11, YS200; Korg Z3 guitar synth]
+
+There are some obviously-related chips that also are on my horizon but have no implementation as yet:
+
+* YMW-258-F 'GEW8' (aka Sega 315-5560 aka Sega Multi-PCM)
+* YMF271 (OPX)
+* YM21280 (OPS) / YM21290 (EGS) [synths: DX7, DX1, DX5, DX9, TX7, TX216, TX416, TX816]
+* OPK?
+
+## History
+
+These cores were originally written during the summer and fall of 2020 as part of the [MAME](https://mamedev.org/) project.
+As such, their design started off heavily based on how MAME works.
+
+The OPM/OPN cores first appeared in MAME 0.230.
+The OPL cores were added in MAME 0.231.
+A further rewrite to abstract MAME dependencies is planned for MAME 0.232.
+
+The goal was threefold:
+1. provide BSD-licensed emulation cores that are more compatible with MAME's core licensing
+1. modernize and unify the code around a common implementation of shared features
+1. improve accuracy where possible based on discoveries made by others
+
+## Accuracy
+
+The goal of these cores is not 100% digital accuracy.
+To achieve that would require full emulation of the pipelines, which would make the code extremely difficult to comprehend.
+It would also make it much harder to share common implementations of features, or to add support for less well-known chip types.
+If you want that level of accuracy, there are [several](https://github.com/nukeykt/Nuked-OPN2) [decap-based](https://github.com/nukeykt/Nuked-OPM) [emulation cores](https://github.com/nukeykt/Nuked-OPLL) out there.
+
+Instead, the main goals are:
+1. Extremely high (audibly indistinguishable) accuracy
+1. Reasonable performance
+1. Clean design with readable code
+1. Clear documentation of the various chips
+
+## General approach
+
+Check out the [examples directory](https://github.com/aaronsgiles/ymfm/tree/main/examples) for some example usage patterns.
+I'm not a big fan of makefiles for simple things, so instructions on how to compile each example are provided at the top.
+
+# IMPORTANT
+
+As of May 2021, the interface to these is still a bit in flux.
+Be prepared when syncing with upstream to make some adjustments.
+
+### Clocking
+
+The general philosophy of the emulators provided here is that they are clock-independent.
+Much like the actual chips, you (the consumer) control the clock; the chips themselves have no idea what time it is.
+They just tick forward each time you ask them to.
+
+The way you move things along is via the `generate()` function, which ticks the internal system forward one or more samples, and writes out an array out chip-specific `output_data`.
+But what, exactly, is a "sample", and how long is it?
+
+This is where the external clock comes in.
+Most of the Yamaha chips are externally clocked in the MHz range.
+They then divide that clock by a factor (sometimes dynamically controllable), and then the internal operators are pipelined to further divide the clock.
+
+For example, the YM2151 internally divides the clock by 2, and has 32 operators to iterate through.
+Thus, for a nominal input lock of 3.58MHz, you end up at around a 55.9kHz sample rate.
+Fortunately, all the chip implementations can compute this for you; just pass the raw external clock value to the `sample_rate()` method and it will hand you back the output sample rate you want.
+
+Then call `generate()` that many times per second to output the results.
+
+But what if I want to output at a "normal" rate, like 44.1kHz?
+Sorry, you'll have to rate convert as needed.
+
+### Reading and Writing
+
+To read or write to the chips, you can call the `read()` and `write()` methods.
+The offset provided corresponds to the addressing input lines in a (hopefully) logical way.
+
+For reads, almost all chips have a status register, which you can read via `read_status()`.
+Some chips have a data port that can be read via `read_data()`.
+And chips with extended addressing may also have `read_status_hi()` and `read_data_hi()`.
+
+For writes, almost all chips have an address register and a data register, and so you can reliably count on there being a `write_address()` and `write_data()` method as well.
+If the chip supports extended addressing, it may also have `write_address_hi()` and `write_data_hi()`.
+
diff --git a/3rdparty/ymfm/examples/buildall/buildall.cpp b/3rdparty/ymfm/examples/buildall/buildall.cpp
new file mode 100644
index 00000000000..40bd75e2864
--- /dev/null
+++ b/3rdparty/ymfm/examples/buildall/buildall.cpp
@@ -0,0 +1,114 @@
+//
+// Simple program that touches all the existing cores to help ensure
+// that everything builds cleanly.
+//
+// Compile with:
+//
+// g++ --std=c++14 -I../../src buildall.cpp ../../src/ymfm_misc.cpp ../../src/ymfm_opl.cpp ../../src/ymfm_opm.cpp ../../src/ymfm_opn.cpp ../../src/ymfm_opq.cpp ../../src/ymfm_opz.cpp ../../src/ymfm_adpcm.cpp ../../src/ymfm_pcm.cpp ../../src/ymfm_ssg.cpp -o buildall.exe
+//
+// or:
+//
+// clang --std=c++14 -I../../src buildall.cpp ../../src/ymfm_misc.cpp ../../src/ymfm_opl.cpp ../../src/ymfm_opm.cpp ../../src/ymfm_opn.cpp ../../src/ymfm_opq.cpp ../../src/ymfm_opz.cpp ../../src/ymfm_adpcm.cpp ../../src/ymfm_pcm.cpp ../../src/ymfm_ssg.cpp -o buildall.exe
+//
+// or:
+//
+// cl -I..\..\src buildall.cpp ..\..\src\ymfm_misc.cpp ..\..\src\ymfm_opl.cpp ..\..\src\ymfm_opm.cpp ..\..\src\ymfm_opn.cpp ..\..\src\ymfm_opq.cpp ..\..\src\ymfm_opz.cpp ..\..\src\ymfm_adpcm.cpp ..\..\src\ymfm_pcm.cpp ..\..\src\ymfm_ssg.cpp /Od /Zi /std:c++14 /EHsc
+//
+
+#include <vector>
+
+#include "ymfm_misc.h"
+#include "ymfm_opl.h"
+#include "ymfm_opm.h"
+#include "ymfm_opn.h"
+#include "ymfm_opq.h"
+#include "ymfm_opz.h"
+
+
+//-------------------------------------------------
+// main - program entry point
+//-------------------------------------------------
+
+template<typename ChipType>
+class chip_wrapper : public ymfm::ymfm_interface
+{
+public:
+ chip_wrapper() :
+ m_chip(*this)
+ {
+ // reset
+ m_chip.reset();
+
+ // save/restore
+ std::vector<uint8_t> buffer;
+ {
+ ymfm::ymfm_saved_state saver(buffer, true);
+ m_chip.save_restore(saver);
+ }
+ {
+ ymfm::ymfm_saved_state restorer(buffer, false);
+ m_chip.save_restore(restorer);
+ }
+
+ // dummy read/write
+ m_chip.read(0);
+ m_chip.write(0, 0);
+
+ // generate
+ typename ChipType::output_data output[20];
+ m_chip.generate(&output[0], ymfm::array_size(output));
+ }
+
+private:
+ ChipType m_chip;
+};
+
+
+//-------------------------------------------------
+// main - program entry point
+//-------------------------------------------------
+
+int main(int argc, char *argv[])
+{
+ // just keep adding chip variants here as they are implemented
+
+ // ymfm_misc.h:
+ chip_wrapper<ymfm::ym2149> test2149;
+
+ // ymfm_opl.h:
+ chip_wrapper<ymfm::ym3526> test3526;
+ chip_wrapper<ymfm::y8950> test8950;
+ chip_wrapper<ymfm::ym3812> test3812;
+ chip_wrapper<ymfm::ymf262> test262;
+ chip_wrapper<ymfm::ymf289b> test289b;
+ chip_wrapper<ymfm::ymf278b> test278b;
+ chip_wrapper<ymfm::ym2413> test2413;
+ chip_wrapper<ymfm::ym2423> test2423;
+ chip_wrapper<ymfm::ymf281> test281;
+ chip_wrapper<ymfm::ds1001> test1001;
+
+ // ymfm_opm.h:
+ chip_wrapper<ymfm::ym2151> test2151;
+ chip_wrapper<ymfm::ym2164> test2164;
+
+ // ymfm_opn.h:
+ chip_wrapper<ymfm::ym2203> test2203;
+ chip_wrapper<ymfm::ym2608> test2608;
+ chip_wrapper<ymfm::ymf288> test288;
+ chip_wrapper<ymfm::ym2610> test2610;
+ chip_wrapper<ymfm::ym2610b> test2610b;
+ chip_wrapper<ymfm::ym2612> test2612;
+ chip_wrapper<ymfm::ym3438> test3438;
+ chip_wrapper<ymfm::ymf276> test276;
+
+ // ymfm_opq.h:
+ chip_wrapper<ymfm::ym3806> test3806;
+ chip_wrapper<ymfm::ym3533> test3533;
+
+ // ymfm_opz.h:
+ chip_wrapper<ymfm::ym2414> test2414;
+
+ printf("Done\n");
+
+ return 0;
+}
diff --git a/3rdparty/ymfm/examples/vgmrender/.gitignore b/3rdparty/ymfm/examples/vgmrender/.gitignore
new file mode 100644
index 00000000000..9daeafb9864
--- /dev/null
+++ b/3rdparty/ymfm/examples/vgmrender/.gitignore
@@ -0,0 +1 @@
+test
diff --git a/3rdparty/ymfm/examples/vgmrender/em_inflate.cpp b/3rdparty/ymfm/examples/vgmrender/em_inflate.cpp
new file mode 100644
index 00000000000..b9d2f428c48
--- /dev/null
+++ b/3rdparty/ymfm/examples/vgmrender/em_inflate.cpp
@@ -0,0 +1,1185 @@
+/*
+ * em_inflate.c - fast in-memory inflate (gzip/zlib decompressor) implementation
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * gzip crc32 computation by Stephen Brumme - zlib-like license included below
+ * adler checksum computation by Mark Adler - zlib license included below
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include "em_inflate.h"
+
+/* Comment out this define to disable checksum verification */
+#define EM_INFLATE_VERIFY_CHECKSUM
+
+/*--- LSB bitreader ---*/
+
+#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__)
+#define EM_LSB_BITREADER_64BIT_SHIFTER
+#endif /* defined(_M_X64) */
+
+#ifdef EM_LSB_BITREADER_64BIT_SHIFTER
+typedef unsigned long long em_lsb_bitreader_shifter_t;
+#else
+typedef unsigned int em_lsb_bitreader_shifter_t;
+#endif
+
+/** Bit reader context */
+typedef struct {
+ int nDecShifterBitCount;
+ em_lsb_bitreader_shifter_t nDecShifterData;
+ const unsigned char *pInBlock;
+ const unsigned char *pInBlockEnd;
+ const unsigned char *pInBlockStart;
+} em_lsb_bitreader_t;
+
+/**
+ * Initialize bit reader
+ *
+ * @param pBitReader bit reader context
+ * @param pInBlock pointer to start of compressed block
+ * @param InBlockEnd pointer to end of compressed block + 1
+ */
+static inline void em_lsb_bitreader_init(em_lsb_bitreader_t *pBitReader, const unsigned char *pInBlock, const unsigned char *pInBlockEnd) {
+ pBitReader->nDecShifterBitCount = 0;
+ pBitReader->nDecShifterData = 0;
+ pBitReader->pInBlock = pInBlock;
+ pBitReader->pInBlockEnd = pInBlockEnd;
+ pBitReader->pInBlockStart = pInBlock;
+}
+
+/**
+ * Refill 32 bits at a time if the architecture allows it, otherwise do nothing.
+ *
+ * @param pBitReader bit reader context
+ */
+static inline void em_lsb_bitreader_refill_32(em_lsb_bitreader_t *pBitReader) {
+#ifdef EM_LSB_BITREADER_64BIT_SHIFTER
+ if (pBitReader->nDecShifterBitCount <= 32 && (pBitReader->pInBlock + 4) <= pBitReader->pInBlockEnd) {
+#if defined(_M_X64) || defined(__x86_64__)
+ /* Read unaligned word */
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*((unsigned int*)pBitReader->pInBlock))) << pBitReader->nDecShifterBitCount);
+ pBitReader->nDecShifterBitCount += 32;
+ pBitReader->pInBlock += 4;
+#else
+ /* Read bytes */
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*pBitReader->pInBlock++)) << pBitReader->nDecShifterBitCount);
+ pBitReader->nDecShifterBitCount += 8;
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*pBitReader->pInBlock++)) << pBitReader->nDecShifterBitCount);
+ pBitReader->nDecShifterBitCount += 8;
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*pBitReader->pInBlock++)) << pBitReader->nDecShifterBitCount);
+ pBitReader->nDecShifterBitCount += 8;
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*pBitReader->pInBlock++)) << pBitReader->nDecShifterBitCount);
+ pBitReader->nDecShifterBitCount += 8;
+#endif
+ }
+#endif /* EM_LSB_BITREADER_64BIT_SHIFTER */
+}
+
+/**
+ * Read variable bit-length value
+ *
+ * @param pBitReader bit reader context
+ * @param nBits size of value in bits (number of bits to read), 0..16
+ *
+ * @return value, or -1 for failure
+ */
+static inline unsigned int em_lsb_bitreader_get_bits(em_lsb_bitreader_t *pBitReader, const int nBits) {
+ if (pBitReader->nDecShifterBitCount < nBits) {
+ if (pBitReader->pInBlock < pBitReader->pInBlockEnd) {
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*pBitReader->pInBlock++)) << pBitReader->nDecShifterBitCount);
+ pBitReader->nDecShifterBitCount += 8;
+
+ if (pBitReader->pInBlock < pBitReader->pInBlockEnd) {
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*pBitReader->pInBlock++)) << pBitReader->nDecShifterBitCount);
+ pBitReader->nDecShifterBitCount += 8;
+ }
+ }
+ else
+ return -1;
+ }
+
+ unsigned int nValue = pBitReader->nDecShifterData & ((1 << nBits) - 1);
+ pBitReader->nDecShifterData >>= nBits;
+ pBitReader->nDecShifterBitCount -= nBits;
+ return nValue;
+}
+
+/**
+ * Peek at a 16-bit value in the bitstream (lookahead)
+ *
+ * @param pBitReader bit reader context
+ *
+ * @return value
+ */
+static inline unsigned int em_lsb_bitreader_peek_16bits(em_lsb_bitreader_t *pBitReader) {
+ if (pBitReader->nDecShifterBitCount < 16) {
+ if (pBitReader->pInBlock < pBitReader->pInBlockEnd) {
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*pBitReader->pInBlock++)) << pBitReader->nDecShifterBitCount);
+ if (pBitReader->pInBlock < pBitReader->pInBlockEnd)
+ pBitReader->nDecShifterData |= (((em_lsb_bitreader_shifter_t)(*pBitReader->pInBlock++)) << (pBitReader->nDecShifterBitCount + 8));
+ pBitReader->nDecShifterBitCount += 16;
+ }
+ }
+
+ return pBitReader->nDecShifterData & 0xffff;
+}
+
+/**
+ * Consume variable bit-length value, after reading it with em_lsb_bitreader_peek_16bits()
+ *
+ * @param pBitReader bit reader context
+ * @param nBits size of value to consume, in bits
+ */
+static inline void em_lsb_bitreader_consume_bits(em_lsb_bitreader_t *pBitReader, const int nBits) {
+ pBitReader->nDecShifterData >>= nBits;
+ pBitReader->nDecShifterBitCount -= nBits;
+}
+
+/**
+ * Re-align bitstream on a byte
+ *
+ * @param pBitReader bit reader context
+ */
+static int em_lsb_bitreader_byte_align(em_lsb_bitreader_t *pBitReader) {
+ /* Align on byte */
+ while (pBitReader->nDecShifterBitCount >= 8) {
+ /* Rewind - the bit reader can load more than 8 bits at a time */
+ pBitReader->nDecShifterBitCount -= 8;
+ pBitReader->pInBlock--;
+ if (pBitReader->pInBlock < pBitReader->pInBlockStart) return -1;
+ }
+
+ pBitReader->nDecShifterBitCount = 0;
+ pBitReader->nDecShifterData = 0;
+ return 0;
+}
+
+/*-- Huffman decoder for a LSB bitstream --*/
+
+/** Maximum number of symbols that can be read by the huffman decoder */
+#define MAX_SYMBOLS 288
+
+/** Number of symbols used to decode variable code lengths */
+#define NCODELENSYMS 19
+
+/** Number of most significant huffman codeword bits that are used to skip the initial code length finding steps */
+#define NFASTSYMBOLBITS 10
+
+/** Huffman decoding context */
+typedef struct {
+ unsigned int nFastSymbol[1 << NFASTSYMBOLBITS];
+ unsigned int nStartIndex[16]; /* One per code bit length */
+ unsigned int nSymbols;
+ int nNumSorted;
+ int nStartingPos[16];
+} em_lsb_huffman_decoder_t;
+
+/**
+ * Prepare huffman tables
+ *
+ * @param pDecoder decoding context
+ * @param pRevSymbolTable array of 2 * nSymbols entries for storing the reverse lookup table
+ * @param pCodeLength codeword lengths table
+ *
+ * @return 0 for success, -1 for failure
+ */
+static int em_lsb_huffman_decoder_prepare_table(em_lsb_huffman_decoder_t *pDecoder, unsigned int *pRevSymbolTable, const int nReadSymbols, const int nSymbols, const unsigned char *pCodeLength) {
+ int nNumSymbolsPerLen[16];
+ int i;
+
+ if (nReadSymbols < 0 || nReadSymbols > MAX_SYMBOLS || nSymbols < 0 || nSymbols > MAX_SYMBOLS || nReadSymbols > nSymbols)
+ return -1;
+ pDecoder->nSymbols = nSymbols;
+
+ /* Calculate starting positions of symbols for each code length, so we don't have to sort them */
+
+ for (i = 0; i < 16; i++)
+ nNumSymbolsPerLen[i] = 0;
+ for (i = 0; i < nReadSymbols; i++) {
+ if (pCodeLength[i] >= 16) return -1;
+ nNumSymbolsPerLen[pCodeLength[i]]++;
+ }
+
+ pDecoder->nStartingPos[0] = 0;
+ pDecoder->nNumSorted = 0;
+ for (i = 1; i < 16; i++) {
+ pDecoder->nStartingPos[i] = pDecoder->nNumSorted;
+ pDecoder->nNumSorted += nNumSymbolsPerLen[i];
+ }
+
+ /* Assign symbols for any non-zero lengths, auto sorted by increasing code length */
+
+ for (i = 0; i < nSymbols; i++)
+ pRevSymbolTable[i] = -1;
+
+ for (i = 0; i < nReadSymbols; i++) {
+ if (pCodeLength[i]) {
+ pRevSymbolTable[pDecoder->nStartingPos[pCodeLength[i]]++] = i;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Finalize huffman codewords for decoding
+ *
+ * @param pDecoder decoding context
+ * @param pRevSymbolTable array of 2 * nSymbols entries that contains the reverse lookup table
+ *
+ * @return 0 for success, -1 for failure
+ */
+static int em_lsb_huffman_decoder_finalize_table(em_lsb_huffman_decoder_t *pDecoder, unsigned int *pRevSymbolTable) {
+ const int nSymbols = pDecoder->nSymbols;
+ unsigned int nCanonicalCodeWord = 0;
+ unsigned int *nRevCodeLengthTable = pRevSymbolTable + nSymbols;
+ int nCanonicalLength = 1;
+ int i;
+
+ /* Re-create canonical huffman codewords and create left-justified words for em_lsb_huffman_decoder_read_value() */
+
+ for (i = 0; i < (1 << NFASTSYMBOLBITS); i++)
+ pDecoder->nFastSymbol[i] = 0;
+ for (i = 0; i < 16; i++)
+ pDecoder->nStartIndex[i] = 0;
+
+ i = 0;
+ while (i < pDecoder->nNumSorted) {
+ if (nCanonicalLength >= 16) return -1;
+ pDecoder->nStartIndex[nCanonicalLength] = i - nCanonicalCodeWord;
+
+ while (i < pDecoder->nStartingPos[nCanonicalLength]) {
+ /* Write canonical order -> codeword mapping */
+ if (i >= nSymbols) return -1;
+ nRevCodeLengthTable[i] = nCanonicalLength;
+
+ /* Check for malformed table */
+ if (nCanonicalCodeWord >= (1U << nCanonicalLength)) return -1;
+
+ /* Write accelerated symbol value + codeword len for the (upside down) top NFASTSYMBOLBITS bits of the codeword, at all bit positions */
+ if (nCanonicalLength <= NFASTSYMBOLBITS) {
+ unsigned int nRevWord;
+
+ /* Get upside down codeword (branchless method by Eric Biggers) */
+ nRevWord = ((nCanonicalCodeWord & 0x5555) << 1) | ((nCanonicalCodeWord & 0xaaaa) >> 1);
+ nRevWord = ((nRevWord & 0x3333) << 2) | ((nRevWord & 0xcccc) >> 2);
+ nRevWord = ((nRevWord & 0x0f0f) << 4) | ((nRevWord & 0xf0f0) >> 4);
+ nRevWord = ((nRevWord & 0x00ff) << 8) | ((nRevWord & 0xff00) >> 8);
+ nRevWord = nRevWord >> (16 - nCanonicalLength);
+
+ int nSlots = 1 << (NFASTSYMBOLBITS - nCanonicalLength);
+ while (nSlots) {
+ pDecoder->nFastSymbol[nRevWord] = (pRevSymbolTable[i] & 0xffffff) | (nCanonicalLength << 24);
+ nRevWord += (1 << nCanonicalLength);
+ nSlots--;
+ }
+ }
+
+ i++;
+ nCanonicalCodeWord++;
+ }
+ nCanonicalLength++;
+ nCanonicalCodeWord <<= 1;
+ }
+
+ while (i < nSymbols) {
+ pRevSymbolTable[i] = -1;
+ nRevCodeLengthTable[i++] = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Decode next symbol
+ *
+ * @param pDecoder decoding context
+ * @param pRevSymbolTable reverse lookup table
+ * @param pBitReader bit reader context
+ *
+ * @return symbol, or -1 for error
+ */
+static inline unsigned int em_lsb_huffman_decoder_read_value(em_lsb_huffman_decoder_t *pDecoder, const unsigned int *pRevSymbolTable, em_lsb_bitreader_t *pBitReader) {
+ unsigned int nStream = em_lsb_bitreader_peek_16bits(pBitReader);
+ unsigned int nFastSymAndBits = pDecoder->nFastSymbol[nStream & ((1 << NFASTSYMBOLBITS) - 1)];
+ if (nFastSymAndBits) {
+ /* Fast path: the symbol len is <= NFASTSYMBOLBITS, we have a symbol and bit length for the (upside-down) MSB bits */
+ em_lsb_bitreader_consume_bits(pBitReader, nFastSymAndBits >> 24);
+ return nFastSymAndBits & 0xffffff;
+ }
+
+ /* The symbol len is larger than NFASTSYMBOLBITS, decode using the slower path. By definition, the more frequent symbols are shorter and in the fast path. */
+
+ const unsigned int *nRevCodeLengthTable = pRevSymbolTable + pDecoder->nSymbols;
+ unsigned int nCodeWord = 0;
+ int nBits = 1;
+
+ do {
+ nCodeWord |= (nStream & 1);
+
+ unsigned int nTableIndex = pDecoder->nStartIndex[nBits] + nCodeWord;
+ if (nTableIndex < pDecoder->nSymbols) {
+ if (nBits == nRevCodeLengthTable[nTableIndex]) {
+ em_lsb_bitreader_consume_bits(pBitReader, nBits);
+ return pRevSymbolTable[nTableIndex];
+ }
+ }
+
+ nCodeWord <<= 1;
+ nStream >>= 1;
+ nBits++;
+ } while (nBits < 16);
+ return -1;
+}
+
+/**
+ * Read fixed bit size code lengths
+ *
+ * @param nLenBits number of bits per code length
+ * @param nReadSymbols number of symbols actually read
+ * @param nSymbols number of symbols to build codes for
+ * @param pCodeLength output code lengths table
+ * @param pBitReader bit reader context
+ *
+ * @return 0 for success, -1 for failure
+ */
+static int em_lsb_huffman_decoder_read_raw_lengths(const int nLenBits, const int nReadSymbols, const int nSymbols, unsigned char *pCodeLength, em_lsb_bitreader_t *pBitReader) {
+ static const unsigned char nCodeLenSymIndex[NCODELENSYMS] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; /* Order in which code lengths are stored, as per the zlib specification */
+ int i;
+
+ if (nReadSymbols < 0 || nReadSymbols > MAX_SYMBOLS || nSymbols < 0 || nSymbols > MAX_SYMBOLS || nReadSymbols > nSymbols)
+ return -1;
+
+ i = 0;
+ while (i < nReadSymbols) {
+ unsigned int nLength = em_lsb_bitreader_get_bits(pBitReader, nLenBits);
+ if (nLength == -1) return -1;
+
+ pCodeLength[nCodeLenSymIndex[i++]] = nLength;
+ }
+
+ while (i < nSymbols) {
+ pCodeLength[nCodeLenSymIndex[i++]] = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Read huffman-encoded code lengths
+ *
+ * @param pTablesDecoder decoding context for code lengths
+ * @param pTablesRevSymbolTable reverse lookup table for code lengths
+ * @param nReadSymbols number of symbols actually read
+ * @param nSymbols number of symbols to build codes for
+ * @param pCodeLength output code lengths table
+ * @param pBitReader bit reader context
+ *
+ * @return 0 for success, -1 for failure
+ */
+static int em_lsb_huffman_decoder_read_var_lengths(em_lsb_huffman_decoder_t *pTablesDecoder, const unsigned int *pTablesRevSymbolTable,
+ const int nReadSymbols, const int nSymbols, unsigned char *pCodeLength, em_lsb_bitreader_t *pBitReader) {
+ int i;
+
+ if (nReadSymbols < 0 || nSymbols < 0 || nReadSymbols > nSymbols)
+ return -1;
+
+ i = 0;
+ unsigned int nPrevLength = 0;
+
+ while (i < nReadSymbols) {
+ unsigned int nLength = em_lsb_huffman_decoder_read_value(pTablesDecoder, pTablesRevSymbolTable, pBitReader);
+ if (nLength == -1) return -1;
+
+ if (nLength < 16) {
+ nPrevLength = nLength;
+ pCodeLength[i++] = nPrevLength;
+ }
+ else {
+ unsigned int nRunLen = 0;
+
+ if (nLength == 16) {
+ int nExtraRunLen = em_lsb_bitreader_get_bits(pBitReader, 2);
+ if (nExtraRunLen == -1) return -1;
+ nRunLen = 3 + nExtraRunLen;
+ }
+ else if (nLength == 17) {
+ int nExtraRunLen = em_lsb_bitreader_get_bits(pBitReader, 3);
+ if (nExtraRunLen == -1) return -1;
+ nPrevLength = 0;
+ nRunLen = 3 + nExtraRunLen;
+ }
+ else if (nLength == 18) {
+ int nExtraRunLen = em_lsb_bitreader_get_bits(pBitReader, 7);
+ if (nExtraRunLen == -1) return -1;
+ nPrevLength = 0;
+ nRunLen = 11 + nExtraRunLen;
+ }
+
+ while (nRunLen && i < nReadSymbols) {
+ pCodeLength[i++] = nPrevLength;
+ nRunLen--;
+ }
+ }
+ }
+
+ while (i < nSymbols)
+ pCodeLength[i++] = 0;
+ return 0;
+}
+
+/*-- zlib stored blocks copier --*/
+
+static size_t em_inflate_copy_stored(em_lsb_bitreader_t *pBitReader, unsigned char *pOutData, size_t nOutDataOffset, size_t nBlockMaxSize) {
+ /* Align on byte */
+ if (em_lsb_bitreader_byte_align(pBitReader) < 0)
+ return -1;
+
+ if ((pBitReader->pInBlock + 4) > pBitReader->pInBlockEnd) return -1;
+
+ /* Read block length, and its two's complement verification value */
+ unsigned short nStoredLen = ((unsigned short)pBitReader->pInBlock[0]) | (((unsigned short)pBitReader->pInBlock[1]) << 8);
+ pBitReader->pInBlock += 2;
+
+ unsigned short nNegStoredLen = ((unsigned short)pBitReader->pInBlock[0]) | (((unsigned short)pBitReader->pInBlock[1]) << 8);
+ pBitReader->pInBlock += 2;
+
+ /* Make sure that the len and the two's complement match */
+ if (nStoredLen != ((~nNegStoredLen) & 0xffff)) return -1;
+
+ /* Make sure there is room */
+ if (nStoredLen > nBlockMaxSize) return -1;
+
+ /* Copy stored data */
+ memcpy(pOutData + nOutDataOffset, pBitReader->pInBlock, nStoredLen);
+ pBitReader->pInBlock += nStoredLen;
+
+ return (size_t)nStoredLen;
+}
+
+/*-- zlib static and dynamic blocks inflater --*/
+
+#define NCODELENBITS 3
+#define NLITERALSYMS 288
+#define NEODMARKERSYM 256
+#define NMATCHLENSYMSTART 257
+#define NMATCHLENSYMS 29
+#define NOFFSETSYMS 32
+#define MIN_MATCH_SIZE 3
+#define MATCHLEN_PAIR(__base,__dispbits) ((__base) | ((__dispbits) << 16) | 0x8000)
+#define OFFSET_PAIR(__base,__dispbits) ((__base) | ((__dispbits) << 16))
+
+/** Base value and number of extra displacement bits for each match length codeword */
+static const unsigned int em_inflate_matchlen_code[NMATCHLENSYMS] = {
+ MATCHLEN_PAIR(MIN_MATCH_SIZE + 0, 0), MATCHLEN_PAIR(MIN_MATCH_SIZE + 1, 0), MATCHLEN_PAIR(MIN_MATCH_SIZE + 2, 0), MATCHLEN_PAIR(MIN_MATCH_SIZE + 3, 0), MATCHLEN_PAIR(MIN_MATCH_SIZE + 4, 0),
+ MATCHLEN_PAIR(MIN_MATCH_SIZE + 5, 0), MATCHLEN_PAIR(MIN_MATCH_SIZE + 6, 0), MATCHLEN_PAIR(MIN_MATCH_SIZE + 7, 0), MATCHLEN_PAIR(MIN_MATCH_SIZE + 8, 1), MATCHLEN_PAIR(MIN_MATCH_SIZE + 10, 1),
+ MATCHLEN_PAIR(MIN_MATCH_SIZE + 12, 1), MATCHLEN_PAIR(MIN_MATCH_SIZE + 14, 1), MATCHLEN_PAIR(MIN_MATCH_SIZE + 16, 2), MATCHLEN_PAIR(MIN_MATCH_SIZE + 20, 2), MATCHLEN_PAIR(MIN_MATCH_SIZE + 24, 2),
+ MATCHLEN_PAIR(MIN_MATCH_SIZE + 28, 2), MATCHLEN_PAIR(MIN_MATCH_SIZE + 32, 3), MATCHLEN_PAIR(MIN_MATCH_SIZE + 40, 3), MATCHLEN_PAIR(MIN_MATCH_SIZE + 48, 3), MATCHLEN_PAIR(MIN_MATCH_SIZE + 56, 3),
+ MATCHLEN_PAIR(MIN_MATCH_SIZE + 64, 4), MATCHLEN_PAIR(MIN_MATCH_SIZE + 80, 4), MATCHLEN_PAIR(MIN_MATCH_SIZE + 96, 4), MATCHLEN_PAIR(MIN_MATCH_SIZE + 112, 4), MATCHLEN_PAIR(MIN_MATCH_SIZE + 128, 5),
+ MATCHLEN_PAIR(MIN_MATCH_SIZE + 160, 5), MATCHLEN_PAIR(MIN_MATCH_SIZE + 192, 5), MATCHLEN_PAIR(MIN_MATCH_SIZE + 224, 5), MATCHLEN_PAIR(MIN_MATCH_SIZE + 255, 0),
+};
+
+/** Base value and number of extra displacement bits for each offset codeword */
+static const unsigned int em_inflate_offset_code[NOFFSETSYMS] = {
+ OFFSET_PAIR(1, 0), OFFSET_PAIR(2, 0), OFFSET_PAIR(3, 0), OFFSET_PAIR(4, 0), OFFSET_PAIR(5, 1), OFFSET_PAIR(7, 1), OFFSET_PAIR(9, 2), OFFSET_PAIR(13, 2), OFFSET_PAIR(17, 3), OFFSET_PAIR(25, 3),
+ OFFSET_PAIR(33, 4), OFFSET_PAIR(49, 4), OFFSET_PAIR(65, 5), OFFSET_PAIR(97, 5), OFFSET_PAIR(129, 6), OFFSET_PAIR(193, 6), OFFSET_PAIR(257, 7), OFFSET_PAIR(385, 7), OFFSET_PAIR(513, 8), OFFSET_PAIR(769, 8),
+ OFFSET_PAIR(1025, 9), OFFSET_PAIR(1537, 9), OFFSET_PAIR(2049, 10), OFFSET_PAIR(3073, 10), OFFSET_PAIR(4097, 11), OFFSET_PAIR(6145, 11), OFFSET_PAIR(8193, 12), OFFSET_PAIR(12289, 12), OFFSET_PAIR(16385, 13), OFFSET_PAIR(24577, 13),
+};
+
+/**
+ * Decompress one data block
+ *
+ * @param pInBlock pointer to compressed data
+ * @param nDynamicBlock non-zero for a dynamic huffman tables block, zero for a static huffman tables block
+ * @param nInBlockSize size of compressed data, in bytes
+ * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block)
+ * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes)
+ * @param nBlockMaxSize total size of output decompression buffer, in bytes
+ *
+ * @return size of decompressed data in bytes, or -1 for error
+ */
+static size_t em_inflate_decompress_block(em_lsb_bitreader_t *pBitReader, int nDynamicBlock, unsigned char *pOutData, size_t nOutDataOffset, size_t nBlockMaxSize) {
+ em_lsb_huffman_decoder_t literalsDecoder;
+ em_lsb_huffman_decoder_t offsetDecoder;
+ unsigned int nLiteralsRevSymbolTable[NLITERALSYMS * 2];
+ unsigned int nOffsetRevSymbolTable[NOFFSETSYMS * 2];
+ int i;
+
+ if (nDynamicBlock) {
+ em_lsb_huffman_decoder_t tablesDecoder;
+ unsigned char nCodeLength[NLITERALSYMS + NOFFSETSYMS];
+ unsigned int nTablesRevSymbolTable[NCODELENSYMS * 2];
+
+ /* Read the encoded number of literals/match len symbols, offset symbols, and code length symbols */
+ unsigned int nLiteralSyms = em_lsb_bitreader_get_bits(pBitReader, 5);
+ if (nLiteralSyms == -1) return -1;
+ nLiteralSyms += 257;
+ if (nLiteralSyms > NLITERALSYMS) return -1;
+
+ unsigned int nOffsetSyms = em_lsb_bitreader_get_bits(pBitReader, 5);
+ if (nOffsetSyms == -1) return -1;
+ nOffsetSyms += 1;
+ if (nOffsetSyms > NOFFSETSYMS) return -1;
+
+ unsigned int nCodeLenSyms = em_lsb_bitreader_get_bits(pBitReader, 4);
+ if (nCodeLenSyms == -1) return -1;
+ nCodeLenSyms += 4;
+ if (nCodeLenSyms > NCODELENSYMS) return -1;
+
+ /* Read code lengths table */
+ if (em_lsb_huffman_decoder_read_raw_lengths(NCODELENBITS /* code length bits */, nCodeLenSyms /* symbols */, NCODELENSYMS, nCodeLength, pBitReader) < 0)
+ return -1;
+ if (em_lsb_huffman_decoder_prepare_table(&tablesDecoder, nTablesRevSymbolTable, NCODELENSYMS, NCODELENSYMS, nCodeLength) < 0)
+ return -1;
+ if (em_lsb_huffman_decoder_finalize_table(&tablesDecoder, nTablesRevSymbolTable) < 0)
+ return -1;
+
+ /* Use code lengths table to read literals/match len and offset tables */
+ if (em_lsb_huffman_decoder_read_var_lengths(&tablesDecoder, nTablesRevSymbolTable, nLiteralSyms + nOffsetSyms /* read symbols */, NLITERALSYMS + NOFFSETSYMS /* total symbols */, nCodeLength, pBitReader) < 0)
+ return -1;
+ if (em_lsb_huffman_decoder_prepare_table(&literalsDecoder, nLiteralsRevSymbolTable, nLiteralSyms, NLITERALSYMS, nCodeLength) < 0)
+ return -1;
+ if (em_lsb_huffman_decoder_prepare_table(&offsetDecoder, nOffsetRevSymbolTable, nOffsetSyms, NOFFSETSYMS, nCodeLength + nLiteralSyms) < 0)
+ return -1;
+ }
+ else {
+ unsigned char nFixedLiteralCodeLen[NLITERALSYMS];
+ unsigned char nFixedOffsetCodeLen[NOFFSETSYMS];
+
+ /* Fixed huffman codes for this block. Build fixed code lengths for literals/match lens and offsets, as per the zlib specification */
+
+ for (i = 0; i < 144; i++)
+ nFixedLiteralCodeLen[i] = 8;
+ for (; i < 256; i++)
+ nFixedLiteralCodeLen[i] = 9;
+ for (; i < 280; i++)
+ nFixedLiteralCodeLen[i] = 7;
+ for (; i < NLITERALSYMS; i++)
+ nFixedLiteralCodeLen[i] = 8;
+
+ for (i = 0; i < NOFFSETSYMS; i++)
+ nFixedOffsetCodeLen[i] = 5;
+
+ if (em_lsb_huffman_decoder_prepare_table(&literalsDecoder, nLiteralsRevSymbolTable, NLITERALSYMS, NLITERALSYMS, nFixedLiteralCodeLen) < 0)
+ return -1;
+ if (em_lsb_huffman_decoder_prepare_table(&offsetDecoder, nOffsetRevSymbolTable, NOFFSETSYMS, NOFFSETSYMS, nFixedOffsetCodeLen) < 0)
+ return -1;
+ }
+
+ /* Replace codewords in the offsets table by the base + extra bit count, save table lookups when decoding */
+ for (i = 0; i < NOFFSETSYMS; i++) {
+ unsigned int n = nOffsetRevSymbolTable[i];
+ if (n < NOFFSETSYMS) {
+ nOffsetRevSymbolTable[i] = em_inflate_offset_code[n];
+ }
+ }
+
+ /* Replace match codewords in the literals/match len table by the base + extra bit count, save table lookups when decoding */
+ for (i = 0; i < NLITERALSYMS; i++) {
+ unsigned int n = nLiteralsRevSymbolTable[i];
+ if (n >= NMATCHLENSYMSTART && n < NLITERALSYMS) {
+ int index = n - NMATCHLENSYMSTART;
+ if (index >= 0 && index < NMATCHLENSYMS) {
+ nLiteralsRevSymbolTable[i] = em_inflate_matchlen_code[index];
+ }
+ else {
+ /* Handle 286 and 287 codes, used for constructing a complete canonical tree, present in static huffman tables but not used */
+ nLiteralsRevSymbolTable[i] = 0;
+ }
+ }
+ }
+
+ /* Finalize huffman tables for decoding, after setting up the bases + extra bits as the symbols */
+ if (em_lsb_huffman_decoder_finalize_table(&literalsDecoder, nLiteralsRevSymbolTable) < 0)
+ return -1;
+ if (em_lsb_huffman_decoder_finalize_table(&offsetDecoder, nOffsetRevSymbolTable) < 0)
+ return -1;
+
+ /* Finally, loop to read all the literals/match len codewords in the block to decompress it */
+
+ unsigned char *pCurOutData = pOutData + nOutDataOffset;
+ const unsigned char *pOutDataEnd = pCurOutData + nBlockMaxSize;
+ const unsigned char *pOutDataFastEnd = pOutDataEnd - 15;
+
+ while (1) {
+ em_lsb_bitreader_refill_32(pBitReader);
+
+ unsigned int nLiteralsCodeword = em_lsb_huffman_decoder_read_value(&literalsDecoder, nLiteralsRevSymbolTable, pBitReader);
+ if (nLiteralsCodeword < 256) {
+ /* 0..255: literal. copy. */
+ if (pCurOutData < pOutDataEnd)
+ *pCurOutData++ = nLiteralsCodeword;
+ else
+ return -1;
+ }
+ else {
+ if (nLiteralsCodeword == NEODMARKERSYM) break; /* EOD marker, all done */
+ if (nLiteralsCodeword == -1) return -1;
+
+ /* 256..284: match. decode match length. */
+
+ unsigned int nMatchLen = em_lsb_bitreader_get_bits(pBitReader, (nLiteralsCodeword >> 16) & 15);
+ if (nMatchLen == -1) return -1;
+
+ nMatchLen += (nLiteralsCodeword & 0x7fff);
+
+ /* Read match offset */
+
+ unsigned int nOffsetCodeword = em_lsb_huffman_decoder_read_value(&offsetDecoder, nOffsetRevSymbolTable, pBitReader);
+ if (nOffsetCodeword == -1) return -1;
+
+ unsigned int nMatchOffset = em_lsb_bitreader_get_bits(pBitReader, (nOffsetCodeword >> 16) & 15);
+ if (nMatchOffset == -1) return -1;
+
+ nMatchOffset += (nOffsetCodeword & 0x7fff);
+
+ /* Copy match */
+
+ const unsigned char *pSrc = pCurOutData - nMatchOffset;
+ if (pSrc >= pOutData) {
+ if (nMatchOffset >= 16 && (pCurOutData + nMatchLen) <= pOutDataFastEnd) {
+ const unsigned char *pCopySrc = pSrc;
+ unsigned char *pCopyDst = pCurOutData;
+ const unsigned char *pCopyEndDst = pCurOutData + nMatchLen;
+
+ do {
+ memcpy(pCopyDst, pCopySrc, 16);
+ pCopySrc += 16;
+ pCopyDst += 16;
+ } while (pCopyDst < pCopyEndDst);
+
+ pCurOutData += nMatchLen;
+ }
+ else {
+ if ((pCurOutData + nMatchLen) > pOutDataEnd) return -1;
+
+ while (nMatchLen--) {
+ *pCurOutData++ = *pSrc++;
+ }
+ }
+ }
+ else
+ return -1;
+ }
+ }
+
+ return (size_t)(pCurOutData - (pOutData + nOutDataOffset));
+}
+
+/*-- zlib adler32 calculation --*/
+
+#ifdef EM_INFLATE_VERIFY_CHECKSUM
+
+/*
+ By Mark Adler - https://github.com/madler/zlib/blob/master/adler32.c
+
+ Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+ */
+
+#define BASE 65521U /* largest prime smaller than 65536 */
+#define NMAX 5552
+#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+# define MOD(a) a %= BASE
+# define MOD28(a) a %= BASE
+# define MOD63(a) a %= BASE
+
+static unsigned int em_inflate_adler32_z(unsigned int adler, const unsigned char *buf, size_t len) {
+ unsigned long sum2;
+ unsigned n;
+
+ /* split Adler-32 into component sums */
+ sum2 = (adler >> 16) & 0xffff;
+ adler &= 0xffff;
+
+ /* in case user likes doing a byte at a time, keep it fast */
+ if (len == 1) {
+ adler += buf[0];
+ if (adler >= BASE)
+ adler -= BASE;
+ sum2 += adler;
+ if (sum2 >= BASE)
+ sum2 -= BASE;
+ return adler | (sum2 << 16);
+ }
+
+ /* initial Adler-32 value (deferred check for len == 1 speed) */
+ if (buf == NULL)
+ return 1L;
+
+ /* in case short lengths are provided, keep it somewhat fast */
+ if (len < 16) {
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ if (adler >= BASE)
+ adler -= BASE;
+ MOD28(sum2); /* only added so many BASE's */
+ return adler | (sum2 << 16);
+ }
+
+ /* do length NMAX blocks -- requires just one modulo operation */
+ while (len >= NMAX) {
+ len -= NMAX;
+ n = NMAX / 16; /* NMAX is divisible by 16 */
+ do {
+ DO16(buf); /* 16 sums unrolled */
+ buf += 16;
+ } while (--n);
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* do remaining bytes (less than NMAX, still just one modulo) */
+ if (len) { /* avoid modulos if none remaining */
+ while (len >= 16) {
+ len -= 16;
+ DO16(buf);
+ buf += 16;
+ }
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* return recombined sums */
+ return adler | (sum2 << 16);
+}
+
+/*-- gzip crc32 calculation --*/
+
+/**
+By Stephen Brumme - https://create.stephan-brumme.com/crc32/
+
+Unless otherwise noted in a file's first 5 lines, all source code published on http://create.stephan-brumme.com and its sub-pages is licensed similar to the zlib license:
+
+This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software.
+Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+ */
+
+static const unsigned int ema_inflate_Crc32Lookup[4][256] = {
+ {
+ 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,
+ 0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
+ 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,
+ 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
+ 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,
+ 0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
+ 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,
+ 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
+ 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,
+ 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
+ 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,
+ 0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
+ 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,
+ 0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
+ 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,
+ 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
+ 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,
+ 0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
+ 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,
+ 0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
+ 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,
+ 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
+ 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,
+ 0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
+ 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,
+ 0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
+ 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,
+ 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
+ 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,
+ 0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
+ 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,
+ 0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
+ }
+
+ // beyond this point only relevant for Slicing-by-4, Slicing-by-8 and Slicing-by-16
+ ,{
+ 0x00000000,0x191B3141,0x32366282,0x2B2D53C3,0x646CC504,0x7D77F445,0x565AA786,0x4F4196C7,
+ 0xC8D98A08,0xD1C2BB49,0xFAEFE88A,0xE3F4D9CB,0xACB54F0C,0xB5AE7E4D,0x9E832D8E,0x87981CCF,
+ 0x4AC21251,0x53D92310,0x78F470D3,0x61EF4192,0x2EAED755,0x37B5E614,0x1C98B5D7,0x05838496,
+ 0x821B9859,0x9B00A918,0xB02DFADB,0xA936CB9A,0xE6775D5D,0xFF6C6C1C,0xD4413FDF,0xCD5A0E9E,
+ 0x958424A2,0x8C9F15E3,0xA7B24620,0xBEA97761,0xF1E8E1A6,0xE8F3D0E7,0xC3DE8324,0xDAC5B265,
+ 0x5D5DAEAA,0x44469FEB,0x6F6BCC28,0x7670FD69,0x39316BAE,0x202A5AEF,0x0B07092C,0x121C386D,
+ 0xDF4636F3,0xC65D07B2,0xED705471,0xF46B6530,0xBB2AF3F7,0xA231C2B6,0x891C9175,0x9007A034,
+ 0x179FBCFB,0x0E848DBA,0x25A9DE79,0x3CB2EF38,0x73F379FF,0x6AE848BE,0x41C51B7D,0x58DE2A3C,
+ 0xF0794F05,0xE9627E44,0xC24F2D87,0xDB541CC6,0x94158A01,0x8D0EBB40,0xA623E883,0xBF38D9C2,
+ 0x38A0C50D,0x21BBF44C,0x0A96A78F,0x138D96CE,0x5CCC0009,0x45D73148,0x6EFA628B,0x77E153CA,
+ 0xBABB5D54,0xA3A06C15,0x888D3FD6,0x91960E97,0xDED79850,0xC7CCA911,0xECE1FAD2,0xF5FACB93,
+ 0x7262D75C,0x6B79E61D,0x4054B5DE,0x594F849F,0x160E1258,0x0F152319,0x243870DA,0x3D23419B,
+ 0x65FD6BA7,0x7CE65AE6,0x57CB0925,0x4ED03864,0x0191AEA3,0x188A9FE2,0x33A7CC21,0x2ABCFD60,
+ 0xAD24E1AF,0xB43FD0EE,0x9F12832D,0x8609B26C,0xC94824AB,0xD05315EA,0xFB7E4629,0xE2657768,
+ 0x2F3F79F6,0x362448B7,0x1D091B74,0x04122A35,0x4B53BCF2,0x52488DB3,0x7965DE70,0x607EEF31,
+ 0xE7E6F3FE,0xFEFDC2BF,0xD5D0917C,0xCCCBA03D,0x838A36FA,0x9A9107BB,0xB1BC5478,0xA8A76539,
+ 0x3B83984B,0x2298A90A,0x09B5FAC9,0x10AECB88,0x5FEF5D4F,0x46F46C0E,0x6DD93FCD,0x74C20E8C,
+ 0xF35A1243,0xEA412302,0xC16C70C1,0xD8774180,0x9736D747,0x8E2DE606,0xA500B5C5,0xBC1B8484,
+ 0x71418A1A,0x685ABB5B,0x4377E898,0x5A6CD9D9,0x152D4F1E,0x0C367E5F,0x271B2D9C,0x3E001CDD,
+ 0xB9980012,0xA0833153,0x8BAE6290,0x92B553D1,0xDDF4C516,0xC4EFF457,0xEFC2A794,0xF6D996D5,
+ 0xAE07BCE9,0xB71C8DA8,0x9C31DE6B,0x852AEF2A,0xCA6B79ED,0xD37048AC,0xF85D1B6F,0xE1462A2E,
+ 0x66DE36E1,0x7FC507A0,0x54E85463,0x4DF36522,0x02B2F3E5,0x1BA9C2A4,0x30849167,0x299FA026,
+ 0xE4C5AEB8,0xFDDE9FF9,0xD6F3CC3A,0xCFE8FD7B,0x80A96BBC,0x99B25AFD,0xB29F093E,0xAB84387F,
+ 0x2C1C24B0,0x350715F1,0x1E2A4632,0x07317773,0x4870E1B4,0x516BD0F5,0x7A468336,0x635DB277,
+ 0xCBFAD74E,0xD2E1E60F,0xF9CCB5CC,0xE0D7848D,0xAF96124A,0xB68D230B,0x9DA070C8,0x84BB4189,
+ 0x03235D46,0x1A386C07,0x31153FC4,0x280E0E85,0x674F9842,0x7E54A903,0x5579FAC0,0x4C62CB81,
+ 0x8138C51F,0x9823F45E,0xB30EA79D,0xAA1596DC,0xE554001B,0xFC4F315A,0xD7626299,0xCE7953D8,
+ 0x49E14F17,0x50FA7E56,0x7BD72D95,0x62CC1CD4,0x2D8D8A13,0x3496BB52,0x1FBBE891,0x06A0D9D0,
+ 0x5E7EF3EC,0x4765C2AD,0x6C48916E,0x7553A02F,0x3A1236E8,0x230907A9,0x0824546A,0x113F652B,
+ 0x96A779E4,0x8FBC48A5,0xA4911B66,0xBD8A2A27,0xF2CBBCE0,0xEBD08DA1,0xC0FDDE62,0xD9E6EF23,
+ 0x14BCE1BD,0x0DA7D0FC,0x268A833F,0x3F91B27E,0x70D024B9,0x69CB15F8,0x42E6463B,0x5BFD777A,
+ 0xDC656BB5,0xC57E5AF4,0xEE530937,0xF7483876,0xB809AEB1,0xA1129FF0,0x8A3FCC33,0x9324FD72,
+ },
+
+ {
+ 0x00000000,0x01C26A37,0x0384D46E,0x0246BE59,0x0709A8DC,0x06CBC2EB,0x048D7CB2,0x054F1685,
+ 0x0E1351B8,0x0FD13B8F,0x0D9785D6,0x0C55EFE1,0x091AF964,0x08D89353,0x0A9E2D0A,0x0B5C473D,
+ 0x1C26A370,0x1DE4C947,0x1FA2771E,0x1E601D29,0x1B2F0BAC,0x1AED619B,0x18ABDFC2,0x1969B5F5,
+ 0x1235F2C8,0x13F798FF,0x11B126A6,0x10734C91,0x153C5A14,0x14FE3023,0x16B88E7A,0x177AE44D,
+ 0x384D46E0,0x398F2CD7,0x3BC9928E,0x3A0BF8B9,0x3F44EE3C,0x3E86840B,0x3CC03A52,0x3D025065,
+ 0x365E1758,0x379C7D6F,0x35DAC336,0x3418A901,0x3157BF84,0x3095D5B3,0x32D36BEA,0x331101DD,
+ 0x246BE590,0x25A98FA7,0x27EF31FE,0x262D5BC9,0x23624D4C,0x22A0277B,0x20E69922,0x2124F315,
+ 0x2A78B428,0x2BBADE1F,0x29FC6046,0x283E0A71,0x2D711CF4,0x2CB376C3,0x2EF5C89A,0x2F37A2AD,
+ 0x709A8DC0,0x7158E7F7,0x731E59AE,0x72DC3399,0x7793251C,0x76514F2B,0x7417F172,0x75D59B45,
+ 0x7E89DC78,0x7F4BB64F,0x7D0D0816,0x7CCF6221,0x798074A4,0x78421E93,0x7A04A0CA,0x7BC6CAFD,
+ 0x6CBC2EB0,0x6D7E4487,0x6F38FADE,0x6EFA90E9,0x6BB5866C,0x6A77EC5B,0x68315202,0x69F33835,
+ 0x62AF7F08,0x636D153F,0x612BAB66,0x60E9C151,0x65A6D7D4,0x6464BDE3,0x662203BA,0x67E0698D,
+ 0x48D7CB20,0x4915A117,0x4B531F4E,0x4A917579,0x4FDE63FC,0x4E1C09CB,0x4C5AB792,0x4D98DDA5,
+ 0x46C49A98,0x4706F0AF,0x45404EF6,0x448224C1,0x41CD3244,0x400F5873,0x4249E62A,0x438B8C1D,
+ 0x54F16850,0x55330267,0x5775BC3E,0x56B7D609,0x53F8C08C,0x523AAABB,0x507C14E2,0x51BE7ED5,
+ 0x5AE239E8,0x5B2053DF,0x5966ED86,0x58A487B1,0x5DEB9134,0x5C29FB03,0x5E6F455A,0x5FAD2F6D,
+ 0xE1351B80,0xE0F771B7,0xE2B1CFEE,0xE373A5D9,0xE63CB35C,0xE7FED96B,0xE5B86732,0xE47A0D05,
+ 0xEF264A38,0xEEE4200F,0xECA29E56,0xED60F461,0xE82FE2E4,0xE9ED88D3,0xEBAB368A,0xEA695CBD,
+ 0xFD13B8F0,0xFCD1D2C7,0xFE976C9E,0xFF5506A9,0xFA1A102C,0xFBD87A1B,0xF99EC442,0xF85CAE75,
+ 0xF300E948,0xF2C2837F,0xF0843D26,0xF1465711,0xF4094194,0xF5CB2BA3,0xF78D95FA,0xF64FFFCD,
+ 0xD9785D60,0xD8BA3757,0xDAFC890E,0xDB3EE339,0xDE71F5BC,0xDFB39F8B,0xDDF521D2,0xDC374BE5,
+ 0xD76B0CD8,0xD6A966EF,0xD4EFD8B6,0xD52DB281,0xD062A404,0xD1A0CE33,0xD3E6706A,0xD2241A5D,
+ 0xC55EFE10,0xC49C9427,0xC6DA2A7E,0xC7184049,0xC25756CC,0xC3953CFB,0xC1D382A2,0xC011E895,
+ 0xCB4DAFA8,0xCA8FC59F,0xC8C97BC6,0xC90B11F1,0xCC440774,0xCD866D43,0xCFC0D31A,0xCE02B92D,
+ 0x91AF9640,0x906DFC77,0x922B422E,0x93E92819,0x96A63E9C,0x976454AB,0x9522EAF2,0x94E080C5,
+ 0x9FBCC7F8,0x9E7EADCF,0x9C381396,0x9DFA79A1,0x98B56F24,0x99770513,0x9B31BB4A,0x9AF3D17D,
+ 0x8D893530,0x8C4B5F07,0x8E0DE15E,0x8FCF8B69,0x8A809DEC,0x8B42F7DB,0x89044982,0x88C623B5,
+ 0x839A6488,0x82580EBF,0x801EB0E6,0x81DCDAD1,0x8493CC54,0x8551A663,0x8717183A,0x86D5720D,
+ 0xA9E2D0A0,0xA820BA97,0xAA6604CE,0xABA46EF9,0xAEEB787C,0xAF29124B,0xAD6FAC12,0xACADC625,
+ 0xA7F18118,0xA633EB2F,0xA4755576,0xA5B73F41,0xA0F829C4,0xA13A43F3,0xA37CFDAA,0xA2BE979D,
+ 0xB5C473D0,0xB40619E7,0xB640A7BE,0xB782CD89,0xB2CDDB0C,0xB30FB13B,0xB1490F62,0xB08B6555,
+ 0xBBD72268,0xBA15485F,0xB853F606,0xB9919C31,0xBCDE8AB4,0xBD1CE083,0xBF5A5EDA,0xBE9834ED,
+ },
+
+ {
+ 0x00000000,0xB8BC6765,0xAA09C88B,0x12B5AFEE,0x8F629757,0x37DEF032,0x256B5FDC,0x9DD738B9,
+ 0xC5B428EF,0x7D084F8A,0x6FBDE064,0xD7018701,0x4AD6BFB8,0xF26AD8DD,0xE0DF7733,0x58631056,
+ 0x5019579F,0xE8A530FA,0xFA109F14,0x42ACF871,0xDF7BC0C8,0x67C7A7AD,0x75720843,0xCDCE6F26,
+ 0x95AD7F70,0x2D111815,0x3FA4B7FB,0x8718D09E,0x1ACFE827,0xA2738F42,0xB0C620AC,0x087A47C9,
+ 0xA032AF3E,0x188EC85B,0x0A3B67B5,0xB28700D0,0x2F503869,0x97EC5F0C,0x8559F0E2,0x3DE59787,
+ 0x658687D1,0xDD3AE0B4,0xCF8F4F5A,0x7733283F,0xEAE41086,0x525877E3,0x40EDD80D,0xF851BF68,
+ 0xF02BF8A1,0x48979FC4,0x5A22302A,0xE29E574F,0x7F496FF6,0xC7F50893,0xD540A77D,0x6DFCC018,
+ 0x359FD04E,0x8D23B72B,0x9F9618C5,0x272A7FA0,0xBAFD4719,0x0241207C,0x10F48F92,0xA848E8F7,
+ 0x9B14583D,0x23A83F58,0x311D90B6,0x89A1F7D3,0x1476CF6A,0xACCAA80F,0xBE7F07E1,0x06C36084,
+ 0x5EA070D2,0xE61C17B7,0xF4A9B859,0x4C15DF3C,0xD1C2E785,0x697E80E0,0x7BCB2F0E,0xC377486B,
+ 0xCB0D0FA2,0x73B168C7,0x6104C729,0xD9B8A04C,0x446F98F5,0xFCD3FF90,0xEE66507E,0x56DA371B,
+ 0x0EB9274D,0xB6054028,0xA4B0EFC6,0x1C0C88A3,0x81DBB01A,0x3967D77F,0x2BD27891,0x936E1FF4,
+ 0x3B26F703,0x839A9066,0x912F3F88,0x299358ED,0xB4446054,0x0CF80731,0x1E4DA8DF,0xA6F1CFBA,
+ 0xFE92DFEC,0x462EB889,0x549B1767,0xEC277002,0x71F048BB,0xC94C2FDE,0xDBF98030,0x6345E755,
+ 0x6B3FA09C,0xD383C7F9,0xC1366817,0x798A0F72,0xE45D37CB,0x5CE150AE,0x4E54FF40,0xF6E89825,
+ 0xAE8B8873,0x1637EF16,0x048240F8,0xBC3E279D,0x21E91F24,0x99557841,0x8BE0D7AF,0x335CB0CA,
+ 0xED59B63B,0x55E5D15E,0x47507EB0,0xFFEC19D5,0x623B216C,0xDA874609,0xC832E9E7,0x708E8E82,
+ 0x28ED9ED4,0x9051F9B1,0x82E4565F,0x3A58313A,0xA78F0983,0x1F336EE6,0x0D86C108,0xB53AA66D,
+ 0xBD40E1A4,0x05FC86C1,0x1749292F,0xAFF54E4A,0x322276F3,0x8A9E1196,0x982BBE78,0x2097D91D,
+ 0x78F4C94B,0xC048AE2E,0xD2FD01C0,0x6A4166A5,0xF7965E1C,0x4F2A3979,0x5D9F9697,0xE523F1F2,
+ 0x4D6B1905,0xF5D77E60,0xE762D18E,0x5FDEB6EB,0xC2098E52,0x7AB5E937,0x680046D9,0xD0BC21BC,
+ 0x88DF31EA,0x3063568F,0x22D6F961,0x9A6A9E04,0x07BDA6BD,0xBF01C1D8,0xADB46E36,0x15080953,
+ 0x1D724E9A,0xA5CE29FF,0xB77B8611,0x0FC7E174,0x9210D9CD,0x2AACBEA8,0x38191146,0x80A57623,
+ 0xD8C66675,0x607A0110,0x72CFAEFE,0xCA73C99B,0x57A4F122,0xEF189647,0xFDAD39A9,0x45115ECC,
+ 0x764DEE06,0xCEF18963,0xDC44268D,0x64F841E8,0xF92F7951,0x41931E34,0x5326B1DA,0xEB9AD6BF,
+ 0xB3F9C6E9,0x0B45A18C,0x19F00E62,0xA14C6907,0x3C9B51BE,0x842736DB,0x96929935,0x2E2EFE50,
+ 0x2654B999,0x9EE8DEFC,0x8C5D7112,0x34E11677,0xA9362ECE,0x118A49AB,0x033FE645,0xBB838120,
+ 0xE3E09176,0x5B5CF613,0x49E959FD,0xF1553E98,0x6C820621,0xD43E6144,0xC68BCEAA,0x7E37A9CF,
+ 0xD67F4138,0x6EC3265D,0x7C7689B3,0xC4CAEED6,0x591DD66F,0xE1A1B10A,0xF3141EE4,0x4BA87981,
+ 0x13CB69D7,0xAB770EB2,0xB9C2A15C,0x017EC639,0x9CA9FE80,0x241599E5,0x36A0360B,0x8E1C516E,
+ 0x866616A7,0x3EDA71C2,0x2C6FDE2C,0x94D3B949,0x090481F0,0xB1B8E695,0xA30D497B,0x1BB12E1E,
+ 0x43D23E48,0xFB6E592D,0xE9DBF6C3,0x516791A6,0xCCB0A91F,0x740CCE7A,0x66B96194,0xDE0506F1,
+ }
+};
+
+// define endianess and some integer data types
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#elif defined(__APPLE__)
+#include <sys/param.h>
+#define __BYTE_ORDER __DARWIN_BYTE_ORDER
+#else
+ // defines __BYTE_ORDER as __LITTLE_ENDIAN or __BIG_ENDIAN
+#include <sys/param.h>
+#endif
+
+/// swap endianess
+static inline unsigned int em_inflate_crc32_swap(unsigned int x) {
+#if defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap32(x);
+#else
+ return (x >> 24) |
+ ((x >> 8) & 0x0000FF00) |
+ ((x << 8) & 0x00FF0000) |
+ (x << 24);
+#endif
+}
+
+static unsigned int em_inflate_crc32_4bytes(const void* data, size_t length, unsigned int previousCrc32) {
+ unsigned int crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF
+ const unsigned int* current = (const unsigned int*)data;
+
+ // process four bytes at once (Slicing-by-4)
+ while (length >= 4)
+ {
+#if __BYTE_ORDER == __BIG_ENDIAN
+ unsigned int one = *current++ ^ em_inflate_crc32_swap(crc);
+ crc = ema_inflate_Crc32Lookup[0][one & 0xFF] ^
+ ema_inflate_Crc32Lookup[1][(one >> 8) & 0xFF] ^
+ ema_inflate_Crc32Lookup[2][(one >> 16) & 0xFF] ^
+ ema_inflate_Crc32Lookup[3][(one >> 24) & 0xFF];
+#else
+ unsigned int one = *current++ ^ crc;
+ crc = ema_inflate_Crc32Lookup[0][(one >> 24) & 0xFF] ^
+ ema_inflate_Crc32Lookup[1][(one >> 16) & 0xFF] ^
+ ema_inflate_Crc32Lookup[2][(one >> 8) & 0xFF] ^
+ ema_inflate_Crc32Lookup[3][one & 0xFF];
+#endif
+
+ length -= 4;
+ }
+
+ const unsigned char* currentChar = (const unsigned char*)current;
+ // remaining 1 to 3 bytes (standard algorithm)
+ while (length-- != 0)
+ crc = (crc >> 8) ^ ema_inflate_Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++];
+
+ return ~crc; // same as crc ^ 0xFFFFFFFF
+}
+
+#endif /* EM_INFLATE_VERIFY_CHECKSUM */
+
+/*-- Top level zlib stream inflater --*/
+
+/* What type of checksum to verify, depending on the stream */
+typedef enum { EM_INFLATE_CHECKSUM_NONE = 0, EM_INFLATE_CHECKSUM_GZIP, EM_INFLATE_CHECKSUM_ZLIB } em_inflate_checksum_type_t;
+
+/**
+ * Inflate gzip or zlib data
+ *
+ * @param pCompressedData pointer to start of zlib data
+ * @param nCompressedDataSize size of zlib data, in bytes
+ * @param pOutData pointer to start of decompression buffer
+ * @param nMaxOutDataSize maximum size of decompression buffer, in bytes
+ *
+ * @return number of bytes decompressed, or -1 in case of an error
+ */
+size_t em_inflate(const void *pCompressedData, size_t nCompressedDataSize, unsigned char *pOutData, size_t nMaxOutDataSize) {
+ const unsigned char *pCurCompressedData = (const unsigned char *)pCompressedData;
+ const unsigned char *pEndCompressedData = pCurCompressedData + nCompressedDataSize;
+ em_lsb_bitreader_t bitReader;
+ unsigned int nIsFinalBlock;
+ size_t nCurOutOffset;
+ em_inflate_checksum_type_t nCheckSumType = EM_INFLATE_CHECKSUM_NONE;
+ unsigned long nCheckSum = 0;
+
+ if ((pCurCompressedData + 2) > pEndCompressedData) return -1;
+
+ /* Check header */
+ if (pCurCompressedData[0] == 0x1f && pCurCompressedData[1] == 0x8b) {
+ /* gzip wrapper */
+ pCurCompressedData += 2;
+ if ((pCurCompressedData + 8) > pEndCompressedData || pCurCompressedData[0] != 0x08 /* deflate */)
+ return -1;
+ pCurCompressedData++;
+
+ /* Read flags and skip over the rest of the header */
+ unsigned char flags = *pCurCompressedData++;
+ pCurCompressedData += 6;
+
+ if (flags & 0x02) { /* Part number present */
+ if ((pCurCompressedData + 2) > pEndCompressedData) return -1;
+ pCurCompressedData += 2;
+ }
+
+ if (flags & 0x04) { /* Extra field present, starts with two-byte length */
+ if ((pCurCompressedData + 2) > pEndCompressedData) return -1;
+ unsigned short nExtraFieldLen = ((unsigned short)pCurCompressedData[0]) | (((unsigned short)pCurCompressedData[1]) << 8);
+ pCurCompressedData += 2;
+
+ if ((pCurCompressedData + nExtraFieldLen) > pEndCompressedData) return -1;
+ pCurCompressedData += nExtraFieldLen;
+ }
+
+ if (flags & 0x08) { /* Original filename present, zero terminated */
+ do {
+ if (pCurCompressedData >= pEndCompressedData) return -1;
+ pCurCompressedData++;
+ } while (pCurCompressedData[-1]);
+ }
+
+ if (flags & 0x10) { /* File comment present, zero terminated */
+ do {
+ if (pCurCompressedData >= pEndCompressedData) return -1;
+ pCurCompressedData++;
+ } while (pCurCompressedData[-1]);
+ }
+
+ if (flags & 0x20) { /* Encryption header present */
+ return -1;
+ }
+
+ nCheckSumType = EM_INFLATE_CHECKSUM_GZIP;
+ }
+ else if ((pCurCompressedData[0] & 0x0f) == 0x08) {
+ /* zlib wrapper? */
+ unsigned char CMF = pCurCompressedData[0];
+ unsigned char FLG = pCurCompressedData[1];
+ unsigned short nCheck = FLG | (((unsigned short)CMF) << 8);
+
+ if ((CMF >> 4) <= 7 && (nCheck % 31) == 0) {
+ /* Looks like a valid zlib wrapper */
+ pCurCompressedData += 2;
+ if (FLG & 0x20) { /* Preset dictionary present */
+ if ((pCurCompressedData + 4) > pEndCompressedData) return -1;
+ pCurCompressedData += 4;
+ }
+ }
+
+ nCheckSumType = EM_INFLATE_CHECKSUM_ZLIB;
+ }
+
+#ifdef EM_INFLATE_VERIFY_CHECKSUM
+ /* Initialize checksum */
+ if (nCheckSumType == EM_INFLATE_CHECKSUM_ZLIB)
+ nCheckSum = em_inflate_adler32_z(0, NULL, 0);
+#endif /* EM_INFLATE_VERIFY_CHECKSUM */
+
+ /* The rest of the data is bit-packed. Initialize bit reader */
+ em_lsb_bitreader_init(&bitReader, pCurCompressedData, pEndCompressedData);
+
+ /* Process all the blocks */
+ nCurOutOffset = 0;
+
+ do {
+ unsigned int nBlockType;
+ size_t nBlockResult;
+
+ nIsFinalBlock = em_lsb_bitreader_get_bits(&bitReader, 1);
+ nBlockType = em_lsb_bitreader_get_bits(&bitReader, 2);
+
+ switch (nBlockType) {
+ case 0: /* Stored */
+ nBlockResult = em_inflate_copy_stored(&bitReader, pOutData, nCurOutOffset, nMaxOutDataSize - nCurOutOffset);
+ break;
+
+ case 1: /* Static huffman */
+ nBlockResult = em_inflate_decompress_block(&bitReader, 0 /* static */, pOutData, nCurOutOffset, nMaxOutDataSize - nCurOutOffset);
+ break;
+
+ case 2: /* Dynamic huffman */
+ nBlockResult = em_inflate_decompress_block(&bitReader, 1 /* dynamic */, pOutData, nCurOutOffset, nMaxOutDataSize - nCurOutOffset);
+ break;
+
+ case 3: /* Invalid */
+ return -1;
+ }
+
+ if (nBlockResult == -1) return -1;
+
+#ifdef EM_INFLATE_VERIFY_CHECKSUM
+ /* Update checksum with the decompressed block's contents */
+ if (nCheckSumType == EM_INFLATE_CHECKSUM_GZIP)
+ nCheckSum = em_inflate_crc32_4bytes(pOutData + nCurOutOffset, nBlockResult, nCheckSum);
+ else if (nCheckSumType == EM_INFLATE_CHECKSUM_ZLIB)
+ nCheckSum = em_inflate_adler32_z(nCheckSum, pOutData + nCurOutOffset, nBlockResult);
+#endif /* EM_INFLATE_VERIFY_CHECKSUM */
+
+ nCurOutOffset += nBlockResult;
+ } while (!nIsFinalBlock);
+
+ /* Align on a byte again */
+ em_lsb_bitreader_byte_align(&bitReader);
+ pCurCompressedData = bitReader.pInBlock;
+
+#ifdef EM_INFLATE_VERIFY_CHECKSUM
+ /* Read stored checksum and verify it */
+
+ unsigned int nStoredCheckSum;
+ switch (nCheckSumType) {
+ case EM_INFLATE_CHECKSUM_GZIP: /* gzip - little endian crc32 */
+ if ((pCurCompressedData + 4) > pEndCompressedData) return -1;
+ nStoredCheckSum = ((unsigned int)pCurCompressedData[0]);
+ nStoredCheckSum |= ((unsigned int)pCurCompressedData[1]) << 8;
+ nStoredCheckSum |= ((unsigned int)pCurCompressedData[2]) << 16;
+ nStoredCheckSum |= ((unsigned int)pCurCompressedData[3]) << 24;
+ if (nStoredCheckSum != nCheckSum) return -1;
+ pCurCompressedData += 4;
+ break;
+
+ case EM_INFLATE_CHECKSUM_ZLIB: /* zlib - big endian adler32 */
+ if ((pCurCompressedData + 4) > pEndCompressedData) return -1;
+ nStoredCheckSum = ((unsigned int)pCurCompressedData[0]) << 24;
+ nStoredCheckSum |= ((unsigned int)pCurCompressedData[1]) << 16;
+ nStoredCheckSum |= ((unsigned int)pCurCompressedData[2]) << 8;
+ nStoredCheckSum |= ((unsigned int)pCurCompressedData[3]);
+ if (nStoredCheckSum != nCheckSum) return -1;
+ pCurCompressedData += 4;
+ break;
+
+ default: /* None */
+ break;
+ }
+#endif /* EM_INFLATE_VERIFY_CHECKSUM */
+
+ /* Success, return decompressed size */
+ return nCurOutOffset;
+}
diff --git a/3rdparty/ymfm/examples/vgmrender/em_inflate.h b/3rdparty/ymfm/examples/vgmrender/em_inflate.h
new file mode 100644
index 00000000000..6cc1a9a78bb
--- /dev/null
+++ b/3rdparty/ymfm/examples/vgmrender/em_inflate.h
@@ -0,0 +1,49 @@
+/*
+ * em_inflate.h - fast in-memory inflate (gzip/zlib decompressor) definitions
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#ifndef _EM_INFLATE_H
+#define _EM_INFLATE_H
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Inflate gzip or zlib data
+ *
+ * @param pCompressedData pointer to start of zlib data
+ * @param nCompressedDataSize size of zlib data, in bytes
+ * @param pOutData pointer to start of decompression buffer
+ * @param nMaxOutDataSize maximum size of decompression buffer, in bytes
+ *
+ * @return number of bytes decompressed, or -1 in case of an error
+ */
+size_t em_inflate(const void *pCompressedData, size_t nCompressedDataSize, unsigned char *pOutData, size_t nMaxOutDataSize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EM_INFLATE_H */
diff --git a/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp b/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp
new file mode 100644
index 00000000000..14b5bda29d6
--- /dev/null
+++ b/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp
@@ -0,0 +1,1439 @@
+//
+// Simple vgm renderer.
+//
+// Leverages em_inflate tiny inflater from https://github.com/emmanuel-marty/em_inflate
+//
+// Compile with:
+//
+// g++ --std=c++14 -I../../src vgmrender.cpp em_inflate.cpp ../../src/ymfm_misc.cpp ../../src/ymfm_opl.cpp ../../src/ymfm_opm.cpp ../../src/ymfm_opn.cpp ../../src/ymfm_adpcm.cpp ../../src/ymfm_pcm.cpp ../../src/ymfm_ssg.cpp -o vgmrender.exe
+//
+// or:
+//
+// clang++ --std=c++14 -I../../src vgmrender.cpp em_inflate.cpp ../../src/ymfm_misc.cpp ../../src/ymfm_opl.cpp ../../src/ymfm_opm.cpp ../../src/ymfm_opn.cpp ../../src/ymfm_adpcm.cpp ../../src/ymfm_pcm.cpp ../../src/ymfm_ssg.cpp -o vgmrender.exe
+//
+// or:
+//
+// cl -I..\..\src vgmrender.cpp em_inflate.cpp ..\..\src\ymfm_misc.cpp ..\..\src\ymfm_opl.cpp ..\..\src\ymfm_opm.cpp ..\..\src\ymfm_opn.cpp ..\..\src\ymfm_adpcm.cpp ..\..\src\ymfm_pcm.cpp ..\..\src\ymfm_ssg.cpp /Od /Zi /std:c++14 /EHsc
+//
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <cmath>
+#include <cstdio>
+#include <cstdint>
+#include <cstring>
+#include <list>
+#include <string>
+
+#include "em_inflate.h"
+#include "ymfm_misc.h"
+#include "ymfm_opl.h"
+#include "ymfm_opm.h"
+#include "ymfm_opn.h"
+
+#define LOG_WRITES (0)
+
+// run this many dummy clocks of each chip before generating
+#define EXTRA_CLOCKS (0)
+
+
+// enable this to run the nuked OPN2 core in parallel; output is not captured,
+// but logging can be added to observe behaviors
+#define RUN_NUKED_OPN2 (0)
+#if (RUN_NUKED_OPN2)
+namespace nuked {
+bool s_log_envelopes = false;
+const int s_log_envelopes_channel = 5;
+#include "test/ym3438.h"
+}
+#endif
+
+// enable this to capture each chip at its native rate as well
+#define CAPTURE_NATIVE (0 || RUN_NUKED_OPN2)
+
+
+
+//*********************************************************
+// GLOBAL TYPES
+//*********************************************************
+
+// we use an int64_t as emulated time, as a 32.32 fixed point value
+using emulated_time = int64_t;
+
+// enumeration of the different types of chips we support
+enum chip_type
+{
+ CHIP_YM2149,
+ CHIP_YM2151,
+ CHIP_YM2203,
+ CHIP_YM2413,
+ CHIP_YM2608,
+ CHIP_YM2610,
+ CHIP_YM2612,
+ CHIP_YM3526,
+ CHIP_Y8950,
+ CHIP_YM3812,
+ CHIP_YMF262,
+ CHIP_YMF278B,
+ CHIP_TYPES
+};
+
+
+
+//*********************************************************
+// CLASSES
+//*********************************************************
+
+// ======================> vgm_chip_base
+
+// abstract base class for a Yamaha chip; we keep a list of these for processing
+// as new commands come in
+class vgm_chip_base
+{
+public:
+ // construction
+ vgm_chip_base(uint32_t clock, chip_type type, char const *name) :
+ m_type(type),
+ m_name(name)
+ {
+ }
+
+ // destruction
+ virtual ~vgm_chip_base()
+ {
+ }
+
+ // simple getters
+ chip_type type() const { return m_type; }
+ virtual uint32_t sample_rate() const = 0;
+
+ // required methods for derived classes to implement
+ virtual void write(uint32_t reg, uint8_t data) = 0;
+ virtual void generate(emulated_time output_start, emulated_time output_step, int32_t *buffer) = 0;
+
+ // write data to the ADPCM-A buffer
+ void write_data(ymfm::access_class type, uint32_t base, uint32_t length, uint8_t const *src)
+ {
+ uint32_t end = base + length;
+ if (end > m_data[type].size())
+ m_data[type].resize(end);
+ memcpy(&m_data[type][base], src, length);
+ }
+
+ // seek within the PCM stream
+ void seek_pcm(uint32_t pos) { m_pcm_offset = pos; }
+ uint8_t read_pcm() { auto &pcm = m_data[ymfm::ACCESS_PCM]; return (m_pcm_offset < pcm.size()) ? pcm[m_pcm_offset++] : 0; }
+
+protected:
+ // internal state
+ chip_type m_type;
+ std::string m_name;
+ std::vector<uint8_t> m_data[ymfm::ACCESS_CLASSES];
+ uint32_t m_pcm_offset;
+#if (CAPTURE_NATIVE)
+public:
+ std::vector<int32_t> m_native_data;
+#endif
+#if (RUN_NUKED_OPN2)
+public:
+ nuked::ym3438_t *m_external = nullptr;
+ std::vector<int32_t> m_nuked_data;
+#endif
+};
+
+
+// ======================> vgm_chip
+
+// actual chip-specific implementation class; includes implementatino of the
+// ymfm_interface as needed for vgmplay purposes
+template<typename ChipType>
+class vgm_chip : public vgm_chip_base, public ymfm::ymfm_interface
+{
+public:
+ // construction
+ vgm_chip(uint32_t clock, chip_type type, char const *name) :
+ vgm_chip_base(clock, type, name),
+ m_chip(*this),
+ m_clock(clock),
+ m_clocks(0),
+ m_step(0x100000000ull / m_chip.sample_rate(clock)),
+ m_pos(0)
+ {
+ m_chip.reset();
+
+ for (int clock = 0; clock < EXTRA_CLOCKS; clock++)
+ m_chip.generate(&m_output);
+
+#if (RUN_NUKED_OPN2)
+ if (type == CHIP_YM2612)
+ {
+ m_external = new nuked::ym3438_t;
+ nuked::OPN2_SetChipType(nuked::ym3438_mode_ym2612);
+ nuked::OPN2_Reset(m_external);
+ nuked::Bit16s buffer[2];
+ for (int clocks = 0; clocks < 24 * EXTRA_CLOCKS; clocks++)
+ nuked::OPN2_Clock(m_external, buffer);
+ }
+#endif
+ }
+
+ virtual uint32_t sample_rate() const override
+ {
+ return m_chip.sample_rate(m_clock);
+ }
+
+ // handle a register write: just queue for now
+ virtual void write(uint32_t reg, uint8_t data) override
+ {
+ m_queue.push_back(std::make_pair(reg, data));
+ }
+
+ // generate one output sample of output
+ virtual void generate(emulated_time output_start, emulated_time output_step, int32_t *buffer) override
+ {
+ uint32_t addr1 = 0xffff, addr2 = 0xffff;
+ uint8_t data1 = 0, data2 = 0;
+
+ // see if there is data to be written; if so, extract it and dequeue
+ if (!m_queue.empty())
+ {
+ auto front = m_queue.front();
+ addr1 = 0 + 2 * ((front.first >> 8) & 3);
+ data1 = front.first & 0xff;
+ addr2 = addr1 + ((m_type == CHIP_YM2149) ? 2 : 1);
+ data2 = front.second;
+ m_queue.erase(m_queue.begin());
+ }
+
+ // write to the chip
+ if (addr1 != 0xffff)
+ {
+ if (LOG_WRITES)
+ printf("%10.5f: %s %03X=%02X\n", double(output_start) / double(1LL << 32), m_name.c_str(), data1 + 0x100 * (addr1/2), data2);
+ m_chip.write(addr1, data1);
+ m_chip.write(addr2, data2);
+ }
+
+ // generate at the appropriate sample rate
+// nuked::s_log_envelopes = (output_start >= (22ll << 32) && output_start < (24ll << 32));
+ for ( ; m_pos <= output_start; m_pos += m_step)
+ {
+ m_chip.generate(&m_output);
+
+#if (CAPTURE_NATIVE)
+ // if capturing native, append each generated sample
+ m_native_data.push_back(m_output.data[0]);
+ m_native_data.push_back(m_output.data[ChipType::OUTPUTS > 1 ? 1 : 0]);
+#endif
+
+#if (RUN_NUKED_OPN2)
+ // if running nuked, capture its output as well
+ if (m_external != nullptr)
+ {
+ int32_t sum[2] = { 0 };
+ if (addr1 != 0xffff)
+ nuked::OPN2_Write(m_external, addr1, data1);
+ nuked::Bit16s buffer[2];
+ for (int clocks = 0; clocks < 12; clocks++)
+ {
+ nuked::OPN2_Clock(m_external, buffer);
+ sum[0] += buffer[0];
+ sum[1] += buffer[1];
+ }
+ if (addr2 != 0xffff)
+ nuked::OPN2_Write(m_external, addr2, data2);
+ for (int clocks = 0; clocks < 12; clocks++)
+ {
+ nuked::OPN2_Clock(m_external, buffer);
+ sum[0] += buffer[0];
+ sum[1] += buffer[1];
+ }
+ addr1 = addr2 = 0xffff;
+ m_nuked_data.push_back(sum[0] / 24);
+ m_nuked_data.push_back(sum[1] / 24);
+ }
+#endif
+ }
+
+ // add the final result to the buffer
+ if (m_type == CHIP_YM2203)
+ {
+ int32_t out0 = m_output.data[0];
+ int32_t out1 = m_output.data[1 % ChipType::OUTPUTS];
+ int32_t out2 = m_output.data[2 % ChipType::OUTPUTS];
+ int32_t out3 = m_output.data[3 % ChipType::OUTPUTS];
+ *buffer++ += out0 + out1 + out2 + out3;
+ *buffer++ += out0 + out1 + out2 + out3;
+ }
+ else if (m_type == CHIP_YM2608 || m_type == CHIP_YM2610)
+ {
+ int32_t out0 = m_output.data[0];
+ int32_t out1 = m_output.data[1 % ChipType::OUTPUTS];
+ int32_t out2 = m_output.data[2 % ChipType::OUTPUTS];
+ *buffer++ += out0 + out2;
+ *buffer++ += out1 + out2;
+ }
+ else if (m_type == CHIP_YMF278B)
+ {
+ *buffer++ += m_output.data[4 % ChipType::OUTPUTS];
+ *buffer++ += m_output.data[5 % ChipType::OUTPUTS];
+ }
+ else if (ChipType::OUTPUTS == 1)
+ {
+ *buffer++ += m_output.data[0];
+ *buffer++ += m_output.data[0];
+ }
+ else
+ {
+ *buffer++ += m_output.data[0];
+ *buffer++ += m_output.data[1 % ChipType::OUTPUTS];
+ }
+ m_clocks++;
+ }
+
+protected:
+ // handle a read from the buffer
+ virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t offset) override
+ {
+ auto &data = m_data[type];
+ return (offset < data.size()) ? data[offset] : 0;
+ }
+
+ // internal state
+ ChipType m_chip;
+ uint32_t m_clock;
+ uint64_t m_clocks;
+ typename ChipType::output_data m_output;
+ emulated_time m_step;
+ emulated_time m_pos;
+ std::vector<std::pair<uint32_t, uint8_t>> m_queue;
+};
+
+
+
+//*********************************************************
+// GLOBAL HELPERS
+//*********************************************************
+
+// global list of active chips
+std::vector<std::unique_ptr<vgm_chip_base>> active_chips;
+
+
+//-------------------------------------------------
+// parse_uint32 - parse a little-endian uint32_t
+//-------------------------------------------------
+
+uint32_t parse_uint32(std::vector<uint8_t> &buffer, uint32_t &offset)
+{
+ uint32_t result = buffer[offset++];
+ result |= buffer[offset++] << 8;
+ result |= buffer[offset++] << 16;
+ result |= buffer[offset++] << 24;
+ return result;
+}
+
+
+//-------------------------------------------------
+// add_chips - add 1 or 2 instances of the given
+// supported chip type
+//-------------------------------------------------
+
+template<typename ChipType>
+void add_chips(uint32_t clock, chip_type type, char const *chipname)
+{
+ uint32_t clockval = clock & 0x3fffffff;
+ int numchips = (clock & 0x40000000) ? 2 : 1;
+ printf("Adding %s%s @ %dHz\n", (numchips == 2) ? "2 x " : "", chipname, clockval);
+ for (int index = 0; index < numchips; index++)
+ {
+ char name[100];
+ sprintf(name, "%s #%d", chipname, index);
+ active_chips.push_back(std::make_unique<vgm_chip<ChipType>>(clockval, type, (numchips == 2) ? name : chipname));
+ }
+
+ if (type == CHIP_YM2608)
+ {
+ FILE *rom = fopen("ym2608_adpcm_rom.bin", "rb");
+ if (rom == nullptr)
+ fprintf(stderr, "Warning: YM2608 enabled but ym2608_adpcm_rom.bin not found\n");
+ else
+ {
+ fseek(rom, 0, SEEK_END);
+ uint32_t size = ftell(rom);
+ fseek(rom, 0, SEEK_SET);
+ std::vector<uint8_t> temp(size);
+ fread(&temp[0], 1, size, rom);
+ fclose(rom);
+ for (auto &chip : active_chips)
+ if (chip->type() == type)
+ chip->write_data(ymfm::ACCESS_ADPCM_A, 0, size, &temp[0]);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// parse_header - parse the vgm header, adding
+// chips for anything we encounter that we can
+// support
+//-------------------------------------------------
+
+uint32_t parse_header(std::vector<uint8_t> &buffer)
+{
+ // +00: already checked the ID
+ uint32_t offset = 4;
+
+ // +04: parse the size
+ uint32_t size = parse_uint32(buffer, offset);
+ if (offset - 4 + size > buffer.size())
+ {
+ fprintf(stderr, "Total size for file is too small; file may be truncated\n");
+ size = buffer.size() - 4;
+ }
+ buffer.resize(size + 4);
+
+ // +08: parse the version
+ uint32_t version = parse_uint32(buffer, offset);
+ if (version > 0x171)
+ fprintf(stderr, "Warning: version > 1.71 detected, some things may not work\n");
+
+ // +0C: SN76489 clock
+ uint32_t clock = parse_uint32(buffer, offset);
+ if (clock != 0)
+ fprintf(stderr, "Warning: clock for SN76489 specified (%d), but not supported\n", clock);
+
+ // +10: YM2413 clock
+ clock = parse_uint32(buffer, offset);
+ if (clock != 0)
+ add_chips<ymfm::ym2413>(clock, CHIP_YM2413, "YM2413");
+
+ // +14: GD3 offset
+ uint32_t dummy = parse_uint32(buffer, offset);
+
+ // +18: Total # samples
+ dummy = parse_uint32(buffer, offset);
+
+ // +1C: Loop offset
+ dummy = parse_uint32(buffer, offset);
+
+ // +20: Loop # samples
+ dummy = parse_uint32(buffer, offset);
+
+ // +24: Rate
+ dummy = parse_uint32(buffer, offset);
+
+ // +28: SN76489 feedback / SN76489 shift register width / SN76489 Flags
+ dummy = parse_uint32(buffer, offset);
+
+ // +2C: YM2612 clock
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x110 && clock != 0)
+ add_chips<ymfm::ym2612>(clock, CHIP_YM2612, "YM2612");
+
+ // +30: YM2151 clock
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x110 && clock != 0)
+ add_chips<ymfm::ym2151>(clock, CHIP_YM2151, "YM2151");
+
+ // +34: VGM data offset
+ uint32_t data_start = parse_uint32(buffer, offset);
+ data_start += offset - 4;
+ if (version < 0x150)
+ data_start = 0x40;
+
+ // +38: Sega PCM clock
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ fprintf(stderr, "Warning: clock for Sega PCM specified, but not supported\n");
+
+ // +3C: Sega PCM interface register
+ dummy = parse_uint32(buffer, offset);
+
+ // +40: RF5C68 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ fprintf(stderr, "Warning: clock for RF5C68 specified, but not supported\n");
+
+ // +44: YM2203 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ add_chips<ymfm::ym2203>(clock, CHIP_YM2203, "YM2203");
+
+ // +48: YM2608 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ add_chips<ymfm::ym2608>(clock, CHIP_YM2608, "YM2608");
+
+ // +4C: YM2610/2610B clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ {
+ if (clock & 0x80000000)
+ add_chips<ymfm::ym2610b>(clock, CHIP_YM2610, "YM2610B");
+ else
+ add_chips<ymfm::ym2610>(clock, CHIP_YM2610, "YM2610");
+ }
+
+ // +50: YM3812 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ add_chips<ymfm::ym3812>(clock, CHIP_YM3812, "YM3812");
+
+ // +54: YM3526 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ add_chips<ymfm::ym3526>(clock, CHIP_YM3526, "YM3526");
+
+ // +58: Y8950 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ add_chips<ymfm::y8950>(clock, CHIP_Y8950, "Y8950");
+
+ // +5C: YMF262 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ add_chips<ymfm::ymf262>(clock, CHIP_YMF262, "YMF262");
+
+ // +60: YMF278B clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ add_chips<ymfm::ymf278b>(clock, CHIP_YMF278B, "YMF278B");
+
+ // +64: YMF271 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ fprintf(stderr, "Warning: clock for YMF271 specified, but not supported\n");
+
+ // +68: YMF280B clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ fprintf(stderr, "Warning: clock for YMF280B specified, but not supported\n");
+
+ // +6C: RF5C164 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ fprintf(stderr, "Warning: clock for RF5C164 specified, but not supported\n");
+
+ // +70: PWM clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ fprintf(stderr, "Warning: clock for PWM specified, but not supported\n");
+
+ // +74: AY8910 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x151 && clock != 0)
+ {
+ fprintf(stderr, "Warning: clock for AY8910 specified, substituting YM2149\n");
+ add_chips<ymfm::ym2149>(clock, CHIP_YM2149, "YM2149");
+ }
+
+ // +78: AY8910 flags
+ if (offset + 4 > data_start)
+ return data_start;
+ dummy = parse_uint32(buffer, offset);
+
+ // +7C: volume / loop info
+ if (offset + 4 > data_start)
+ return data_start;
+ dummy = parse_uint32(buffer, offset);
+ if ((dummy & 0xff) != 0)
+ printf("Volume modifier: %02X (=%d)\n", dummy & 0xff, int(pow(2, double(dummy & 0xff) / 0x20)));
+
+ // +80: GameBoy DMG clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for GameBoy DMG specified, but not supported\n");
+
+ // +84: NES APU clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for NES APU specified, but not supported\n");
+
+ // +88: MultiPCM clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for MultiPCM specified, but not supported\n");
+
+ // +8C: uPD7759 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for uPD7759 specified, but not supported\n");
+
+ // +90: OKIM6258 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for OKIM6258 specified, but not supported\n");
+
+ // +94: OKIM6258 Flags / K054539 Flags / C140 Chip Type / reserved
+ if (offset + 4 > data_start)
+ return data_start;
+ dummy = parse_uint32(buffer, offset);
+
+ // +98: OKIM6295 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for OKIM6295 specified, but not supported\n");
+
+ // +9C: K051649 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for K051649 specified, but not supported\n");
+
+ // +A0: K054539 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for K054539 specified, but not supported\n");
+
+ // +A4: HuC6280 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for HuC6280 specified, but not supported\n");
+
+ // +A8: C140 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for C140 specified, but not supported\n");
+
+ // +AC: K053260 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for K053260 specified, but not supported\n");
+
+ // +B0: Pokey clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for Pokey specified, but not supported\n");
+
+ // +B4: QSound clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x161 && clock != 0)
+ fprintf(stderr, "Warning: clock for QSound specified, but not supported\n");
+
+ // +B8: SCSP clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for SCSP specified, but not supported\n");
+
+ // +BC: extra header offset
+ if (offset + 4 > data_start)
+ return data_start;
+ uint32_t extra_header = parse_uint32(buffer, offset);
+
+ // +C0: WonderSwan clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for WonderSwan specified, but not supported\n");
+
+ // +C4: VSU clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for VSU specified, but not supported\n");
+
+ // +C8: SAA1099 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for SAA1099 specified, but not supported\n");
+
+ // +CC: ES5503 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for ES5503 specified, but not supported\n");
+
+ // +D0: ES5505/ES5506 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for ES5505/ES5506 specified, but not supported\n");
+
+ // +D4: ES5503 output channels / ES5505/ES5506 amount of output channels / C352 clock divider
+ if (offset + 4 > data_start)
+ return data_start;
+ dummy = parse_uint32(buffer, offset);
+
+ // +D8: X1-010 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for X1-010 specified, but not supported\n");
+
+ // +DC: C352 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for C352 specified, but not supported\n");
+
+ // +E0: GA20 clock
+ if (offset + 4 > data_start)
+ return data_start;
+ clock = parse_uint32(buffer, offset);
+ if (version >= 0x171 && clock != 0)
+ fprintf(stderr, "Warning: clock for GA20 specified, but not supported\n");
+
+ return data_start;
+}
+
+
+//-------------------------------------------------
+// find_chip - find the given chip and index
+//-------------------------------------------------
+
+vgm_chip_base *find_chip(chip_type type, uint8_t index)
+{
+ for (auto &chip : active_chips)
+ if (chip->type() == type && index-- == 0)
+ return chip.get();
+ return nullptr;
+}
+
+
+//-------------------------------------------------
+// write_chip - handle a write to the given chip
+// and index
+//-------------------------------------------------
+
+void write_chip(chip_type type, uint8_t index, uint32_t reg, uint8_t data)
+{
+ vgm_chip_base *chip = find_chip(type, index);
+ if (chip != nullptr)
+ chip->write(reg, data);
+}
+
+
+//-------------------------------------------------
+// add_rom_data - add data to the given chip
+// type in the given access class
+//-------------------------------------------------
+
+void add_rom_data(chip_type type, ymfm::access_class access, std::vector<uint8_t> &buffer, uint32_t &localoffset, uint32_t size)
+{
+ uint32_t length = parse_uint32(buffer, localoffset);
+ uint32_t start = parse_uint32(buffer, localoffset);
+ for (int index = 0; index < 2; index++)
+ {
+ vgm_chip_base *chip = find_chip(type, index);
+ if (chip != nullptr)
+ chip->write_data(access, start, size, &buffer[localoffset]);
+ }
+}
+
+
+//-------------------------------------------------
+// generate_all - generate everything described
+// in the vgmplay file
+//-------------------------------------------------
+
+void generate_all(std::vector<uint8_t> &buffer, uint32_t data_start, uint32_t output_rate, std::vector<int32_t> &wav_buffer)
+{
+ // set the offset to the data start and go
+ uint32_t offset = data_start;
+ bool done = false;
+ emulated_time output_step = 0x100000000ull / output_rate;
+ emulated_time output_pos = 0;
+ while (!done && offset < buffer.size())
+ {
+ int delay = 0;
+ uint8_t cmd = buffer[offset++];
+ switch (cmd)
+ {
+ // YM2413, write value dd to register aa
+ case 0x51:
+ case 0xa1:
+ write_chip(CHIP_YM2413, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM2612 port 0, write value dd to register aa
+ case 0x52:
+ case 0xa2:
+ write_chip(CHIP_YM2612, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM2612 port 1, write value dd to register aa
+ case 0x53:
+ case 0xa3:
+ write_chip(CHIP_YM2612, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM2151, write value dd to register aa
+ case 0x54:
+ case 0xa4:
+ write_chip(CHIP_YM2151, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM2203, write value dd to register aa
+ case 0x55:
+ case 0xa5:
+ write_chip(CHIP_YM2203, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM2608 port 0, write value dd to register aa
+ case 0x56:
+ case 0xa6:
+ write_chip(CHIP_YM2608, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM2608 port 1, write value dd to register aa
+ case 0x57:
+ case 0xa7:
+ write_chip(CHIP_YM2608, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM2610 port 0, write value dd to register aa
+ case 0x58:
+ case 0xa8:
+ write_chip(CHIP_YM2610, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM2610 port 1, write value dd to register aa
+ case 0x59:
+ case 0xa9:
+ write_chip(CHIP_YM2610, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM3812, write value dd to register aa
+ case 0x5a:
+ case 0xaa:
+ write_chip(CHIP_YM3812, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YM3526, write value dd to register aa
+ case 0x5b:
+ case 0xab:
+ write_chip(CHIP_YM3526, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // Y8950, write value dd to register aa
+ case 0x5c:
+ case 0xac:
+ write_chip(CHIP_Y8950, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YMF262 port 0, write value dd to register aa
+ case 0x5e:
+ case 0xae:
+ write_chip(CHIP_YMF262, cmd >> 7, buffer[offset], buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // YMF262 port 1, write value dd to register aa
+ case 0x5f:
+ case 0xaf:
+ write_chip(CHIP_YMF262, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // Wait n samples, n can range from 0 to 65535 (approx 1.49 seconds)
+ case 0x61:
+ delay = buffer[offset] | (buffer[offset + 1] << 8);
+ offset += 2;
+ break;
+
+ // wait 735 samples (60th of a second)
+ case 0x62:
+ delay = 735;
+ break;
+
+ // wait 882 samples (50th of a second)
+ case 0x63:
+ delay = 882;
+ break;
+
+ // end of sound data
+ case 0x66:
+ done = true;
+ break;
+
+ // data block
+ case 0x67:
+ {
+ uint8_t dummy = buffer[offset++];
+ if (dummy != 0x66)
+ break;
+ uint8_t type = buffer[offset++];
+ uint32_t size = parse_uint32(buffer, offset);
+ uint32_t localoffset = offset;
+
+ switch (type)
+ {
+ case 0x01: // RF5C68 PCM data for use with associated commands
+ case 0x02: // RF5C164 PCM data for use with associated commands
+ case 0x03: // PWM PCM data for use with associated commands
+ case 0x04: // OKIM6258 ADPCM data for use with associated commands
+ case 0x05: // HuC6280 PCM data for use with associated commands
+ case 0x06: // SCSP PCM data for use with associated commands
+ case 0x07: // NES APU DPCM data for use with associated commands
+ break;
+
+ case 0x00: // YM2612 PCM data for use with associated commands
+ {
+ vgm_chip_base *chip = find_chip(CHIP_YM2612, 0);
+ if (chip != nullptr)
+ chip->write_data(ymfm::ACCESS_PCM, 0, size - 8, &buffer[localoffset]);
+ break;
+ }
+
+ case 0x82: // YM2610 ADPCM ROM data
+ add_rom_data(CHIP_YM2610, ymfm::ACCESS_ADPCM_A, buffer, localoffset, size - 8);
+ break;
+
+ case 0x81: // YM2608 DELTA-T ROM data
+ add_rom_data(CHIP_YM2608, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8);
+ break;
+
+ case 0x83: // YM2610 DELTA-T ROM data
+ add_rom_data(CHIP_YM2610, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8);
+ break;
+
+ case 0x84: // YMF278B ROM data
+ case 0x87: // YMF278B RAM data
+ add_rom_data(CHIP_YMF278B, ymfm::ACCESS_PCM, buffer, localoffset, size - 8);
+ break;
+
+ case 0x88: // Y8950 DELTA-T ROM data
+ add_rom_data(CHIP_Y8950, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8);
+ break;
+
+ case 0x80: // Sega PCM ROM data
+ case 0x85: // YMF271 ROM data
+ case 0x86: // YMZ280B ROM data
+ case 0x89: // MultiPCM ROM data
+ case 0x8A: // uPD7759 ROM data
+ case 0x8B: // OKIM6295 ROM data
+ case 0x8C: // K054539 ROM data
+ case 0x8D: // C140 ROM data
+ case 0x8E: // K053260 ROM data
+ case 0x8F: // Q-Sound ROM data
+ case 0x90: // ES5505/ES5506 ROM data
+ case 0x91: // X1-010 ROM data
+ case 0x92: // C352 ROM data
+ case 0x93: // GA20 ROM data
+ break;
+
+ case 0xC0: // RF5C68 RAM write
+ case 0xC1: // RF5C164 RAM write
+ case 0xC2: // NES APU RAM write
+ case 0xE0: // SCSP RAM write
+ case 0xE1: // ES5503 RAM write
+ break;
+
+ default:
+ if (type >= 0x40 && type < 0x7f)
+ printf("Compressed data block not supported\n");
+ else
+ printf("Unknown data block type 0x%02X\n", type);
+ break;
+ }
+ offset += size;
+ break;
+ }
+
+ // PCM RAM write
+ case 0x68:
+ printf("68: PCM RAM write\n");
+ break;
+
+ // AY8910, write value dd to register aa
+ case 0xa0:
+ write_chip(CHIP_YM2149, buffer[offset] >> 7, buffer[offset] & 0x7f, buffer[offset + 1]);
+ offset += 2;
+ break;
+
+ // pp aa dd: YMF278B, port pp, write value dd to register aa
+ case 0xd0:
+ write_chip(CHIP_YMF278B, buffer[offset] >> 7, ((buffer[offset] & 0x7f) << 8) | buffer[offset + 1], buffer[offset + 2]);
+ offset += 3;
+ break;
+
+ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ delay = (cmd & 15) + 1;
+ break;
+
+ case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ {
+ vgm_chip_base *chip = find_chip(CHIP_YM2612, 0);
+ if (chip != nullptr)
+ chip->write(0x2a, chip->read_pcm());
+ delay = cmd & 15;
+ break;
+ }
+
+ // ignored, consume one byte
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ case 0x4f: // dd: Game Gear PSG stereo, write dd to port 0x06
+ case 0x50: // dd: PSG (SN76489/SN76496) write value dd
+ offset++;
+ break;
+
+ // ignored, consume two bytes
+ case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
+ case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e:
+ case 0x5d: // aa dd: YMZ280B, write value dd to register aa
+ case 0xb0: // aa dd: RF5C68, write value dd to register aa
+ case 0xb1: // aa dd: RF5C164, write value dd to register aa
+ case 0xb2: // aa dd: PWM, write value ddd to register a (d is MSB, dd is LSB)
+ case 0xb3: // aa dd: GameBoy DMG, write value dd to register aa
+ case 0xb4: // aa dd: NES APU, write value dd to register aa
+ case 0xb5: // aa dd: MultiPCM, write value dd to register aa
+ case 0xb6: // aa dd: uPD7759, write value dd to register aa
+ case 0xb7: // aa dd: OKIM6258, write value dd to register aa
+ case 0xb8: // aa dd: OKIM6295, write value dd to register aa
+ case 0xb9: // aa dd: HuC6280, write value dd to register aa
+ case 0xba: // aa dd: K053260, write value dd to register aa
+ case 0xbb: // aa dd: Pokey, write value dd to register aa
+ case 0xbc: // aa dd: WonderSwan, write value dd to register aa
+ case 0xbd: // aa dd: SAA1099, write value dd to register aa
+ case 0xbe: // aa dd: ES5506, write value dd to register aa
+ case 0xbf: // aa dd: GA20, write value dd to register aa
+ offset += 2;
+ break;
+
+ // ignored, consume three bytes
+ case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf:
+ case 0xd7: case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
+ case 0xc0: // bbaa dd: Sega PCM, write value dd to memory offset aabb
+ case 0xc1: // bbaa dd: RF5C68, write value dd to memory offset aabb
+ case 0xc2: // bbaa dd: RF5C164, write value dd to memory offset aabb
+ case 0xc3: // cc bbaa: MultiPCM, write set bank offset aabb to channel cc
+ case 0xc4: // mmll rr: QSound, write value mmll to register rr (mm - data MSB, ll - data LSB)
+ case 0xc5: // mmll dd: SCSP, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB)
+ case 0xc6: // mmll dd: WonderSwan, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB)
+ case 0xc7: // mmll dd: VSU, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB)
+ case 0xc8: // mmll dd: X1-010, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB)
+ case 0xd1: // pp aa dd: YMF271, port pp, write value dd to register aa
+ case 0xd2: // pp aa dd: SCC1, port pp, write value dd to register aa
+ case 0xd3: // pp aa dd: K054539, write value dd to register ppaa
+ case 0xd4: // pp aa dd: C140, write value dd to register ppaa
+ case 0xd5: // pp aa dd: ES5503, write value dd to register ppaa
+ case 0xd6: // pp aa dd: ES5506, write value aadd to register pp
+ offset += 3;
+ break;
+
+ // ignored, consume four bytes
+ case 0xe0: // dddddddd: Seek to offset dddddddd (Intel byte order) in PCM data bank of data block type 0 (YM2612).
+ {
+ vgm_chip_base *chip = find_chip(CHIP_YM2612, 0);
+ uint32_t pos = parse_uint32(buffer, offset);
+ if (chip != nullptr)
+ chip->seek_pcm(pos);
+ offset += 4;
+ break;
+ }
+ case 0xe1: // mmll aadd: C352, write value aadd to register mmll
+ case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ offset += 4;
+ break;
+ }
+
+ // handle delays
+ while (delay-- != 0)
+ {
+ bool more_remaining = false;
+ int32_t outputs[2] = { 0 };
+ for (auto &chip : active_chips)
+ chip->generate(output_pos, output_step, outputs);
+ output_pos += output_step;
+ wav_buffer.push_back(outputs[0]);
+ wav_buffer.push_back(outputs[1]);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// write_wav - write a WAV file from the provided
+// stereo data
+//-------------------------------------------------
+
+int write_wav(char const *filename, uint32_t output_rate, std::vector<int32_t> &wav_buffer_src)
+{
+ // determine normalization parameters
+ int32_t max_scale = 0;
+ for (size_t index = 0; index < wav_buffer_src.size(); index++)
+ {
+ int32_t absval = std::abs(wav_buffer_src[index]);
+ max_scale = std::max(max_scale, absval);
+ }
+
+ // warn if only silence was detected (and also avoid divide by zero)
+ if (max_scale == 0)
+ {
+ fprintf(stderr, "The WAV file data will only contain silence.\n");
+ max_scale = 1;
+ }
+
+ // now convert
+ std::vector<int16_t> wav_buffer(wav_buffer_src.size());
+ for (size_t index = 0; index < wav_buffer_src.size(); index++)
+ wav_buffer[index] = wav_buffer_src[index] * 26000 / max_scale;
+
+ // write the WAV file
+ FILE *out = fopen(filename, "wb");
+ if (out == nullptr)
+ {
+ fprintf(stderr, "Error creating output file '%s'\n", filename);
+ return 6;
+ }
+
+ // write the 'RIFF' header
+ if (fwrite("RIFF", 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the total size
+ uint32_t total_size = 48 + wav_buffer.size() * 2 - 8;
+ uint8_t wavdata[4];
+ wavdata[0] = total_size >> 0;
+ wavdata[1] = total_size >> 8;
+ wavdata[2] = total_size >> 16;
+ wavdata[3] = total_size >> 24;
+ if (fwrite(wavdata, 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the 'WAVE' type
+ if (fwrite("WAVE", 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the 'fmt ' tag
+ if (fwrite("fmt ", 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the format length
+ wavdata[0] = 16;
+ wavdata[1] = 0;
+ wavdata[2] = 0;
+ wavdata[3] = 0;
+ if (fwrite(wavdata, 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the format (PCM)
+ wavdata[0] = 1;
+ wavdata[1] = 0;
+ if (fwrite(wavdata, 1, 2, out) != 2)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the channels
+ wavdata[0] = 2;
+ wavdata[1] = 0;
+ if (fwrite(wavdata, 1, 2, out) != 2)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the sample rate
+ wavdata[0] = output_rate >> 0;
+ wavdata[1] = output_rate >> 8;
+ wavdata[2] = output_rate >> 16;
+ wavdata[3] = output_rate >> 24;
+ if (fwrite(wavdata, 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the bytes/second
+ uint32_t bps = output_rate * 2 * 2;
+ wavdata[0] = bps >> 0;
+ wavdata[1] = bps >> 8;
+ wavdata[2] = bps >> 16;
+ wavdata[3] = bps >> 24;
+ if (fwrite(wavdata, 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the block align
+ wavdata[0] = 4;
+ wavdata[1] = 0;
+ if (fwrite(wavdata, 1, 2, out) != 2)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the bits/sample
+ wavdata[0] = 16;
+ wavdata[1] = 0;
+ if (fwrite(wavdata, 1, 2, out) != 2)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the 'data' tag
+ if (fwrite("data", 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the data length
+ uint32_t datalen = wav_buffer.size() * 2;
+ wavdata[0] = datalen >> 0;
+ wavdata[1] = datalen >> 8;
+ wavdata[2] = datalen >> 16;
+ wavdata[3] = datalen >> 24;
+ if (fwrite(wavdata, 1, 4, out) != 4)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+
+ // write the data
+ if (fwrite(&wav_buffer[0], 1, datalen, out) != datalen)
+ {
+ fprintf(stderr, "Error writing to output file\n");
+ return 7;
+ }
+ fclose(out);
+ return 0;
+}
+
+
+//-------------------------------------------------
+// main - program entry point
+//-------------------------------------------------
+
+int main(int argc, char *argv[])
+{
+ char const *filename = nullptr;
+ char const *outfilename = nullptr;
+ int output_rate = 44100;
+
+ // parse command line
+ bool argerr = false;
+ for (int arg = 1; arg < argc; arg++)
+ {
+ char const *curarg = argv[arg];
+ if (*curarg == '-')
+ {
+ if (strcmp(curarg, "-o") == 0 || strcmp(curarg, "--output") == 0)
+ outfilename = argv[++arg];
+ else if (strcmp(curarg, "-r") == 0 || strcmp(curarg, "--samplerate") == 0)
+ output_rate = atoi(argv[++arg]);
+ else
+ {
+ fprintf(stderr, "Unknown argument: %s\n", curarg);
+ argerr = true;
+ }
+ }
+ else
+ filename = curarg;
+ }
+
+ // if invalid syntax, show usage
+ if (argerr || filename == nullptr || outfilename == nullptr)
+ {
+ fprintf(stderr, "Usage: vgmrender <inputfile> -o <outputfile> [-r <rate>]\n");
+ return 1;
+ }
+
+ // attempt to read the file
+ FILE *file = fopen(filename, "rb");
+ if (file == nullptr)
+ {
+ fprintf(stderr, "Error opening file '%s'\n", filename);
+ return 2;
+ }
+
+ // get the length and create a buffer
+ fseek(file, 0, SEEK_END);
+ uint32_t size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ std::vector<uint8_t> buffer(size);
+
+ // read the contents
+ auto bytes_read = fread(&buffer[0], 1, size, file);
+ if (bytes_read != size)
+ {
+ fprintf(stderr, "Error reading file contents\n");
+ return 3;
+ }
+ fclose(file);
+
+ // check for gzip-format
+ if (buffer.size() >= 10 && buffer[0] == 0x1f && buffer[1] == 0x8b && buffer[2] == 0x08)
+ {
+ // copy the raw data to a new buffer
+ std::vector<uint8_t> compressed = buffer;
+
+ // determine uncompressed size and resize the buffer
+ uint8_t *end = &compressed[compressed.size()];
+ uint32_t uncompressed = end[-4] | (end[-3] << 8) | (end[-2] << 16) | (end[-1] << 24);
+ if (size < compressed.size() || size > 32*1024*1024)
+ {
+ fprintf(stderr, "File '%s' appears to be a compressed file but has unexpected size of %d\n", filename, size);
+ return 4;
+ }
+ buffer.resize(uncompressed);
+
+ // decompress the data
+ auto result = em_inflate(&compressed[0], compressed.size(), &buffer[0], buffer.size());
+ if (result == -1)
+ {
+ fprintf(stderr, "Error decompressing data from file\n");
+ return 4;
+ }
+ }
+
+ // check the ID
+ uint32_t offset = 0;
+ if (buffer.size() < 64 || buffer[0] != 'V' || buffer[1] != 'g' || buffer[2] != 'm' || buffer[3] != ' ')
+ {
+ fprintf(stderr, "File '%s' does not appear to be a valid VGM file\n", filename);
+ return 4;
+ }
+
+ // parse the header, creating any chips needed
+ uint32_t data_start = parse_header(buffer);
+
+ // if no chips created, fail
+ if (active_chips.size() == 0)
+ {
+ fprintf(stderr, "No compatible chips found, exiting.\n");
+ return 5;
+ }
+
+ // generate the output
+ std::vector<int32_t> wav_buffer;
+ generate_all(buffer, data_start, output_rate, wav_buffer);
+
+ int err = write_wav(outfilename, output_rate, wav_buffer);
+
+#if (CAPTURE_NATIVE)
+ {
+ int chipnum = 0;
+ for (auto &chip : active_chips)
+ if (err == 0 && chip->m_native_data.size() > 0)
+ {
+ char filename[20];
+ sprintf(filename, "native-%d.wav", chipnum++);
+ err = write_wav(filename, chip->sample_rate(), chip->m_native_data);
+ }
+ }
+#endif
+#if (RUN_NUKED_OPN2)
+ {
+ int chipnum = 0;
+ for (auto &chip : active_chips)
+ if (err == 0 && chip->m_nuked_data.size() > 0)
+ {
+ char filename[20];
+ sprintf(filename, "nuked-%d.wav", chipnum++);
+ err = write_wav(filename, chip->sample_rate(), chip->m_nuked_data);
+ }
+ }
+#endif
+
+ active_chips.clear();
+
+ return err;
+}
+
+#if (RUN_NUKED_OPN2)
+namespace nuked {
+#include "test/ym3438.c"
+}
+#endif
diff --git a/3rdparty/ymfm/src/ymfm.h b/3rdparty/ymfm/src/ymfm.h
new file mode 100644
index 00000000000..6427e34cda2
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm.h
@@ -0,0 +1,566 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_H
+#define YMFM_H
+
+#pragma once
+
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+ #define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+#include <array>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace ymfm
+{
+
+//*********************************************************
+// DEBUGGING
+//*********************************************************
+
+class debug
+{
+public:
+ // masks to help isolate specific channels
+ static constexpr uint32_t GLOBAL_FM_CHANNEL_MASK = 0xffffffff;
+ static constexpr uint32_t GLOBAL_ADPCM_A_CHANNEL_MASK = 0xffffffff;
+ static constexpr uint32_t GLOBAL_ADPCM_B_CHANNEL_MASK = 0xffffffff;
+ static constexpr uint32_t GLOBAL_PCM_CHANNEL_MASK = 0xffffffff;
+
+ // types of logging
+ static constexpr bool LOG_FM_WRITES = false;
+ static constexpr bool LOG_KEYON_EVENTS = false;
+ static constexpr bool LOG_UNEXPECTED_READ_WRITES = false;
+
+ // helpers to write based on the log type
+ template<typename... Params> static void log_fm_write(Params &&... args) { if (LOG_FM_WRITES) log(args...); }
+ template<typename... Params> static void log_keyon(Params &&... args) { if (LOG_KEYON_EVENTS) log(args...); }
+ template<typename... Params> static void log_unexpected_read_write(Params &&... args) { if (LOG_UNEXPECTED_READ_WRITES) log(args...); }
+
+ // downstream helper to output log data; defaults to printf
+ template<typename... Params> static void log(Params &&... args) { printf(args...); }
+};
+
+
+
+//*********************************************************
+// GLOBAL HELPERS
+//*********************************************************
+
+//-------------------------------------------------
+// bitfield - extract a bitfield from the given
+// value, starting at bit 'start' for a length of
+// 'length' bits
+//-------------------------------------------------
+
+inline uint32_t bitfield(uint32_t value, int start, int length = 1)
+{
+ return (value >> start) & ((1 << length) - 1);
+}
+
+
+//-------------------------------------------------
+// clamp - clamp between the minimum and maximum
+// values provided
+//-------------------------------------------------
+
+inline int32_t clamp(int32_t value, int32_t minval, int32_t maxval)
+{
+ if (value < minval)
+ return minval;
+ if (value > maxval)
+ return maxval;
+ return value;
+}
+
+
+//-------------------------------------------------
+// count_leading_zeros - return the number of
+// leading zeros in a 32-bit value; CPU-optimized
+// versions for various architectures are included
+// below
+//-------------------------------------------------
+
+#if defined(__GNUC__)
+
+inline uint8_t count_leading_zeros(uint32_t value)
+{
+ if (value == 0)
+ return 32;
+ return __builtin_clz(value);
+}
+
+#elif defined(_MSC_VER)
+
+inline uint8_t count_leading_zeros(uint32_t value)
+{
+ unsigned long index;
+ return _BitScanReverse(&index, value) ? uint8_t(31U - index) : 32U;
+}
+
+#else
+
+inline uint8_t count_leading_zeros(uint32_t value)
+{
+ if (value == 0)
+ return 32;
+ uint8_t count;
+ for (count = 0; int32_t(value) >= 0; count++)
+ value <<= 1;
+ return count;
+}
+
+#endif
+
+
+// Many of the Yamaha FM chips emit a floating-point value, which is sent to
+// a DAC for processing. The exact format of this floating-point value is
+// documented below. This description only makes sense if the "internal"
+// format treats sign as 1=positive and 0=negative, so the helpers below
+// presume that.
+//
+// Internal OPx data 16-bit signed data Exp Sign Mantissa
+// ================= ================= === ==== ========
+// 1 1xxxxxxxx------ -> 0 1xxxxxxxx------ -> 111 1 1xxxxxxx
+// 1 01xxxxxxxx----- -> 0 01xxxxxxxx----- -> 110 1 1xxxxxxx
+// 1 001xxxxxxxx---- -> 0 001xxxxxxxx---- -> 101 1 1xxxxxxx
+// 1 0001xxxxxxxx--- -> 0 0001xxxxxxxx--- -> 100 1 1xxxxxxx
+// 1 00001xxxxxxxx-- -> 0 00001xxxxxxxx-- -> 011 1 1xxxxxxx
+// 1 000001xxxxxxxx- -> 0 000001xxxxxxxx- -> 010 1 1xxxxxxx
+// 1 000000xxxxxxxxx -> 0 000000xxxxxxxxx -> 001 1 xxxxxxxx
+// 0 111111xxxxxxxxx -> 1 111111xxxxxxxxx -> 001 0 xxxxxxxx
+// 0 111110xxxxxxxx- -> 1 111110xxxxxxxx- -> 010 0 0xxxxxxx
+// 0 11110xxxxxxxx-- -> 1 11110xxxxxxxx-- -> 011 0 0xxxxxxx
+// 0 1110xxxxxxxx--- -> 1 1110xxxxxxxx--- -> 100 0 0xxxxxxx
+// 0 110xxxxxxxx---- -> 1 110xxxxxxxx---- -> 101 0 0xxxxxxx
+// 0 10xxxxxxxx----- -> 1 10xxxxxxxx----- -> 110 0 0xxxxxxx
+// 0 0xxxxxxxx------ -> 1 0xxxxxxxx------ -> 111 0 0xxxxxxx
+
+//-------------------------------------------------
+// encode_fp - given a 32-bit signed input value
+// convert it to a signed 3.10 floating-point
+// value
+//-------------------------------------------------
+
+inline int16_t encode_fp(int32_t value)
+{
+ // handle overflows first
+ if (value < -32768)
+ return (7 << 10) | 0x000;
+ if (value > 32767)
+ return (7 << 10) | 0x3ff;
+
+ // we need to count the number of leading sign bits after the sign
+ // we can use count_leading_zeros if we invert negative values
+ int32_t scanvalue = value ^ (int32_t(value) >> 31);
+
+ // exponent is related to the number of leading bits starting from bit 14
+ int exponent = 7 - count_leading_zeros(scanvalue << 17);
+
+ // smallest exponent value allowed is 1
+ exponent = std::max(exponent, 1);
+
+ // mantissa
+ int32_t mantissa = value >> (exponent - 1);
+
+ // assemble into final form, inverting the sign
+ return ((exponent << 10) | (mantissa & 0x3ff)) ^ 0x200;
+}
+
+
+//-------------------------------------------------
+// decode_fp - given a 3.10 floating-point value,
+// convert it to a signed 16-bit value
+//-------------------------------------------------
+
+inline int16_t decode_fp(int16_t value)
+{
+ // invert the sign and the exponent
+ value ^= 0x1e00;
+
+ // shift mantissa up to 16 bits then apply inverted exponent
+ return int16_t(value << 6) >> bitfield(value, 10, 3);
+}
+
+
+//-------------------------------------------------
+// roundtrip_fp - compute the result of a round
+// trip through the encode/decode process above
+//-------------------------------------------------
+
+inline int16_t roundtrip_fp(int32_t value)
+{
+ // handle overflows first
+ if (value < -32768)
+ return -32768;
+ if (value > 32767)
+ return 32767;
+
+ // we need to count the number of leading sign bits after the sign
+ // we can use count_leading_zeros if we invert negative values
+ int32_t scanvalue = value ^ (int32_t(value) >> 31);
+
+ // exponent is related to the number of leading bits starting from bit 14
+ int exponent = 7 - count_leading_zeros(scanvalue << 17);
+
+ // smallest exponent value allowed is 1
+ exponent = std::max(exponent, 1);
+
+ // apply the shift back and forth to zero out bits that are lost
+ exponent -= 1;
+ int32_t mask = (1 << exponent) - 1;
+ return value & ~mask;
+}
+
+
+
+//*********************************************************
+// HELPER CLASSES
+//*********************************************************
+
+// various envelope states
+enum envelope_state : uint32_t
+{
+ EG_DEPRESS = 0, // OPLL only; set EG_HAS_DEPRESS to enable
+ EG_ATTACK = 1,
+ EG_DECAY = 2,
+ EG_SUSTAIN = 3,
+ EG_RELEASE = 4,
+ EG_REVERB = 5, // OPQ/OPZ only; set EG_HAS_REVERB to enable
+ EG_STATES = 6
+};
+
+// external I/O access classes
+enum access_class : uint32_t
+{
+ ACCESS_IO = 0,
+ ACCESS_ADPCM_A,
+ ACCESS_ADPCM_B,
+ ACCESS_PCM,
+ ACCESS_CLASSES
+};
+
+
+
+//*********************************************************
+// HELPER CLASSES
+//*********************************************************
+
+// ======================> ymfm_output
+
+// struct containing an array of output values
+template<int NumOutputs>
+struct ymfm_output
+{
+ // clear all outputs to 0
+ ymfm_output &clear()
+ {
+ for (uint32_t index = 0; index < NumOutputs; index++)
+ data[index] = 0;
+ return *this;
+ }
+
+ // clamp all outputs to a 16-bit signed value
+ ymfm_output &clamp16()
+ {
+ for (uint32_t index = 0; index < NumOutputs; index++)
+ data[index] = clamp(data[index], -32768, 32767);
+ return *this;
+ }
+
+ // run each output value through the floating-point processor
+ ymfm_output &roundtrip_fp()
+ {
+ for (uint32_t index = 0; index < NumOutputs; index++)
+ data[index] = ymfm::roundtrip_fp(data[index]);
+ return *this;
+ }
+
+ // internal state
+ int32_t data[NumOutputs];
+};
+
+
+// ======================> ymfm_wavfile
+
+// this class is a debugging helper that accumulates data and writes it to wav files
+template<int Channels>
+class ymfm_wavfile
+{
+public:
+ // construction
+ ymfm_wavfile(uint32_t samplerate = 44100) :
+ m_samplerate(samplerate)
+ {
+ }
+
+ // configuration
+ ymfm_wavfile &set_index(uint32_t index) { m_index = index; return *this; }
+ ymfm_wavfile &set_samplerate(uint32_t samplerate) { m_samplerate = samplerate; return *this; }
+
+ // destruction
+ ~ymfm_wavfile()
+ {
+ if (!m_buffer.empty())
+ {
+ // create file
+ char name[20];
+ snprintf(&name[0], sizeof(name), "wavlog-%02d.wav", m_index);
+ FILE *out = fopen(name, "wb");
+
+ // make the wav file header
+ uint8_t header[44];
+ memcpy(&header[0], "RIFF", 4);
+ *(uint32_t *)&header[4] = m_buffer.size() * 2 + 44 - 8;
+ memcpy(&header[8], "WAVE", 4);
+ memcpy(&header[12], "fmt ", 4);
+ *(uint32_t *)&header[16] = 16;
+ *(uint16_t *)&header[20] = 1;
+ *(uint16_t *)&header[22] = Channels;
+ *(uint32_t *)&header[24] = m_samplerate;
+ *(uint32_t *)&header[28] = m_samplerate * 2 * Channels;
+ *(uint16_t *)&header[32] = 2 * Channels;
+ *(uint16_t *)&header[34] = 16;
+ memcpy(&header[36], "data", 4);
+ *(uint32_t *)&header[40] = m_buffer.size() * 2 + 44 - 44;
+
+ // write header then data
+ fwrite(&header[0], 1, sizeof(header), out);
+ fwrite(&m_buffer[0], 2, m_buffer.size(), out);
+ fclose(out);
+ }
+ }
+
+ // add data to the file
+ template<int Outputs>
+ void add(ymfm_output<Outputs> output)
+ {
+ int16_t sum[Channels] = { 0 };
+ for (int index = 0; index < Outputs; index++)
+ sum[index % Channels] += output.data[index];
+ for (int index = 0; index < Channels; index++)
+ m_buffer.push_back(sum[index]);
+ }
+
+ // add data to the file, using a reference
+ template<int Outputs>
+ void add(ymfm_output<Outputs> output, ymfm_output<Outputs> const &ref)
+ {
+ int16_t sum[Channels] = { 0 };
+ for (int index = 0; index < Outputs; index++)
+ sum[index % Channels] += output.data[index] - ref.data[index];
+ for (int index = 0; index < Channels; index++)
+ m_buffer.push_back(sum[index]);
+ }
+
+private:
+ // internal state
+ uint32_t m_index;
+ uint32_t m_samplerate;
+ std::vector<int16_t> m_buffer;
+};
+
+
+// ======================> ymfm_saved_state
+
+// this class contains a managed vector of bytes that is used to save and
+// restore state
+class ymfm_saved_state
+{
+public:
+ // construction
+ ymfm_saved_state(std::vector<uint8_t> &buffer, bool saving) :
+ m_buffer(buffer),
+ m_offset(saving ? -1 : 0)
+ {
+ if (saving)
+ buffer.resize(0);
+ }
+
+ // are we saving or restoring?
+ bool saving() const { return (m_offset < 0); }
+
+ // generic save/restore
+ template<typename DataType>
+ void save_restore(DataType &data)
+ {
+ if (saving())
+ save(data);
+ else
+ restore(data);
+ }
+
+public:
+ // save data to the buffer
+ void save(bool &data) { write(data ? 1 : 0); }
+ void save(int8_t &data) { write(data); }
+ void save(uint8_t &data) { write(data); }
+ void save(int16_t &data) { write(uint8_t(data)).write(data >> 8); }
+ void save(uint16_t &data) { write(uint8_t(data)).write(data >> 8); }
+ void save(int32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); }
+ void save(uint32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); }
+ void save(envelope_state &data) { write(uint8_t(data)); }
+ template<typename DataType, int Count>
+ void save(DataType (&data)[Count]) { for (uint32_t index = 0; index < Count; index++) save(data[index]); }
+
+ // restore data from the buffer
+ void restore(bool &data) { data = read() ? true : false; }
+ void restore(int8_t &data) { data = read(); }
+ void restore(uint8_t &data) { data = read(); }
+ void restore(int16_t &data) { data = read(); data |= read() << 8; }
+ void restore(uint16_t &data) { data = read(); data |= read() << 8; }
+ void restore(int32_t &data) { data = read(); data |= read() << 8; data |= read() << 16; data |= read() << 24; }
+ void restore(uint32_t &data) { data = read(); data |= read() << 8; data |= read() << 16; data |= read() << 24; }
+ void restore(envelope_state &data) { data = envelope_state(read()); }
+ template<typename DataType, int Count>
+ void restore(DataType (&data)[Count]) { for (uint32_t index = 0; index < Count; index++) restore(data[index]); }
+
+ // internal helper
+ ymfm_saved_state &write(uint8_t data) { m_buffer.push_back(data); return *this; }
+ uint8_t read() { return (m_offset < int32_t(m_buffer.size())) ? m_buffer[m_offset++] : 0; }
+
+ // internal state
+ std::vector<uint8_t> &m_buffer;
+ int32_t m_offset;
+};
+
+
+
+//*********************************************************
+// INTERFACE CLASSES
+//*********************************************************
+
+// ======================> ymfm_engine_callbacks
+
+// this class represents functions in the engine that the ymfm_interface
+// needs to be able to call; it is represented here as a separate interface
+// that is independent of the actual engine implementation
+class ymfm_engine_callbacks
+{
+public:
+ virtual ~ymfm_engine_callbacks() = default;
+
+ // timer callback; called by the interface when a timer fires
+ virtual void engine_timer_expired(uint32_t tnum) = 0;
+
+ // check interrupts; called by the interface after synchronization
+ virtual void engine_check_interrupts() = 0;
+
+ // mode register write; called by the interface after synchronization
+ virtual void engine_mode_write(uint8_t data) = 0;
+};
+
+
+// ======================> ymfm_interface
+
+// this class represents the interface between the fm_engine and the outside
+// world; it provides hooks for timers, synchronization, and I/O
+class ymfm_interface
+{
+ // the engine is our friend
+ template<typename RegisterType> friend class fm_engine_base;
+
+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
+ // all prefixed with ymfm_ to reduce the likelihood of namespace collisions
+
+ //
+ // timing and synchronizaton
+ //
+
+ // the chip implementation calls this when a write happens to the mode
+ // register, which could affect timers and interrupts; our responsibility
+ // is to ensure the system is up to date before calling the engine's
+ // engine_mode_write() method
+ virtual void ymfm_sync_mode_write(uint8_t data) { m_engine->engine_mode_write(data); }
+
+ // the chip implementation calls this when the chip's status has changed,
+ // which may affect the interrupt state; our responsibility is to ensure
+ // the system is up to date before calling the engine's
+ // engine_check_interrupts() method
+ virtual void ymfm_sync_check_interrupts() { m_engine->engine_check_interrupts(); }
+
+ // the chip implementation calls this when one of the two internal timers
+ // has changed state; our responsibility is to arrange to call the engine's
+ // engine_timer_expired() method after the provided number of clocks; if
+ // duration_in_clocks is negative, we should cancel any outstanding timers
+ virtual void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) { }
+
+ // the chip implementation calls this to indicate that the chip should be
+ // considered in a busy state until the given number of clocks has passed;
+ // our responsibility is to compute and remember the ending time based on
+ // the chip's clock for later checking
+ virtual void ymfm_set_busy_end(uint32_t clocks) { }
+
+ // the chip implementation calls this to see if the chip is still currently
+ // is a busy state, as specified by a previous call to ymfm_set_busy_end();
+ // our responsibility is to compare the current time against the previously
+ // noted busy end time and return true if we haven't yet passed it
+ virtual bool ymfm_is_busy() { return false; }
+
+ //
+ // I/O functions
+ //
+
+ // the chip implementation calls this when the state of the IRQ signal has
+ // changed due to a status change; our responsibility is to respond as
+ // needed to the change in IRQ state, signaling any consumers
+ virtual void ymfm_update_irq(bool asserted) { }
+
+ // the chip implementation calls this whenever data is read from outside
+ // of the chip; our responsibility is to provide the data requested
+ virtual uint8_t ymfm_external_read(access_class type, uint32_t address) { return 0; }
+
+ // the chip implementation calls this whenever data is written outside
+ // of the chip; our responsibility is to pass the written data on to any consumers
+ virtual void ymfm_external_write(access_class type, uint32_t address, uint8_t data) { }
+
+protected:
+ // pointer to engine callbacks -- this is set directly by the engine at
+ // construction time
+ ymfm_engine_callbacks *m_engine;
+};
+
+}
+
+#endif // YMFM_H
diff --git a/3rdparty/ymfm/src/ymfm_adpcm.cpp b/3rdparty/ymfm/src/ymfm_adpcm.cpp
new file mode 100644
index 00000000000..4bc22beb2b0
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_adpcm.cpp
@@ -0,0 +1,807 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_adpcm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// ADPCM "A" REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// reset - reset the register state
+//-------------------------------------------------
+
+void adpcm_a_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+
+ // initialize the pans to on by default, and max instrument volume;
+ // some neogeo homebrews (for example ffeast) rely on this
+ m_regdata[0x08] = m_regdata[0x09] = m_regdata[0x0a] =
+ m_regdata[0x0b] = m_regdata[0x0c] = m_regdata[0x0d] = 0xdf;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void adpcm_a_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_regdata);
+}
+
+
+//*********************************************************
+// ADPCM "A" CHANNEL
+//*********************************************************
+
+//-------------------------------------------------
+// adpcm_a_channel - constructor
+//-------------------------------------------------
+
+adpcm_a_channel::adpcm_a_channel(adpcm_a_engine &owner, uint32_t choffs, uint32_t addrshift) :
+ m_choffs(choffs),
+ m_address_shift(addrshift),
+ m_playing(0),
+ m_curnibble(0),
+ m_curbyte(0),
+ m_curaddress(0),
+ m_accumulator(0),
+ m_step_index(0),
+ m_regs(owner.regs()),
+ m_owner(owner)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the channel state
+//-------------------------------------------------
+
+void adpcm_a_channel::reset()
+{
+ m_playing = 0;
+ m_curnibble = 0;
+ m_curbyte = 0;
+ m_curaddress = 0;
+ m_accumulator = 0;
+ m_step_index = 0;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void adpcm_a_channel::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_playing);
+ state.save_restore(m_curnibble);
+ state.save_restore(m_curbyte);
+ state.save_restore(m_curaddress);
+ state.save_restore(m_accumulator);
+ state.save_restore(m_step_index);
+}
+
+
+//-------------------------------------------------
+// keyonoff - signal key on/off
+//-------------------------------------------------
+
+void adpcm_a_channel::keyonoff(bool on)
+{
+ // QUESTION: repeated key ons restart the sample?
+ m_playing = on;
+ if (m_playing)
+ {
+ m_curaddress = m_regs.ch_start(m_choffs) << m_address_shift;
+ m_curnibble = 0;
+ m_curbyte = 0;
+ m_accumulator = 0;
+ m_step_index = 0;
+
+ // don't log masked channels
+ if (((debug::GLOBAL_ADPCM_A_CHANNEL_MASK >> m_choffs) & 1) != 0)
+ debug::log_keyon("KeyOn ADPCM-A%d: pan=%d%d start=%04X end=%04X level=%02X\n",
+ m_choffs,
+ m_regs.ch_pan_left(m_choffs),
+ m_regs.ch_pan_right(m_choffs),
+ m_regs.ch_start(m_choffs),
+ m_regs.ch_end(m_choffs),
+ m_regs.ch_instrument_level(m_choffs));
+ }
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+bool adpcm_a_channel::clock()
+{
+ // if not playing, just output 0
+ if (m_playing == 0)
+ {
+ m_accumulator = 0;
+ return false;
+ }
+
+ // if we're about to read nibble 0, fetch the data
+ uint8_t data;
+ if (m_curnibble == 0)
+ {
+ // stop when we hit the end address; apparently only low 20 bits are used for
+ // comparison on the YM2610: this affects sample playback in some games, for
+ // example twinspri character select screen music will skip some samples if
+ // this is not correct
+ //
+ // note also: end address is inclusive, so wait until we are about to fetch
+ // the sample just after the end before stopping; this is needed for nitd's
+ // jump sound, for example
+ uint32_t end = (m_regs.ch_end(m_choffs) + 1) << m_address_shift;
+ if (((m_curaddress ^ end) & 0xfffff) == 0)
+ {
+ m_playing = m_accumulator = 0;
+ return true;
+ }
+
+ m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_A, m_curaddress++);
+ data = m_curbyte >> 4;
+ m_curnibble = 1;
+ }
+
+ // otherwise just extract from the previosuly-fetched byte
+ else
+ {
+ data = m_curbyte & 0xf;
+ m_curnibble = 0;
+ }
+
+ // compute the ADPCM delta
+ static uint16_t const s_steps[49] =
+ {
+ 16, 17, 19, 21, 23, 25, 28,
+ 31, 34, 37, 41, 45, 50, 55,
+ 60, 66, 73, 80, 88, 97, 107,
+ 118, 130, 143, 157, 173, 190, 209,
+ 230, 253, 279, 307, 337, 371, 408,
+ 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552
+ };
+ int32_t delta = (2 * bitfield(data, 0, 3) + 1) * s_steps[m_step_index] / 8;
+ if (bitfield(data, 3))
+ delta = -delta;
+
+ // the 12-bit accumulator wraps on the ym2610 and ym2608 (like the msm5205)
+ m_accumulator = (m_accumulator + delta) & 0xfff;
+
+ // adjust ADPCM step
+ static int8_t const s_step_inc[8] = { -1, -1, -1, -1, 2, 5, 7, 9 };
+ m_step_index = clamp(m_step_index + s_step_inc[bitfield(data, 0, 3)], 0, 48);
+
+ return false;
+}
+
+
+//-------------------------------------------------
+// output - return the computed output value, with
+// panning applied
+//-------------------------------------------------
+
+template<int NumOutputs>
+void adpcm_a_channel::output(ymfm_output<NumOutputs> &output) const
+{
+ // volume combines instrument and total levels
+ int vol = (m_regs.ch_instrument_level(m_choffs) ^ 0x1f) + (m_regs.total_level() ^ 0x3f);
+
+ // if combined is maximum, don't add to outputs
+ if (vol >= 63)
+ return;
+
+ // convert into a shift and a multiplier
+ // QUESTION: verify this from other sources
+ int8_t mul = 15 - (vol & 7);
+ uint8_t shift = 4 + 1 + (vol >> 3);
+
+ // m_accumulator is a 12-bit value; shift up to sign-extend;
+ // the downshift is incorporated into 'shift'
+ int16_t value = ((int16_t(m_accumulator << 4) * mul) >> shift) & ~3;
+
+ // apply to left/right as appropriate
+ if (NumOutputs == 1 || m_regs.ch_pan_left(m_choffs))
+ output.data[0] += value;
+ if (NumOutputs > 1 && m_regs.ch_pan_right(m_choffs))
+ output.data[1] += value;
+}
+
+
+
+//*********************************************************
+// ADPCM "A" ENGINE
+//*********************************************************
+
+//-------------------------------------------------
+// adpcm_a_engine - constructor
+//-------------------------------------------------
+
+adpcm_a_engine::adpcm_a_engine(ymfm_interface &intf, uint32_t addrshift) :
+ m_intf(intf)
+{
+ // create the channels
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ m_channel[chnum] = std::make_unique<adpcm_a_channel>(*this, chnum, addrshift);
+}
+
+
+//-------------------------------------------------
+// reset - reset the engine state
+//-------------------------------------------------
+
+void adpcm_a_engine::reset()
+{
+ // reset register state
+ m_regs.reset();
+
+ // reset each channel
+ for (auto &chan : m_channel)
+ chan->reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void adpcm_a_engine::save_restore(ymfm_saved_state &state)
+{
+ // save register state
+ m_regs.save_restore(state);
+
+ // save channel state
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ m_channel[chnum]->save_restore(state);
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+uint32_t adpcm_a_engine::clock(uint32_t chanmask)
+{
+ // clock each channel, setting a bit in result if it finished
+ uint32_t result = 0;
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ if (m_channel[chnum]->clock())
+ result |= 1 << chnum;
+
+ // return the bitmask of completed samples
+ return result;
+}
+
+
+//-------------------------------------------------
+// update - master update function
+//-------------------------------------------------
+
+template<int NumOutputs>
+void adpcm_a_engine::output(ymfm_output<NumOutputs> &output, uint32_t chanmask)
+{
+ // mask out some channels for debug purposes
+ chanmask &= debug::GLOBAL_ADPCM_A_CHANNEL_MASK;
+
+ // compute the output of each channel
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ m_channel[chnum]->output(output);
+}
+
+template void adpcm_a_engine::output<1>(ymfm_output<1> &output, uint32_t chanmask);
+template void adpcm_a_engine::output<2>(ymfm_output<2> &output, uint32_t chanmask);
+
+
+//-------------------------------------------------
+// write - handle writes to the ADPCM-A registers
+//-------------------------------------------------
+
+void adpcm_a_engine::write(uint32_t regnum, uint8_t data)
+{
+ // store the raw value to the register array;
+ // most writes are passive, consumed only when needed
+ m_regs.write(regnum, data);
+
+ // actively handle writes to the control register
+ if (regnum == 0x00)
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(data, chnum))
+ m_channel[chnum]->keyonoff(bitfield(~data, 7));
+}
+
+
+
+//*********************************************************
+// ADPCM "B" REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// reset - reset the register state
+//-------------------------------------------------
+
+void adpcm_b_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+
+ // default limit to wide open
+ m_regdata[0x0c] = m_regdata[0x0d] = 0xff;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void adpcm_b_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_regdata);
+}
+
+
+
+//*********************************************************
+// ADPCM "B" CHANNEL
+//*********************************************************
+
+//-------------------------------------------------
+// adpcm_b_channel - constructor
+//-------------------------------------------------
+
+adpcm_b_channel::adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift) :
+ m_address_shift(addrshift),
+ m_status(STATUS_BRDY),
+ m_curnibble(0),
+ m_curbyte(0),
+ m_dummy_read(0),
+ m_position(0),
+ m_curaddress(0),
+ m_accumulator(0),
+ m_prev_accum(0),
+ m_adpcm_step(STEP_MIN),
+ m_regs(owner.regs()),
+ m_owner(owner)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the channel state
+//-------------------------------------------------
+
+void adpcm_b_channel::reset()
+{
+ m_status = STATUS_BRDY;
+ m_curnibble = 0;
+ m_curbyte = 0;
+ m_dummy_read = 0;
+ m_position = 0;
+ m_curaddress = 0;
+ m_accumulator = 0;
+ m_prev_accum = 0;
+ m_adpcm_step = STEP_MIN;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void adpcm_b_channel::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_status);
+ state.save_restore(m_curnibble);
+ state.save_restore(m_curbyte);
+ state.save_restore(m_dummy_read);
+ state.save_restore(m_position);
+ state.save_restore(m_curaddress);
+ state.save_restore(m_accumulator);
+ state.save_restore(m_prev_accum);
+ state.save_restore(m_adpcm_step);
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+void adpcm_b_channel::clock()
+{
+ // only process if active and not recording (which we don't support)
+ if (!m_regs.execute() || m_regs.record() || (m_status & STATUS_PLAYING) == 0)
+ {
+ m_status &= ~STATUS_PLAYING;
+ return;
+ }
+
+ // otherwise, advance the step
+ uint32_t position = m_position + m_regs.delta_n();
+ m_position = uint16_t(position);
+ if (position < 0x10000)
+ return;
+
+ // if we're about to process nibble 0, fetch sample
+ if (m_curnibble == 0)
+ {
+ // playing from RAM/ROM
+ if (m_regs.external())
+ m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress);
+ }
+
+ // extract the nibble from our current byte
+ uint8_t data = uint8_t(m_curbyte << (4 * m_curnibble)) >> 4;
+ m_curnibble ^= 1;
+
+ // we just processed the last nibble
+ if (m_curnibble == 0)
+ {
+ // if playing from RAM/ROM, check the end/limit address or advance
+ if (m_regs.external())
+ {
+ // handle the sample end, either repeating or stopping
+ if (at_end())
+ {
+ // if repeating, go back to the start
+ if (m_regs.repeat())
+ load_start();
+
+ // otherwise, done; set the EOS bit
+ else
+ {
+ m_accumulator = 0;
+ m_prev_accum = 0;
+ m_status = (m_status & ~STATUS_PLAYING) | STATUS_EOS;
+ debug::log_keyon("%s\n", "ADPCM EOS");
+ return;
+ }
+ }
+
+ // wrap at the limit address
+ else if (at_limit())
+ m_curaddress = 0;
+
+ // otherwise, advance the current address
+ else
+ {
+ m_curaddress++;
+ m_curaddress &= 0xffffff;
+ }
+ }
+
+ // if CPU-driven, copy the next byte and request more
+ else
+ {
+ m_curbyte = m_regs.cpudata();
+ m_status |= STATUS_BRDY;
+ }
+ }
+
+ // remember previous value for interpolation
+ m_prev_accum = m_accumulator;
+
+ // forecast to next forecast: 1/8, 3/8, 5/8, 7/8, 9/8, 11/8, 13/8, 15/8
+ int32_t delta = (2 * bitfield(data, 0, 3) + 1) * m_adpcm_step / 8;
+ if (bitfield(data, 3))
+ delta = -delta;
+
+ // add and clamp to 16 bits
+ m_accumulator = clamp(m_accumulator + delta, -32768, 32767);
+
+ // scale the ADPCM step: 0.9, 0.9, 0.9, 0.9, 1.2, 1.6, 2.0, 2.4
+ static uint8_t const s_step_scale[8] = { 57, 57, 57, 57, 77, 102, 128, 153 };
+ m_adpcm_step = clamp((m_adpcm_step * s_step_scale[bitfield(data, 0, 3)]) / 64, STEP_MIN, STEP_MAX);
+}
+
+
+//-------------------------------------------------
+// output - return the computed output value, with
+// panning applied
+//-------------------------------------------------
+
+template<int NumOutputs>
+void adpcm_b_channel::output(ymfm_output<NumOutputs> &output, uint32_t rshift) const
+{
+ // mask out some channels for debug purposes
+ if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) == 0)
+ return;
+
+ // do a linear interpolation between samples
+ int32_t result = (m_prev_accum * int32_t((m_position ^ 0xffff) + 1) + m_accumulator * int32_t(m_position)) >> 16;
+
+ // apply volume (level) in a linear fashion and reduce
+ result = (result * int32_t(m_regs.level())) >> (8 + rshift);
+
+ // apply to left/right
+ if (NumOutputs == 1 || m_regs.pan_left())
+ output.data[0] += result;
+ if (NumOutputs > 1 && m_regs.pan_right())
+ output.data[1] += result;
+}
+
+
+//-------------------------------------------------
+// read - handle special register reads
+//-------------------------------------------------
+
+uint8_t adpcm_b_channel::read(uint32_t regnum)
+{
+ uint8_t result = 0;
+
+ // register 8 reads over the bus under some conditions
+ if (regnum == 0x08 && !m_regs.execute() && !m_regs.record() && m_regs.external())
+ {
+ // two dummy reads are consumed first
+ if (m_dummy_read != 0)
+ {
+ load_start();
+ m_dummy_read--;
+ }
+
+ // read the data
+ else
+ {
+ // read from outside of the chip
+ result = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++);
+
+ // did we hit the end? if so, signal EOS
+ if (at_end())
+ {
+ m_status = STATUS_EOS | STATUS_BRDY;
+ debug::log_keyon("%s\n", "ADPCM EOS");
+ }
+ else
+ {
+ // signal ready
+ m_status = STATUS_BRDY;
+ }
+
+ // wrap at the limit address
+ if (at_limit())
+ m_curaddress = 0;
+ }
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write - handle special register writes
+//-------------------------------------------------
+
+void adpcm_b_channel::write(uint32_t regnum, uint8_t value)
+{
+ // register 0 can do a reset; also use writes here to reset the
+ // dummy read counter
+ if (regnum == 0x00)
+ {
+ if (m_regs.execute())
+ {
+ load_start();
+
+ // don't log masked channels
+ if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) != 0)
+ debug::log_keyon("KeyOn ADPCM-B: rep=%d spk=%d pan=%d%d dac=%d 8b=%d rom=%d ext=%d rec=%d start=%04X end=%04X pre=%04X dn=%04X lvl=%02X lim=%04X\n",
+ m_regs.repeat(),
+ m_regs.speaker(),
+ m_regs.pan_left(),
+ m_regs.pan_right(),
+ m_regs.dac_enable(),
+ m_regs.dram_8bit(),
+ m_regs.rom_ram(),
+ m_regs.external(),
+ m_regs.record(),
+ m_regs.start(),
+ m_regs.end(),
+ m_regs.prescale(),
+ m_regs.delta_n(),
+ m_regs.level(),
+ m_regs.limit());
+ }
+ else
+ m_status &= ~STATUS_EOS;
+ if (m_regs.resetflag())
+ reset();
+ if (m_regs.external())
+ m_dummy_read = 2;
+ }
+
+ // register 8 writes over the bus under some conditions
+ else if (regnum == 0x08)
+ {
+ // if writing from the CPU during execute, clear the ready flag
+ if (m_regs.execute() && !m_regs.record() && !m_regs.external())
+ m_status &= ~STATUS_BRDY;
+
+ // if writing during "record", pass through as data
+ else if (!m_regs.execute() && m_regs.record() && m_regs.external())
+ {
+ // clear out dummy reads and set start address
+ if (m_dummy_read != 0)
+ {
+ load_start();
+ m_dummy_read = 0;
+ }
+
+ // did we hit the end? if so, signal EOS
+ if (at_end())
+ {
+ debug::log_keyon("%s\n", "ADPCM EOS");
+ m_status = STATUS_EOS | STATUS_BRDY;
+ }
+
+ // otherwise, write the data and signal ready
+ else
+ {
+ m_owner.intf().ymfm_external_write(ACCESS_ADPCM_B, m_curaddress++, value);
+ m_status = STATUS_BRDY;
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// address_shift - compute the current address
+// shift amount based on register settings
+//-------------------------------------------------
+
+uint32_t adpcm_b_channel::address_shift() const
+{
+ // if a constant address shift, just provide that
+ if (m_address_shift != 0)
+ return m_address_shift;
+
+ // if ROM or 8-bit DRAM, shift is 5 bits
+ if (m_regs.rom_ram())
+ return 5;
+ if (m_regs.dram_8bit())
+ return 5;
+
+ // otherwise, shift is 2 bits
+ return 2;
+}
+
+
+//-------------------------------------------------
+// load_start - load the start address and
+// initialize the state
+//-------------------------------------------------
+
+void adpcm_b_channel::load_start()
+{
+ m_status = (m_status & ~STATUS_EOS) | STATUS_PLAYING;
+ m_curaddress = m_regs.external() ? (m_regs.start() << address_shift()) : 0;
+ m_curnibble = 0;
+ m_curbyte = 0;
+ m_position = 0;
+ m_accumulator = 0;
+ m_prev_accum = 0;
+ m_adpcm_step = STEP_MIN;
+}
+
+
+
+//*********************************************************
+// ADPCM "B" ENGINE
+//*********************************************************
+
+//-------------------------------------------------
+// adpcm_b_engine - constructor
+//-------------------------------------------------
+
+adpcm_b_engine::adpcm_b_engine(ymfm_interface &intf, uint32_t addrshift) :
+ m_intf(intf)
+{
+ // create the channel (only one supported for now, but leaving possibilities open)
+ m_channel = std::make_unique<adpcm_b_channel>(*this, addrshift);
+}
+
+
+//-------------------------------------------------
+// reset - reset the engine state
+//-------------------------------------------------
+
+void adpcm_b_engine::reset()
+{
+ // reset registers
+ m_regs.reset();
+
+ // reset each channel
+ m_channel->reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void adpcm_b_engine::save_restore(ymfm_saved_state &state)
+{
+ // save our state
+ m_regs.save_restore(state);
+
+ // save channel state
+ m_channel->save_restore(state);
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+void adpcm_b_engine::clock()
+{
+ // clock each channel, setting a bit in result if it finished
+ m_channel->clock();
+}
+
+
+//-------------------------------------------------
+// output - master output function
+//-------------------------------------------------
+
+template<int NumOutputs>
+void adpcm_b_engine::output(ymfm_output<NumOutputs> &output, uint32_t rshift)
+{
+ // compute the output of each channel
+ m_channel->output(output, rshift);
+}
+
+template void adpcm_b_engine::output<1>(ymfm_output<1> &output, uint32_t rshift);
+template void adpcm_b_engine::output<2>(ymfm_output<2> &output, uint32_t rshift);
+
+
+//-------------------------------------------------
+// write - handle writes to the ADPCM-B registers
+//-------------------------------------------------
+
+void adpcm_b_engine::write(uint32_t regnum, uint8_t data)
+{
+ // store the raw value to the register array;
+ // most writes are passive, consumed only when needed
+ m_regs.write(regnum, data);
+
+ // let the channel handle any special writes
+ m_channel->write(regnum, data);
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_adpcm.h b/3rdparty/ymfm/src/ymfm_adpcm.h
new file mode 100644
index 00000000000..d74e24f2770
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_adpcm.h
@@ -0,0 +1,411 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_ADPCM_H
+#define YMFM_ADPCM_H
+
+#pragma once
+
+#include "ymfm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// INTERFACE CLASSES
+//*********************************************************
+
+// forward declarations
+class adpcm_a_engine;
+class adpcm_b_engine;
+
+
+// ======================> adpcm_a_registers
+
+//
+// ADPCM-A register map:
+//
+// System-wide registers:
+// 00 x------- Dump (disable=1) or keyon (0) control
+// --xxxxxx Mask of channels to dump or keyon
+// 01 --xxxxxx Total level
+// 02 xxxxxxxx Test register
+// 08-0D x------- Pan left
+// -x------ Pan right
+// ---xxxxx Instrument level
+// 10-15 xxxxxxxx Start address (low)
+// 18-1D xxxxxxxx Start address (high)
+// 20-25 xxxxxxxx End address (low)
+// 28-2D xxxxxxxx End address (high)
+//
+class adpcm_a_registers
+{
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = 2;
+ static constexpr uint32_t CHANNELS = 6;
+ static constexpr uint32_t REGISTERS = 0x30;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+
+ // constructor
+ adpcm_a_registers() { }
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ return chnum;
+ }
+
+ // direct read/write access
+ void write(uint32_t index, uint8_t data) { m_regdata[index] = data; }
+
+ // system-wide registers
+ uint32_t dump() const { return bitfield(m_regdata[0x00], 7); }
+ uint32_t dump_mask() const { return bitfield(m_regdata[0x00], 0, 6); }
+ uint32_t total_level() const { return bitfield(m_regdata[0x01], 0, 6); }
+ uint32_t test() const { return m_regdata[0x02]; }
+
+ // per-channel registers
+ uint32_t ch_pan_left(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 7); }
+ uint32_t ch_pan_right(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 6); }
+ uint32_t ch_instrument_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 0, 5); }
+ uint32_t ch_start(uint32_t choffs) const { return m_regdata[choffs + 0x10] | (m_regdata[choffs + 0x18] << 8); }
+ uint32_t ch_end(uint32_t choffs) const { return m_regdata[choffs + 0x20] | (m_regdata[choffs + 0x28] << 8); }
+
+ // per-channel writes
+ void write_start(uint32_t choffs, uint32_t address)
+ {
+ write(choffs + 0x10, address);
+ write(choffs + 0x18, address >> 8);
+ }
+ void write_end(uint32_t choffs, uint32_t address)
+ {
+ write(choffs + 0x20, address);
+ write(choffs + 0x28, address >> 8);
+ }
+
+private:
+ // internal state
+ uint8_t m_regdata[REGISTERS]; // register data
+};
+
+
+// ======================> adpcm_a_channel
+
+class adpcm_a_channel
+{
+public:
+ // constructor
+ adpcm_a_channel(adpcm_a_engine &owner, uint32_t choffs, uint32_t addrshift);
+
+ // reset the channel state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // signal key on/off
+ void keyonoff(bool on);
+
+ // master clockingfunction
+ bool clock();
+
+ // return the computed output value, with panning applied
+ template<int NumOutputs>
+ void output(ymfm_output<NumOutputs> &output) const;
+
+private:
+ // internal state
+ uint32_t const m_choffs; // channel offset
+ uint32_t const m_address_shift; // address bits shift-left
+ uint32_t m_playing; // currently playing?
+ uint32_t m_curnibble; // index of the current nibble
+ uint32_t m_curbyte; // current byte of data
+ uint32_t m_curaddress; // current address
+ int32_t m_accumulator; // accumulator
+ int32_t m_step_index; // index in the stepping table
+ adpcm_a_registers &m_regs; // reference to registers
+ adpcm_a_engine &m_owner; // reference to our owner
+};
+
+
+// ======================> adpcm_a_engine
+
+class adpcm_a_engine
+{
+public:
+ static constexpr int CHANNELS = adpcm_a_registers::CHANNELS;
+
+ // constructor
+ adpcm_a_engine(ymfm_interface &intf, uint32_t addrshift);
+
+ // reset our status
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // master clocking function
+ uint32_t clock(uint32_t chanmask);
+
+ // compute sum of channel outputs
+ template<int NumOutputs>
+ void output(ymfm_output<NumOutputs> &output, uint32_t chanmask);
+
+ // write to the ADPCM-A registers
+ void write(uint32_t regnum, uint8_t data);
+
+ // set the start/end address for a channel (for hardcoded YM2608 percussion)
+ void set_start_end(uint8_t chnum, uint16_t start, uint16_t end)
+ {
+ uint32_t choffs = adpcm_a_registers::channel_offset(chnum);
+ m_regs.write_start(choffs, start);
+ m_regs.write_end(choffs, end);
+ }
+
+ // return a reference to our interface
+ ymfm_interface &intf() { return m_intf; }
+
+ // return a reference to our registers
+ adpcm_a_registers &regs() { return m_regs; }
+
+private:
+ // internal state
+ ymfm_interface &m_intf; // reference to the interface
+ std::unique_ptr<adpcm_a_channel> m_channel[CHANNELS]; // array of channels
+ adpcm_a_registers m_regs; // registers
+};
+
+
+// ======================> adpcm_b_registers
+
+//
+// ADPCM-B register map:
+//
+// System-wide registers:
+// 00 x------- Start of synthesis/analysis
+// -x------ Record
+// --x----- External/manual driving
+// ---x---- Repeat playback
+// ----x--- Speaker off
+// -------x Reset
+// 01 x------- Pan left
+// -x------ Pan right
+// ----x--- Start conversion
+// -----x-- DAC enable
+// ------x- DRAM access (1=8-bit granularity; 0=1-bit)
+// -------x RAM/ROM (1=ROM, 0=RAM)
+// 02 xxxxxxxx Start address (low)
+// 03 xxxxxxxx Start address (high)
+// 04 xxxxxxxx End address (low)
+// 05 xxxxxxxx End address (high)
+// 06 xxxxxxxx Prescale value (low)
+// 07 -----xxx Prescale value (high)
+// 08 xxxxxxxx CPU data/buffer
+// 09 xxxxxxxx Delta-N frequency scale (low)
+// 0a xxxxxxxx Delta-N frequency scale (high)
+// 0b xxxxxxxx Level control
+// 0c xxxxxxxx Limit address (low)
+// 0d xxxxxxxx Limit address (high)
+// 0e xxxxxxxx DAC data [YM2608/10]
+// 0f xxxxxxxx PCM data [YM2608/10]
+// 0e xxxxxxxx DAC data high [Y8950]
+// 0f xx------ DAC data low [Y8950]
+// 10 -----xxx DAC data exponent [Y8950]
+//
+class adpcm_b_registers
+{
+public:
+ // constants
+ static constexpr uint32_t REGISTERS = 0x11;
+
+ // constructor
+ adpcm_b_registers() { }
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // direct read/write access
+ void write(uint32_t index, uint8_t data) { m_regdata[index] = data; }
+
+ // system-wide registers
+ uint32_t execute() const { return bitfield(m_regdata[0x00], 7); }
+ uint32_t record() const { return bitfield(m_regdata[0x00], 6); }
+ uint32_t external() const { return bitfield(m_regdata[0x00], 5); }
+ uint32_t repeat() const { return bitfield(m_regdata[0x00], 4); }
+ uint32_t speaker() const { return bitfield(m_regdata[0x00], 3); }
+ uint32_t resetflag() const { return bitfield(m_regdata[0x00], 0); }
+ uint32_t pan_left() const { return bitfield(m_regdata[0x01], 7); }
+ uint32_t pan_right() const { return bitfield(m_regdata[0x01], 6); }
+ uint32_t start_conversion() const { return bitfield(m_regdata[0x01], 3); }
+ uint32_t dac_enable() const { return bitfield(m_regdata[0x01], 2); }
+ uint32_t dram_8bit() const { return bitfield(m_regdata[0x01], 1); }
+ uint32_t rom_ram() const { return bitfield(m_regdata[0x01], 0); }
+ uint32_t start() const { return m_regdata[0x02] | (m_regdata[0x03] << 8); }
+ uint32_t end() const { return m_regdata[0x04] | (m_regdata[0x05] << 8); }
+ uint32_t prescale() const { return m_regdata[0x06] | (bitfield(m_regdata[0x07], 0, 3) << 8); }
+ uint32_t cpudata() const { return m_regdata[0x08]; }
+ uint32_t delta_n() const { return m_regdata[0x09] | (m_regdata[0x0a] << 8); }
+ uint32_t level() const { return m_regdata[0x0b]; }
+ uint32_t limit() const { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); }
+ uint32_t dac() const { return m_regdata[0x0e]; }
+ uint32_t pcm() const { return m_regdata[0x0f]; }
+
+private:
+ // internal state
+ uint8_t m_regdata[REGISTERS]; // register data
+};
+
+
+// ======================> adpcm_b_channel
+
+class adpcm_b_channel
+{
+ static constexpr int32_t STEP_MIN = 127;
+ static constexpr int32_t STEP_MAX = 24576;
+
+public:
+ static constexpr uint8_t STATUS_EOS = 0x01;
+ static constexpr uint8_t STATUS_BRDY = 0x02;
+ static constexpr uint8_t STATUS_PLAYING = 0x04;
+
+ // constructor
+ adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift);
+
+ // reset the channel state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // signal key on/off
+ void keyonoff(bool on);
+
+ // master clocking function
+ void clock();
+
+ // return the computed output value, with panning applied
+ template<int NumOutputs>
+ void output(ymfm_output<NumOutputs> &output, uint32_t rshift) const;
+
+ // return the status register
+ uint8_t status() const { return m_status; }
+
+ // handle special register reads
+ uint8_t read(uint32_t regnum);
+
+ // handle special register writes
+ void write(uint32_t regnum, uint8_t value);
+
+private:
+ // helper - return the current address shift
+ uint32_t address_shift() const;
+
+ // load the start address
+ void load_start();
+
+ // limit checker; stops at the last byte of the chunk described by address_shift()
+ bool at_limit() const { return (m_curaddress == (((m_regs.limit() + 1) << address_shift()) - 1)); }
+
+ // end checker; stops at the last byte of the chunk described by address_shift()
+ bool at_end() const { return (m_curaddress == (((m_regs.end() + 1) << address_shift()) - 1)); }
+
+ // internal state
+ uint32_t const m_address_shift; // address bits shift-left
+ uint32_t m_status; // currently playing?
+ uint32_t m_curnibble; // index of the current nibble
+ uint32_t m_curbyte; // current byte of data
+ uint32_t m_dummy_read; // dummy read tracker
+ uint32_t m_position; // current fractional position
+ uint32_t m_curaddress; // current address
+ int32_t m_accumulator; // accumulator
+ int32_t m_prev_accum; // previous accumulator (for linear interp)
+ int32_t m_adpcm_step; // next forecast
+ adpcm_b_registers &m_regs; // reference to registers
+ adpcm_b_engine &m_owner; // reference to our owner
+};
+
+
+// ======================> adpcm_b_engine
+
+class adpcm_b_engine
+{
+public:
+ // constructor
+ adpcm_b_engine(ymfm_interface &intf, uint32_t addrshift = 0);
+
+ // reset our status
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // master clocking function
+ void clock();
+
+ // compute sum of channel outputs
+ template<int NumOutputs>
+ void output(ymfm_output<NumOutputs> &output, uint32_t rshift);
+
+ // read from the ADPCM-B registers
+ uint32_t read(uint32_t regnum) { return m_channel->read(regnum); }
+
+ // write to the ADPCM-B registers
+ void write(uint32_t regnum, uint8_t data);
+
+ // status
+ uint8_t status() const { return m_channel->status(); }
+
+ // return a reference to our interface
+ ymfm_interface &intf() { return m_intf; }
+
+ // return a reference to our registers
+ adpcm_b_registers &regs() { return m_regs; }
+
+private:
+ // internal state
+ ymfm_interface &m_intf; // reference to our interface
+ std::unique_ptr<adpcm_b_channel> m_channel; // channel pointer
+ adpcm_b_registers m_regs; // registers
+};
+
+}
+
+#endif // YMFM_ADPCM_H
diff --git a/3rdparty/ymfm/src/ymfm_fm.h b/3rdparty/ymfm/src/ymfm_fm.h
new file mode 100644
index 00000000000..d40409fddfd
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_fm.h
@@ -0,0 +1,463 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_FM_H
+#define YMFM_FM_H
+
+#pragma once
+
+#define YMFM_DEBUG_LOG_WAVFILES (0)
+
+namespace ymfm
+{
+
+//*********************************************************
+// GLOBAL ENUMERATORS
+//*********************************************************
+
+// three different keyon sources; actual keyon is an OR over all of these
+enum keyon_type : uint32_t
+{
+ KEYON_NORMAL = 0,
+ KEYON_RHYTHM = 1,
+ KEYON_CSM = 2
+};
+
+
+
+//*********************************************************
+// CORE IMPLEMENTATION
+//*********************************************************
+
+// ======================> opdata_cache
+
+// this class holds data that is computed once at the start of clocking
+// and remains static during subsequent sound generation
+struct opdata_cache
+{
+ // set phase_step to this value to recalculate it each sample; needed
+ // in the case of PM LFO changes
+ static constexpr uint32_t PHASE_STEP_DYNAMIC = 1;
+
+ uint16_t const *waveform; // base of sine table
+ uint32_t phase_step; // phase step, or PHASE_STEP_DYNAMIC if PM is active
+ uint32_t total_level; // total level * 8 + KSL
+ uint32_t block_freq; // raw block frequency value (used to compute phase_step)
+ int32_t detune; // detuning value (used to compute phase_step)
+ uint32_t multiple; // multiple value (x.1, used to compute phase_step)
+ uint32_t eg_sustain; // sustain level, shifted up to envelope values
+ uint8_t eg_rate[EG_STATES]; // envelope rate, including KSR
+ uint8_t eg_shift = 0; // envelope shift amount
+};
+
+
+// ======================> fm_registers_base
+
+// base class for family-specific register classes; this provides a few
+// constants, common defaults, and helpers, but mostly each derived class is
+// responsible for defining all commonly-called methods
+class fm_registers_base
+{
+public:
+ // this value is returned from the write() function for rhythm channels
+ static constexpr uint32_t RHYTHM_CHANNEL = 0xff;
+
+ // this is the size of a full sin waveform
+ static constexpr uint32_t WAVEFORM_LENGTH = 0x400;
+
+ //
+ // the following constants need to be defined per family:
+ // uint32_t OUTPUTS: The number of outputs exposed (1-4)
+ // uint32_t CHANNELS: The number of channels on the chip
+ // uint32_t ALL_CHANNELS: A bitmask of all channels
+ // uint32_t OPERATORS: The number of operators on the chip
+ // uint32_t WAVEFORMS: The number of waveforms offered
+ // uint32_t REGISTERS: The number of 8-bit registers allocated
+ // uint32_t DEFAULT_PRESCALE: The starting clock prescale
+ // uint32_t EG_CLOCK_DIVIDER: The clock divider of the envelope generator
+ // uint32_t CSM_TRIGGER_MASK: Mask of channels to trigger in CSM mode
+ // uint32_t REG_MODE: The address of the "mode" register controlling timers
+ // uint8_t STATUS_TIMERA: Status bit to set when timer A fires
+ // uint8_t STATUS_TIMERB: Status bit to set when tiemr B fires
+ // uint8_t STATUS_BUSY: Status bit to set when the chip is busy
+ // uint8_t STATUS_IRQ: Status bit to set when an IRQ is signalled
+ //
+ // the following constants are uncommon:
+ // bool DYNAMIC_OPS: True if ops/channel can be changed at runtime (OPL3+)
+ // bool EG_HAS_DEPRESS: True if the chip has a DP ("depress"?) envelope stage (OPLL)
+ // bool EG_HAS_REVERB: True if the chip has a faux reverb envelope stage (OPQ/OPZ)
+ // bool EG_HAS_SSG: True if the chip has SSG envelope support (OPN)
+ // bool MODULATOR_DELAY: True if the modulator is delayed by 1 sample (OPL pre-OPL3)
+ //
+ static constexpr bool DYNAMIC_OPS = false;
+ static constexpr bool EG_HAS_DEPRESS = false;
+ static constexpr bool EG_HAS_REVERB = false;
+ static constexpr bool EG_HAS_SSG = false;
+ static constexpr bool MODULATOR_DELAY = false;
+
+ // system-wide register defaults
+ uint32_t status_mask() const { return 0; } // OPL only
+ uint32_t irq_reset() const { return 0; } // OPL only
+ uint32_t noise_enable() const { return 0; } // OPM only
+ uint32_t rhythm_enable() const { return 0; } // OPL only
+
+ // per-operator register defaults
+ uint32_t op_ssg_eg_enable(uint32_t opoffs) const { return 0; } // OPN(A) only
+ uint32_t op_ssg_eg_mode(uint32_t opoffs) const { return 0; } // OPN(A) only
+
+protected:
+ // helper to encode four operator numbers into a 32-bit value in the
+ // operator maps for each register class
+ static constexpr uint32_t operator_list(uint8_t o1 = 0xff, uint8_t o2 = 0xff, uint8_t o3 = 0xff, uint8_t o4 = 0xff)
+ {
+ return o1 | (o2 << 8) | (o3 << 16) | (o4 << 24);
+ }
+
+ // helper to apply KSR to the raw ADSR rate, ignoring ksr if the
+ // raw value is 0, and clamping to 63
+ static constexpr uint32_t effective_rate(uint32_t rawrate, uint32_t ksr)
+ {
+ return (rawrate == 0) ? 0 : std::min<uint32_t>(rawrate + ksr, 63);
+ }
+};
+
+
+
+//*********************************************************
+// CORE ENGINE CLASSES
+//*********************************************************
+
+// forward declarations
+template<class RegisterType> class fm_engine_base;
+
+// ======================> fm_operator
+
+// fm_operator represents an FM operator (or "slot" in FM parlance), which
+// produces an output sine wave modulated by an envelope
+template<class RegisterType>
+class fm_operator
+{
+ // "quiet" value, used to optimize when we can skip doing work
+ static constexpr uint32_t EG_QUIET = 0x380;
+
+public:
+ // constructor
+ fm_operator(fm_engine_base<RegisterType> &owner, uint32_t opoffs);
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // reset the operator state
+ void reset();
+
+ // return the operator/channel offset
+ uint32_t opoffs() const { return m_opoffs; }
+ uint32_t choffs() const { return m_choffs; }
+
+ // set the current channel
+ void set_choffs(uint32_t choffs) { m_choffs = choffs; }
+
+ // prepare prior to clocking
+ bool prepare();
+
+ // master clocking function
+ void clock(uint32_t env_counter, int32_t lfo_raw_pm);
+
+ // return the current phase value
+ uint32_t phase() const { return m_phase >> 10; }
+
+ // compute operator volume
+ int32_t compute_volume(uint32_t phase, uint32_t am_offset) const;
+
+ // compute volume for the OPM noise channel
+ int32_t compute_noise_volume(uint32_t am_offset) const;
+
+ // key state control
+ void keyonoff(uint32_t on, keyon_type type);
+
+ // return a reference to our registers
+ RegisterType &regs() const { return m_regs; }
+
+ // simple getters for debugging
+ envelope_state debug_eg_state() const { return m_env_state; }
+ uint16_t debug_eg_attenuation() const { return m_env_attenuation; }
+ uint8_t debug_ssg_inverted() const { return m_ssg_inverted; }
+ opdata_cache &debug_cache() { return m_cache; }
+
+private:
+ // start the attack phase
+ void start_attack(bool is_restart = false);
+
+ // start the release phase
+ void start_release();
+
+ // clock phases
+ void clock_keystate(uint32_t keystate);
+ void clock_ssg_eg_state();
+ void clock_envelope(uint32_t env_counter);
+ void clock_phase(int32_t lfo_raw_pm);
+
+ // return effective attenuation of the envelope
+ uint32_t envelope_attenuation(uint32_t am_offset) const;
+
+ // internal state
+ uint32_t m_choffs; // channel offset in registers
+ uint32_t m_opoffs; // operator offset in registers
+ uint32_t m_phase; // current phase value (10.10 format)
+ uint16_t m_env_attenuation; // computed envelope attenuation (4.6 format)
+ envelope_state m_env_state; // current envelope state
+ uint8_t m_ssg_inverted; // non-zero if the output should be inverted (bit 0)
+ uint8_t m_key_state; // current key state: on or off (bit 0)
+ uint8_t m_keyon_live; // live key on state (bit 0 = direct, bit 1 = rhythm, bit 2 = CSM)
+ opdata_cache m_cache; // cached values for performance
+ RegisterType &m_regs; // direct reference to registers
+ fm_engine_base<RegisterType> &m_owner; // reference to the owning engine
+};
+
+
+// ======================> fm_channel
+
+// fm_channel represents an FM channel which combines the output of 2 or 4
+// operators into a final result
+template<class RegisterType>
+class fm_channel
+{
+ using output_data = ymfm_output<RegisterType::OUTPUTS>;
+
+public:
+ // constructor
+ fm_channel(fm_engine_base<RegisterType> &owner, uint32_t choffs);
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // reset the channel state
+ void reset();
+
+ // return the channel offset
+ uint32_t choffs() const { return m_choffs; }
+
+ // assign operators
+ void assign(uint32_t index, fm_operator<RegisterType> *op)
+ {
+ assert(index < m_op.size());
+ m_op[index] = op;
+ if (op != nullptr)
+ op->set_choffs(m_choffs);
+ }
+
+ // signal key on/off to our operators
+ void keyonoff(uint32_t states, keyon_type type, uint32_t chnum);
+
+ // prepare prior to clocking
+ bool prepare();
+
+ // master clocking function
+ void clock(uint32_t env_counter, int32_t lfo_raw_pm);
+
+ // specific 2-operator and 4-operator output handlers
+ void output_2op(output_data &output, uint32_t rshift, int32_t clipmax) const;
+ void output_4op(output_data &output, uint32_t rshift, int32_t clipmax) const;
+
+ // compute the special OPL rhythm channel outputs
+ void output_rhythm_ch6(output_data &output, uint32_t rshift, int32_t clipmax) const;
+ void output_rhythm_ch7(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const;
+ void output_rhythm_ch8(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const;
+
+ // are we a 4-operator channel or a 2-operator one?
+ bool is4op() const
+ {
+ if (RegisterType::DYNAMIC_OPS)
+ return (m_op[2] != nullptr);
+ return (RegisterType::OPERATORS / RegisterType::CHANNELS == 4);
+ }
+
+ // return a reference to our registers
+ RegisterType &regs() const { return m_regs; }
+
+ // simple getters for debugging
+ fm_operator<RegisterType> *debug_operator(uint32_t index) const { return m_op[index]; }
+
+private:
+ // helper to add values to the outputs based on channel enables
+ void add_to_output(uint32_t choffs, output_data &output, int32_t value) const
+ {
+ // create these constants to appease overzealous compilers checking array
+ // bounds in unreachable code (looking at you, clang)
+ constexpr int out0_index = 0;
+ constexpr int out1_index = 1 % RegisterType::OUTPUTS;
+ constexpr int out2_index = 2 % RegisterType::OUTPUTS;
+ constexpr int out3_index = 3 % RegisterType::OUTPUTS;
+
+ if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs))
+ output.data[out0_index] += value;
+ if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs))
+ output.data[out1_index] += value;
+ if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs))
+ output.data[out2_index] += value;
+ if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs))
+ output.data[out3_index] += value;
+ }
+
+ // internal state
+ 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)
+ 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
+};
+
+
+// ======================> fm_engine_base
+
+// fm_engine_base represents a set of operators and channels which together
+// form a Yamaha FM core; chips that implement other engines (ADPCM, wavetable,
+// etc) take this output and combine it with the others externally
+template<class RegisterType>
+class fm_engine_base : public ymfm_engine_callbacks
+{
+public:
+ // expose some constants from the registers
+ static constexpr uint32_t OUTPUTS = RegisterType::OUTPUTS;
+ static constexpr uint32_t CHANNELS = RegisterType::CHANNELS;
+ static constexpr uint32_t ALL_CHANNELS = RegisterType::ALL_CHANNELS;
+ static constexpr uint32_t OPERATORS = RegisterType::OPERATORS;
+
+ // also expose status flags for consumers that inject additional bits
+ static constexpr uint8_t STATUS_TIMERA = RegisterType::STATUS_TIMERA;
+ static constexpr uint8_t STATUS_TIMERB = RegisterType::STATUS_TIMERB;
+ static constexpr uint8_t STATUS_BUSY = RegisterType::STATUS_BUSY;
+ static constexpr uint8_t STATUS_IRQ = RegisterType::STATUS_IRQ;
+
+ // expose the correct output class
+ using output_data = ymfm_output<OUTPUTS>;
+
+ // constructor
+ fm_engine_base(ymfm_interface &intf);
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // reset the overall state
+ void reset();
+
+ // master clocking function
+ uint32_t clock(uint32_t chanmask);
+
+ // compute sum of channel outputs
+ void output(output_data &output, uint32_t rshift, int32_t clipmax, uint32_t chanmask) const;
+
+ // write to the OPN registers
+ void write(uint16_t regnum, uint8_t data);
+
+ // return the current status
+ uint8_t status() const;
+
+ // set/reset bits in the status register, updating the IRQ status
+ uint8_t set_reset_status(uint8_t set, uint8_t reset)
+ {
+ m_status = (m_status | set) & ~(reset | STATUS_BUSY);
+ m_intf.ymfm_sync_check_interrupts();
+ return m_status & ~m_regs.status_mask();
+ }
+
+ // set the IRQ mask
+ void set_irq_mask(uint8_t mask) { m_irq_mask = mask; m_intf.ymfm_sync_check_interrupts(); }
+
+ // return the current clock prescale
+ uint32_t clock_prescale() const { return m_clock_prescale; }
+
+ // set prescale factor (2/3/6)
+ void set_clock_prescale(uint32_t prescale) { m_clock_prescale = prescale; }
+
+ // compute sample rate
+ uint32_t sample_rate(uint32_t baseclock) const
+ {
+#if (YMFM_DEBUG_LOG_WAVFILES)
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ m_wavfile[chnum].set_samplerate(baseclock / (m_clock_prescale * OPERATORS));
+#endif
+ return baseclock / (m_clock_prescale * OPERATORS);
+ }
+
+ // return the owning device
+ ymfm_interface &intf() const { return m_intf; }
+
+ // return a reference to our registers
+ RegisterType &regs() { return m_regs; }
+
+ // invalidate any caches
+ void invalidate_caches() { m_modified_channels = RegisterType::ALL_CHANNELS; }
+
+ // simple getters for debugging
+ fm_channel<RegisterType> *debug_channel(uint32_t index) const { return m_channel[index].get(); }
+ fm_operator<RegisterType> *debug_operator(uint32_t index) const { return m_operator[index].get(); }
+
+public:
+ // timer callback; called by the interface when a timer fires
+ virtual void engine_timer_expired(uint32_t tnum) override;
+
+ // check interrupts; called by the interface after synchronization
+ virtual void engine_check_interrupts() override;
+
+ // mode register write; called by the interface after synchronization
+ virtual void engine_mode_write(uint8_t data) override;
+
+protected:
+ // assign the current set of operators to channels
+ void assign_operators();
+
+ // update the state of the given timer
+ void update_timer(uint32_t which, uint32_t enable, int32_t delta_clocks);
+
+ // internal state
+ ymfm_interface &m_intf; // reference to the system interface
+ uint32_t m_env_counter; // envelope counter; low 2 bits are sub-counter
+ uint8_t m_status; // current status register
+ uint8_t m_clock_prescale; // prescale factor (2/3/6)
+ uint8_t m_irq_mask; // mask of which bits signal IRQs
+ uint8_t m_irq_state; // current IRQ state
+ uint8_t m_timer_running[2]; // current timer running state
+ uint8_t m_total_clocks; // low 8 bits of the total number of clocks processed
+ uint32_t m_active_channels; // mask of active channels (computed by prepare)
+ uint32_t m_modified_channels; // mask of channels that have been modified
+ uint32_t m_prepare_count; // counter to do periodic prepare sweeps
+ RegisterType m_regs; // register accessor
+ std::unique_ptr<fm_channel<RegisterType>> m_channel[CHANNELS]; // channel pointers
+ std::unique_ptr<fm_operator<RegisterType>> m_operator[OPERATORS]; // operator pointers
+#if (YMFM_DEBUG_LOG_WAVFILES)
+ mutable ymfm_wavfile<1> m_wavfile[CHANNELS]; // for debugging
+#endif
+};
+
+}
+
+#endif // YMFM_FM_H
diff --git a/3rdparty/ymfm/src/ymfm_fm.ipp b/3rdparty/ymfm/src/ymfm_fm.ipp
new file mode 100644
index 00000000000..675424839ce
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_fm.ipp
@@ -0,0 +1,1592 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace ymfm
+{
+
+//*********************************************************
+// GLOBAL TABLE LOOKUPS
+//*********************************************************
+
+//-------------------------------------------------
+// abs_sin_attenuation - given a sin (phase) input
+// where the range 0-2*PI is mapped onto 10 bits,
+// return the absolute value of sin(input),
+// logarithmically-adjusted and treated as an
+// attenuation value, in 4.8 fixed point format
+//-------------------------------------------------
+
+inline uint32_t abs_sin_attenuation(uint32_t input)
+{
+ // the values here are stored as 4.8 logarithmic values for 1/4 phase
+ // this matches the internal format of the OPN chip, extracted from the die
+ static uint16_t const s_sin_table[256] =
+ {
+ 0x859,0x6c3,0x607,0x58b,0x52e,0x4e4,0x4a6,0x471,0x443,0x41a,0x3f5,0x3d3,0x3b5,0x398,0x37e,0x365,
+ 0x34e,0x339,0x324,0x311,0x2ff,0x2ed,0x2dc,0x2cd,0x2bd,0x2af,0x2a0,0x293,0x286,0x279,0x26d,0x261,
+ 0x256,0x24b,0x240,0x236,0x22c,0x222,0x218,0x20f,0x206,0x1fd,0x1f5,0x1ec,0x1e4,0x1dc,0x1d4,0x1cd,
+ 0x1c5,0x1be,0x1b7,0x1b0,0x1a9,0x1a2,0x19b,0x195,0x18f,0x188,0x182,0x17c,0x177,0x171,0x16b,0x166,
+ 0x160,0x15b,0x155,0x150,0x14b,0x146,0x141,0x13c,0x137,0x133,0x12e,0x129,0x125,0x121,0x11c,0x118,
+ 0x114,0x10f,0x10b,0x107,0x103,0x0ff,0x0fb,0x0f8,0x0f4,0x0f0,0x0ec,0x0e9,0x0e5,0x0e2,0x0de,0x0db,
+ 0x0d7,0x0d4,0x0d1,0x0cd,0x0ca,0x0c7,0x0c4,0x0c1,0x0be,0x0bb,0x0b8,0x0b5,0x0b2,0x0af,0x0ac,0x0a9,
+ 0x0a7,0x0a4,0x0a1,0x09f,0x09c,0x099,0x097,0x094,0x092,0x08f,0x08d,0x08a,0x088,0x086,0x083,0x081,
+ 0x07f,0x07d,0x07a,0x078,0x076,0x074,0x072,0x070,0x06e,0x06c,0x06a,0x068,0x066,0x064,0x062,0x060,
+ 0x05e,0x05c,0x05b,0x059,0x057,0x055,0x053,0x052,0x050,0x04e,0x04d,0x04b,0x04a,0x048,0x046,0x045,
+ 0x043,0x042,0x040,0x03f,0x03e,0x03c,0x03b,0x039,0x038,0x037,0x035,0x034,0x033,0x031,0x030,0x02f,
+ 0x02e,0x02d,0x02b,0x02a,0x029,0x028,0x027,0x026,0x025,0x024,0x023,0x022,0x021,0x020,0x01f,0x01e,
+ 0x01d,0x01c,0x01b,0x01a,0x019,0x018,0x017,0x017,0x016,0x015,0x014,0x014,0x013,0x012,0x011,0x011,
+ 0x010,0x00f,0x00f,0x00e,0x00d,0x00d,0x00c,0x00c,0x00b,0x00a,0x00a,0x009,0x009,0x008,0x008,0x007,
+ 0x007,0x007,0x006,0x006,0x005,0x005,0x005,0x004,0x004,0x004,0x003,0x003,0x003,0x002,0x002,0x002,
+ 0x002,0x001,0x001,0x001,0x001,0x001,0x001,0x001,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000
+ };
+
+ // if the top bit is set, we're in the second half of the curve
+ // which is a mirror image, so invert the index
+ if (bitfield(input, 8))
+ input = ~input;
+
+ // return the value from the table
+ return s_sin_table[input & 0xff];
+}
+
+
+//-------------------------------------------------
+// attenuation_to_volume - given a 5.8 fixed point
+// logarithmic attenuation value, return a 13-bit
+// linear volume
+//-------------------------------------------------
+
+inline uint32_t attenuation_to_volume(uint32_t input)
+{
+ // the values here are 10-bit mantissas with an implied leading bit
+ // this matches the internal format of the OPN chip, extracted from the die
+
+ // as a nod to performance, the implicit 0x400 bit is pre-incorporated, and
+ // the values are left-shifted by 2 so that a simple right shift is all that
+ // is needed; also the order is reversed to save a NOT on the input
+#define X(a) (((a) | 0x400) << 2)
+ static uint16_t const s_power_table[256] =
+ {
+ X(0x3fa),X(0x3f5),X(0x3ef),X(0x3ea),X(0x3e4),X(0x3df),X(0x3da),X(0x3d4),
+ X(0x3cf),X(0x3c9),X(0x3c4),X(0x3bf),X(0x3b9),X(0x3b4),X(0x3ae),X(0x3a9),
+ X(0x3a4),X(0x39f),X(0x399),X(0x394),X(0x38f),X(0x38a),X(0x384),X(0x37f),
+ X(0x37a),X(0x375),X(0x370),X(0x36a),X(0x365),X(0x360),X(0x35b),X(0x356),
+ X(0x351),X(0x34c),X(0x347),X(0x342),X(0x33d),X(0x338),X(0x333),X(0x32e),
+ X(0x329),X(0x324),X(0x31f),X(0x31a),X(0x315),X(0x310),X(0x30b),X(0x306),
+ X(0x302),X(0x2fd),X(0x2f8),X(0x2f3),X(0x2ee),X(0x2e9),X(0x2e5),X(0x2e0),
+ X(0x2db),X(0x2d6),X(0x2d2),X(0x2cd),X(0x2c8),X(0x2c4),X(0x2bf),X(0x2ba),
+ X(0x2b5),X(0x2b1),X(0x2ac),X(0x2a8),X(0x2a3),X(0x29e),X(0x29a),X(0x295),
+ X(0x291),X(0x28c),X(0x288),X(0x283),X(0x27f),X(0x27a),X(0x276),X(0x271),
+ X(0x26d),X(0x268),X(0x264),X(0x25f),X(0x25b),X(0x257),X(0x252),X(0x24e),
+ X(0x249),X(0x245),X(0x241),X(0x23c),X(0x238),X(0x234),X(0x230),X(0x22b),
+ X(0x227),X(0x223),X(0x21e),X(0x21a),X(0x216),X(0x212),X(0x20e),X(0x209),
+ X(0x205),X(0x201),X(0x1fd),X(0x1f9),X(0x1f5),X(0x1f0),X(0x1ec),X(0x1e8),
+ X(0x1e4),X(0x1e0),X(0x1dc),X(0x1d8),X(0x1d4),X(0x1d0),X(0x1cc),X(0x1c8),
+ X(0x1c4),X(0x1c0),X(0x1bc),X(0x1b8),X(0x1b4),X(0x1b0),X(0x1ac),X(0x1a8),
+ X(0x1a4),X(0x1a0),X(0x19c),X(0x199),X(0x195),X(0x191),X(0x18d),X(0x189),
+ X(0x185),X(0x181),X(0x17e),X(0x17a),X(0x176),X(0x172),X(0x16f),X(0x16b),
+ X(0x167),X(0x163),X(0x160),X(0x15c),X(0x158),X(0x154),X(0x151),X(0x14d),
+ X(0x149),X(0x146),X(0x142),X(0x13e),X(0x13b),X(0x137),X(0x134),X(0x130),
+ X(0x12c),X(0x129),X(0x125),X(0x122),X(0x11e),X(0x11b),X(0x117),X(0x114),
+ X(0x110),X(0x10c),X(0x109),X(0x106),X(0x102),X(0x0ff),X(0x0fb),X(0x0f8),
+ X(0x0f4),X(0x0f1),X(0x0ed),X(0x0ea),X(0x0e7),X(0x0e3),X(0x0e0),X(0x0dc),
+ X(0x0d9),X(0x0d6),X(0x0d2),X(0x0cf),X(0x0cc),X(0x0c8),X(0x0c5),X(0x0c2),
+ X(0x0be),X(0x0bb),X(0x0b8),X(0x0b5),X(0x0b1),X(0x0ae),X(0x0ab),X(0x0a8),
+ X(0x0a4),X(0x0a1),X(0x09e),X(0x09b),X(0x098),X(0x094),X(0x091),X(0x08e),
+ X(0x08b),X(0x088),X(0x085),X(0x082),X(0x07e),X(0x07b),X(0x078),X(0x075),
+ X(0x072),X(0x06f),X(0x06c),X(0x069),X(0x066),X(0x063),X(0x060),X(0x05d),
+ X(0x05a),X(0x057),X(0x054),X(0x051),X(0x04e),X(0x04b),X(0x048),X(0x045),
+ X(0x042),X(0x03f),X(0x03c),X(0x039),X(0x036),X(0x033),X(0x030),X(0x02d),
+ X(0x02a),X(0x028),X(0x025),X(0x022),X(0x01f),X(0x01c),X(0x019),X(0x016),
+ X(0x014),X(0x011),X(0x00e),X(0x00b),X(0x008),X(0x006),X(0x003),X(0x000)
+ };
+#undef X
+
+ // look up the fractional part, then shift by the whole
+ return s_power_table[input & 0xff] >> (input >> 8);
+}
+
+
+//-------------------------------------------------
+// attenuation_increment - given a 6-bit ADSR
+// rate value and a 3-bit stepping index,
+// return a 4-bit increment to the attenutaion
+// for this step (or for the attack case, the
+// fractional scale factor to decrease by)
+//-------------------------------------------------
+
+inline uint32_t attenuation_increment(uint32_t rate, uint32_t index)
+{
+ static uint32_t const s_increment_table[64] =
+ {
+ 0x00000000, 0x00000000, 0x10101010, 0x10101010, // 0-3 (0x00-0x03)
+ 0x10101010, 0x10101010, 0x11101110, 0x11101110, // 4-7 (0x04-0x07)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 8-11 (0x08-0x0B)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 12-15 (0x0C-0x0F)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 16-19 (0x10-0x13)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 20-23 (0x14-0x17)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 24-27 (0x18-0x1B)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 28-31 (0x1C-0x1F)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 32-35 (0x20-0x23)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 36-39 (0x24-0x27)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 40-43 (0x28-0x2B)
+ 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 44-47 (0x2C-0x2F)
+ 0x11111111, 0x21112111, 0x21212121, 0x22212221, // 48-51 (0x30-0x33)
+ 0x22222222, 0x42224222, 0x42424242, 0x44424442, // 52-55 (0x34-0x37)
+ 0x44444444, 0x84448444, 0x84848484, 0x88848884, // 56-59 (0x38-0x3B)
+ 0x88888888, 0x88888888, 0x88888888, 0x88888888 // 60-63 (0x3C-0x3F)
+ };
+ return bitfield(s_increment_table[rate], 4*index, 4);
+}
+
+
+//-------------------------------------------------
+// detune_adjustment - given a 5-bit key code
+// value and a 3-bit detune parameter, return a
+// 6-bit signed phase displacement; this table
+// has been verified against Nuked's equations,
+// but the equations are rather complicated, so
+// we'll keep the simplicity of the table
+//-------------------------------------------------
+
+inline int32_t detune_adjustment(uint32_t detune, uint32_t keycode)
+{
+ static uint8_t const s_detune_adjustment[32][4] =
+ {
+ { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 },
+ { 0, 1, 2, 2 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 },
+ { 0, 1, 2, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 5 },
+ { 0, 2, 4, 5 }, { 0, 2, 4, 6 }, { 0, 2, 4, 6 }, { 0, 2, 5, 7 },
+ { 0, 2, 5, 8 }, { 0, 3, 6, 8 }, { 0, 3, 6, 9 }, { 0, 3, 7, 10 },
+ { 0, 4, 8, 11 }, { 0, 4, 8, 12 }, { 0, 4, 9, 13 }, { 0, 5, 10, 14 },
+ { 0, 5, 11, 16 }, { 0, 6, 12, 17 }, { 0, 6, 13, 19 }, { 0, 7, 14, 20 },
+ { 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 }
+ };
+ int32_t result = s_detune_adjustment[keycode][detune & 3];
+ return bitfield(detune, 2) ? -result : result;
+}
+
+
+//-------------------------------------------------
+// opm_key_code_to_phase_step - converts an
+// OPM concatenated block (3 bits), keycode
+// (4 bits) and key fraction (6 bits) to a 0.10
+// phase step, after applying the given delta;
+// this applies to OPM and OPZ, so it lives here
+// in a central location
+//-------------------------------------------------
+
+inline uint32_t opm_key_code_to_phase_step(uint32_t block_freq, int32_t delta)
+{
+ // The phase step is essentially the fnum in OPN-speak. To compute this table,
+ // we used the standard formula for computing the frequency of a note, and
+ // then converted that frequency to fnum using the formula documented in the
+ // YM2608 manual.
+ //
+ // However, the YM2608 manual describes everything in terms of a nominal 8MHz
+ // clock, which produces an FM clock of:
+ //
+ // 8000000 / 24(operators) / 6(prescale) = 55555Hz FM clock
+ //
+ // Whereas the descriptions for the YM2151 use a nominal 3.579545MHz clock:
+ //
+ // 3579545 / 32(operators) / 2(prescale) = 55930Hz FM clock
+ //
+ // To correct for this, the YM2608 formula was adjusted to use a clock of
+ // 8053920Hz, giving this equation for the fnum:
+ //
+ // fnum = (double(144) * freq * (1 << 20)) / double(8053920) / 4;
+ //
+ // Unfortunately, the computed table differs in a few spots from the data
+ // verified from an actual chip. The table below comes from David Viens'
+ // analysis, used with his permission.
+ static const uint32_t s_phase_step[12*64] =
+ {
+ 41568,41600,41632,41664,41696,41728,41760,41792,41856,41888,41920,41952,42016,42048,42080,42112,
+ 42176,42208,42240,42272,42304,42336,42368,42400,42464,42496,42528,42560,42624,42656,42688,42720,
+ 42784,42816,42848,42880,42912,42944,42976,43008,43072,43104,43136,43168,43232,43264,43296,43328,
+ 43392,43424,43456,43488,43552,43584,43616,43648,43712,43744,43776,43808,43872,43904,43936,43968,
+ 44032,44064,44096,44128,44192,44224,44256,44288,44352,44384,44416,44448,44512,44544,44576,44608,
+ 44672,44704,44736,44768,44832,44864,44896,44928,44992,45024,45056,45088,45152,45184,45216,45248,
+ 45312,45344,45376,45408,45472,45504,45536,45568,45632,45664,45728,45760,45792,45824,45888,45920,
+ 45984,46016,46048,46080,46144,46176,46208,46240,46304,46336,46368,46400,46464,46496,46528,46560,
+ 46656,46688,46720,46752,46816,46848,46880,46912,46976,47008,47072,47104,47136,47168,47232,47264,
+ 47328,47360,47392,47424,47488,47520,47552,47584,47648,47680,47744,47776,47808,47840,47904,47936,
+ 48032,48064,48096,48128,48192,48224,48288,48320,48384,48416,48448,48480,48544,48576,48640,48672,
+ 48736,48768,48800,48832,48896,48928,48992,49024,49088,49120,49152,49184,49248,49280,49344,49376,
+ 49440,49472,49504,49536,49600,49632,49696,49728,49792,49824,49856,49888,49952,49984,50048,50080,
+ 50144,50176,50208,50240,50304,50336,50400,50432,50496,50528,50560,50592,50656,50688,50752,50784,
+ 50880,50912,50944,50976,51040,51072,51136,51168,51232,51264,51328,51360,51424,51456,51488,51520,
+ 51616,51648,51680,51712,51776,51808,51872,51904,51968,52000,52064,52096,52160,52192,52224,52256,
+ 52384,52416,52448,52480,52544,52576,52640,52672,52736,52768,52832,52864,52928,52960,52992,53024,
+ 53120,53152,53216,53248,53312,53344,53408,53440,53504,53536,53600,53632,53696,53728,53792,53824,
+ 53920,53952,54016,54048,54112,54144,54208,54240,54304,54336,54400,54432,54496,54528,54592,54624,
+ 54688,54720,54784,54816,54880,54912,54976,55008,55072,55104,55168,55200,55264,55296,55360,55392,
+ 55488,55520,55584,55616,55680,55712,55776,55808,55872,55936,55968,56032,56064,56128,56160,56224,
+ 56288,56320,56384,56416,56480,56512,56576,56608,56672,56736,56768,56832,56864,56928,56960,57024,
+ 57120,57152,57216,57248,57312,57376,57408,57472,57536,57568,57632,57664,57728,57792,57824,57888,
+ 57952,57984,58048,58080,58144,58208,58240,58304,58368,58400,58464,58496,58560,58624,58656,58720,
+ 58784,58816,58880,58912,58976,59040,59072,59136,59200,59232,59296,59328,59392,59456,59488,59552,
+ 59648,59680,59744,59776,59840,59904,59936,60000,60064,60128,60160,60224,60288,60320,60384,60416,
+ 60512,60544,60608,60640,60704,60768,60800,60864,60928,60992,61024,61088,61152,61184,61248,61280,
+ 61376,61408,61472,61536,61600,61632,61696,61760,61824,61856,61920,61984,62048,62080,62144,62208,
+ 62272,62304,62368,62432,62496,62528,62592,62656,62720,62752,62816,62880,62944,62976,63040,63104,
+ 63200,63232,63296,63360,63424,63456,63520,63584,63648,63680,63744,63808,63872,63904,63968,64032,
+ 64096,64128,64192,64256,64320,64352,64416,64480,64544,64608,64672,64704,64768,64832,64896,64928,
+ 65024,65056,65120,65184,65248,65312,65376,65408,65504,65536,65600,65664,65728,65792,65856,65888,
+ 65984,66016,66080,66144,66208,66272,66336,66368,66464,66496,66560,66624,66688,66752,66816,66848,
+ 66944,66976,67040,67104,67168,67232,67296,67328,67424,67456,67520,67584,67648,67712,67776,67808,
+ 67904,67936,68000,68064,68128,68192,68256,68288,68384,68448,68512,68544,68640,68672,68736,68800,
+ 68896,68928,68992,69056,69120,69184,69248,69280,69376,69440,69504,69536,69632,69664,69728,69792,
+ 69920,69952,70016,70080,70144,70208,70272,70304,70400,70464,70528,70560,70656,70688,70752,70816,
+ 70912,70976,71040,71104,71136,71232,71264,71360,71424,71488,71552,71616,71648,71744,71776,71872,
+ 71968,72032,72096,72160,72192,72288,72320,72416,72480,72544,72608,72672,72704,72800,72832,72928,
+ 72992,73056,73120,73184,73216,73312,73344,73440,73504,73568,73632,73696,73728,73824,73856,73952,
+ 74080,74144,74208,74272,74304,74400,74432,74528,74592,74656,74720,74784,74816,74912,74944,75040,
+ 75136,75200,75264,75328,75360,75456,75488,75584,75648,75712,75776,75840,75872,75968,76000,76096,
+ 76224,76288,76352,76416,76448,76544,76576,76672,76736,76800,76864,76928,77024,77120,77152,77248,
+ 77344,77408,77472,77536,77568,77664,77696,77792,77856,77920,77984,78048,78144,78240,78272,78368,
+ 78464,78528,78592,78656,78688,78784,78816,78912,78976,79040,79104,79168,79264,79360,79392,79488,
+ 79616,79680,79744,79808,79840,79936,79968,80064,80128,80192,80256,80320,80416,80512,80544,80640,
+ 80768,80832,80896,80960,80992,81088,81120,81216,81280,81344,81408,81472,81568,81664,81696,81792,
+ 81952,82016,82080,82144,82176,82272,82304,82400,82464,82528,82592,82656,82752,82848,82880,82976
+ };
+
+ // extract the block (octave) first
+ uint32_t block = bitfield(block_freq, 10, 3);
+
+ // the keycode (bits 6-9) is "gappy", mapping 12 values over 16 in each
+ // octave; to correct for this, we multiply the 4-bit value by 3/4 (or
+ // rather subtract 1/4); note that a (invalid) value of 15 will bleed into
+ // the next octave -- this is confirmed
+ uint32_t adjusted_code = bitfield(block_freq, 6, 4) - bitfield(block_freq, 8, 2);
+
+ // now re-insert the 6-bit fraction
+ int32_t eff_freq = (adjusted_code << 6) | bitfield(block_freq, 0, 6);
+
+ // now that the gaps are removed, add the delta
+ eff_freq += delta;
+
+ // handle over/underflow by adjusting the block:
+ if (uint32_t(eff_freq) >= 768)
+ {
+ // minimum delta is -512 (PM), so we can only underflow by 1 octave
+ if (eff_freq < 0)
+ {
+ eff_freq += 768;
+ if (block-- == 0)
+ return s_phase_step[0] >> 7;
+ }
+
+ // maximum delta is +512+608 (PM+detune), so we can overflow by up to 2 octaves
+ else
+ {
+ eff_freq -= 768;
+ if (eff_freq >= 768)
+ block++, eff_freq -= 768;
+ if (block++ >= 7)
+ return s_phase_step[767];
+ }
+ }
+
+ // look up the phase shift for the key code, then shift by octave
+ return s_phase_step[eff_freq] >> (block ^ 7);
+}
+
+
+//-------------------------------------------------
+// opn_lfo_pm_phase_adjustment - given the 7 most
+// significant frequency number bits, plus a 3-bit
+// PM depth value and a signed 5-bit raw PM value,
+// return a signed PM adjustment to the frequency;
+// algorithm written to match Nuked behavior
+//-------------------------------------------------
+
+inline int32_t opn_lfo_pm_phase_adjustment(uint32_t fnum_bits, uint32_t pm_sensitivity, int32_t lfo_raw_pm)
+{
+ // this table encodes 2 shift values to apply to the top 7 bits
+ // of fnum; it is effectively a cheap multiply by a constant
+ // value containing 0-2 bits
+ static uint8_t const s_lfo_pm_shifts[8][8] =
+ {
+ { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 },
+ { 0x77, 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x72 },
+ { 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x17, 0x17 },
+ { 0x77, 0x77, 0x72, 0x72, 0x17, 0x17, 0x12, 0x12 },
+ { 0x77, 0x77, 0x72, 0x17, 0x17, 0x17, 0x12, 0x07 },
+ { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 },
+ { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 },
+ { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 }
+ };
+
+ // look up the relevant shifts
+ int32_t abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm;
+ uint32_t const shifts = s_lfo_pm_shifts[pm_sensitivity][bitfield(abs_pm, 0, 3)];
+
+ // compute the adjustment
+ int32_t adjust = (fnum_bits >> bitfield(shifts, 0, 4)) + (fnum_bits >> bitfield(shifts, 4, 4));
+ if (pm_sensitivity > 5)
+ adjust <<= pm_sensitivity - 5;
+ adjust >>= 2;
+
+ // every 16 cycles it inverts sign
+ return (lfo_raw_pm < 0) ? -adjust : adjust;
+}
+
+
+
+//*********************************************************
+// FM OPERATOR
+//*********************************************************
+
+//-------------------------------------------------
+// fm_operator - constructor
+//-------------------------------------------------
+
+template<class RegisterType>
+fm_operator<RegisterType>::fm_operator(fm_engine_base<RegisterType> &owner, uint32_t opoffs) :
+ m_choffs(0),
+ m_opoffs(opoffs),
+ m_phase(0),
+ m_env_attenuation(0x3ff),
+ m_env_state(EG_RELEASE),
+ m_ssg_inverted(false),
+ m_key_state(0),
+ m_keyon_live(0),
+ m_regs(owner.regs()),
+ m_owner(owner)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the channel state
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::reset()
+{
+ // reset our data
+ m_phase = 0;
+ m_env_attenuation = 0x3ff;
+ m_env_state = EG_RELEASE;
+ m_ssg_inverted = 0;
+ m_key_state = 0;
+ m_keyon_live = 0;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_phase);
+ state.save_restore(m_env_attenuation);
+ state.save_restore(m_env_state);
+ state.save_restore(m_ssg_inverted);
+ state.save_restore(m_key_state);
+ state.save_restore(m_keyon_live);
+}
+
+
+//-------------------------------------------------
+// prepare - prepare for clocking
+//-------------------------------------------------
+
+template<class RegisterType>
+bool fm_operator<RegisterType>::prepare()
+{
+ // cache the data
+ m_regs.cache_operator_data(m_choffs, m_opoffs, m_cache);
+
+ // clock the key state
+ clock_keystate(uint32_t(m_keyon_live != 0));
+ m_keyon_live &= ~(1 << KEYON_CSM);
+
+ // we're active until we're quiet after the release
+ return (m_env_state != (RegisterType::EG_HAS_REVERB ? EG_REVERB : EG_RELEASE) || m_env_attenuation < EG_QUIET);
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
+{
+ // clock the SSG-EG state (OPN/OPNA)
+ if (m_regs.op_ssg_eg_enable(m_opoffs))
+ clock_ssg_eg_state();
+ else
+ m_ssg_inverted = false;
+
+ // clock the envelope if on an envelope cycle; env_counter is a x.2 value
+ if (bitfield(env_counter, 0, 2) == 0)
+ clock_envelope(env_counter >> 2);
+
+ // clock the phase
+ clock_phase(lfo_raw_pm);
+}
+
+
+//-------------------------------------------------
+// compute_volume - compute the 14-bit signed
+// volume of this operator, given a phase
+// modulation and an AM LFO offset
+//-------------------------------------------------
+
+template<class RegisterType>
+int32_t fm_operator<RegisterType>::compute_volume(uint32_t phase, uint32_t am_offset) const
+{
+ // the low 10 bits of phase represents a full 2*PI period over
+ // the full sin wave
+
+ // early out if the envelope is effectively off
+ if (m_env_attenuation > EG_QUIET)
+ return 0;
+
+ // get the absolute value of the sin, as attenuation, as a 4.8 fixed point value
+ uint32_t sin_attenuation = m_cache.waveform[phase & (RegisterType::WAVEFORM_LENGTH - 1)];
+
+ // get the attenuation from the evelope generator as a 4.6 value, shifted up to 4.8
+ uint32_t env_attenuation = envelope_attenuation(am_offset) << 2;
+
+ // combine into a 5.8 value, then convert from attenuation to 13-bit linear volume
+ int32_t result = attenuation_to_volume((sin_attenuation & 0x7fff) + env_attenuation);
+
+ // negate if in the negative part of the sin wave (sign bit gives 14 bits)
+ return bitfield(sin_attenuation, 15) ? -result : result;
+}
+
+
+//-------------------------------------------------
+// compute_noise_volume - compute the 14-bit
+// signed noise volume of this operator, given a
+// noise input value and an AM offset
+//-------------------------------------------------
+
+template<class RegisterType>
+int32_t fm_operator<RegisterType>::compute_noise_volume(uint32_t am_offset) const
+{
+ // application manual says the logarithmic transform is not applied here, so we
+ // just use the raw envelope attenuation, inverted (since 0 attenuation should be
+ // maximum), and shift it up from a 10-bit value to an 11-bit value
+ int32_t result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1;
+
+ // QUESTION: is AM applied still?
+
+ // negate based on the noise state
+ return bitfield(m_regs.noise_state(), 0) ? -result : result;
+}
+
+
+//-------------------------------------------------
+// keyonoff - signal a key on/off event
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::keyonoff(uint32_t on, keyon_type type)
+{
+ m_keyon_live = (m_keyon_live & ~(1 << int(type))) | (bitfield(on, 0) << int(type));
+}
+
+
+//-------------------------------------------------
+// start_attack - start the attack phase; called
+// when a keyon happens or when an SSG-EG cycle
+// is complete and restarts
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::start_attack(bool is_restart)
+{
+ // don't change anything if already in attack state
+ if (m_env_state == EG_ATTACK)
+ return;
+ m_env_state = EG_ATTACK;
+
+ // generally not inverted at start, except if SSG-EG is enabled and
+ // one of the inverted modes is specified; leave this alone on a
+ // restart, as it is managed by the clock_ssg_eg_state() code
+ if (RegisterType::EG_HAS_SSG && !is_restart)
+ m_ssg_inverted = m_regs.op_ssg_eg_enable(m_opoffs) & bitfield(m_regs.op_ssg_eg_mode(m_opoffs), 2);
+
+ // reset the phase when we start an attack due to a key on
+ // (but not when due to an SSG-EG restart except in certain cases
+ // managed directly by the SSG-EG code)
+ if (!is_restart)
+ m_phase = 0;
+
+ // if the attack rate >= 62 then immediately go to max attenuation
+ if (m_cache.eg_rate[EG_ATTACK] >= 62)
+ m_env_attenuation = 0;
+}
+
+
+//-------------------------------------------------
+// start_release - start the release phase;
+// called when a keyoff happens
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::start_release()
+{
+ // don't change anything if already in release state
+ if (m_env_state >= EG_RELEASE)
+ return;
+ m_env_state = EG_RELEASE;
+
+ // if attenuation if inverted due to SSG-EG, snap the inverted attenuation
+ // as the starting point
+ if (RegisterType::EG_HAS_SSG && m_ssg_inverted)
+ {
+ m_env_attenuation = (0x200 - m_env_attenuation) & 0x3ff;
+ m_ssg_inverted = false;
+ }
+}
+
+
+//-------------------------------------------------
+// clock_keystate - clock the keystate to match
+// the incoming keystate
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::clock_keystate(uint32_t keystate)
+{
+ assert(keystate == 0 || keystate == 1);
+
+ // has the key changed?
+ if ((keystate ^ m_key_state) != 0)
+ {
+ m_key_state = keystate;
+
+ // if the key has turned on, start the attack
+ if (keystate != 0)
+ {
+ // OPLL has a DP ("depress"?) state to bring the volume
+ // down before starting the attack
+ if (RegisterType::EG_HAS_DEPRESS && m_env_attenuation < 0x200)
+ m_env_state = EG_DEPRESS;
+ else
+ start_attack();
+ }
+
+ // otherwise, start the release
+ else
+ start_release();
+ }
+}
+
+
+//-------------------------------------------------
+// clock_ssg_eg_state - clock the SSG-EG state;
+// should only be called if SSG-EG is enabled
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::clock_ssg_eg_state()
+{
+ // work only happens once the attenuation crosses above 0x200
+ if (!bitfield(m_env_attenuation, 9))
+ return;
+
+ // 8 SSG-EG modes:
+ // 000: repeat normally
+ // 001: run once, hold low
+ // 010: repeat, alternating between inverted/non-inverted
+ // 011: run once, hold high
+ // 100: inverted repeat normally
+ // 101: inverted run once, hold low
+ // 110: inverted repeat, alternating between inverted/non-inverted
+ // 111: inverted run once, hold high
+ uint32_t mode = m_regs.op_ssg_eg_mode(m_opoffs);
+
+ // hold modes (1/3/5/7)
+ if (bitfield(mode, 0))
+ {
+ // set the inverted flag to the end state (0 for modes 1/7, 1 for modes 3/5)
+ m_ssg_inverted = bitfield(mode, 2) ^ bitfield(mode, 1);
+
+ // if holding, force the attenuation to the expected value once we're
+ // past the attack phase
+ if (m_env_state != EG_ATTACK)
+ m_env_attenuation = m_ssg_inverted ? 0x200 : 0x3ff;
+ }
+
+ // continuous modes (0/2/4/6)
+ else
+ {
+ // toggle invert in alternating mode (even in attack state)
+ m_ssg_inverted ^= bitfield(mode, 1);
+
+ // restart attack if in decay/sustain states
+ if (m_env_state == EG_DECAY || m_env_state == EG_SUSTAIN)
+ start_attack(true);
+
+ // phase is reset to 0 in modes 0/4
+ if (bitfield(mode, 1) == 0)
+ m_phase = 0;
+ }
+
+ // in all modes, once we hit release state, attenuation is forced to maximum
+ if (m_env_state == EG_RELEASE)
+ m_env_attenuation = 0x3ff;
+}
+
+
+//-------------------------------------------------
+// clock_envelope - clock the envelope state
+// according to the given count
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::clock_envelope(uint32_t env_counter)
+{
+ // handle attack->decay transitions
+ if (m_env_state == EG_ATTACK && m_env_attenuation == 0)
+ m_env_state = EG_DECAY;
+
+ // handle decay->sustain transitions; it is important to do this immediately
+ // after the attack->decay transition above in the event that the sustain level
+ // is set to 0 (in which case we will skip right to sustain without doing any
+ // decay); as an example where this can be heard, check the cymbals sound
+ // in channel 0 of shinobi's test mode sound #5
+ if (m_env_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain)
+ m_env_state = EG_SUSTAIN;
+
+ // fetch the appropriate 6-bit rate value from the cache
+ uint32_t rate = m_cache.eg_rate[m_env_state];
+
+ // compute the rate shift value; this is the shift needed to
+ // apply to the env_counter such that it becomes a 5.11 fixed
+ // point number
+ uint32_t rate_shift = rate >> 2;
+ env_counter <<= rate_shift;
+
+ // see if the fractional part is 0; if not, it's not time to clock
+ if (bitfield(env_counter, 0, 11) != 0)
+ return;
+
+ // determine the increment based on the non-fractional part of env_counter
+ uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3);
+ uint32_t increment = attenuation_increment(rate, relevant_bits);
+
+ // attack is the only one that increases
+ if (m_env_state == EG_ATTACK)
+ {
+ // glitch means that attack rates of 62/63 don't increment if
+ // changed after the initial key on (where they are handled
+ // specially); nukeykt confirms this happens on OPM, OPN, OPL/OPLL
+ // at least so assuming it is true for everyone
+ if (rate < 62)
+ m_env_attenuation += (~m_env_attenuation * increment) >> 4;
+ }
+
+ // all other cases are similar
+ else
+ {
+ // non-SSG-EG cases just apply the increment
+ if (!m_regs.op_ssg_eg_enable(m_opoffs))
+ m_env_attenuation += increment;
+
+ // SSG-EG only applies if less than mid-point, and then at 4x
+ else if (m_env_attenuation < 0x200)
+ m_env_attenuation += 4 * increment;
+
+ // clamp the final attenuation
+ if (m_env_attenuation >= 0x400)
+ m_env_attenuation = 0x3ff;
+
+ // transition from depress to attack
+ if (RegisterType::EG_HAS_DEPRESS && m_env_state == EG_DEPRESS && m_env_attenuation >= 0x200)
+ start_attack();
+
+ // transition from release to reverb, should switch at -18dB
+ if (RegisterType::EG_HAS_REVERB && m_env_state == EG_RELEASE && m_env_attenuation >= 0xc0)
+ m_env_state = EG_REVERB;
+ }
+}
+
+
+//-------------------------------------------------
+// clock_phase - clock the 10.10 phase value; the
+// OPN version of the logic has been verified
+// against the Nuked phase generator
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_operator<RegisterType>::clock_phase(int32_t lfo_raw_pm)
+{
+ // read from the cache, or recalculate if PM active
+ uint32_t phase_step = m_cache.phase_step;
+ if (phase_step == opdata_cache::PHASE_STEP_DYNAMIC)
+ phase_step = m_regs.compute_phase_step(m_choffs, m_opoffs, m_cache, lfo_raw_pm);
+
+ // finally apply the step to the current phase value
+ m_phase += phase_step;
+}
+
+
+//-------------------------------------------------
+// envelope_attenuation - return the effective
+// attenuation of the envelope
+//-------------------------------------------------
+
+template<class RegisterType>
+uint32_t fm_operator<RegisterType>::envelope_attenuation(uint32_t am_offset) const
+{
+ uint32_t result = m_env_attenuation >> m_cache.eg_shift;
+
+ // invert if necessary due to SSG-EG
+ if (RegisterType::EG_HAS_SSG && m_ssg_inverted)
+ result = (0x200 - result) & 0x3ff;
+
+ // add in LFO AM modulation
+ if (m_regs.op_lfo_am_enable(m_opoffs))
+ result += am_offset;
+
+ // add in total level and KSL from the cache
+ result += m_cache.total_level;
+
+ // clamp to max, apply shift, and return
+ return std::min<uint32_t>(result, 0x3ff);
+}
+
+
+
+//*********************************************************
+// FM CHANNEL
+//*********************************************************
+
+//-------------------------------------------------
+// fm_channel - constructor
+//-------------------------------------------------
+
+template<class RegisterType>
+fm_channel<RegisterType>::fm_channel(fm_engine_base<RegisterType> &owner, uint32_t choffs) :
+ m_choffs(choffs),
+ m_feedback{ 0, 0 },
+ m_feedback_in(0),
+ m_op{ nullptr, nullptr, nullptr, nullptr },
+ m_regs(owner.regs()),
+ m_owner(owner)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the channel state
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::reset()
+{
+ // reset our data
+ m_feedback[0] = m_feedback[1] = 0;
+ m_feedback_in = 0;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_feedback[0]);
+ state.save_restore(m_feedback[1]);
+ state.save_restore(m_feedback_in);
+}
+
+
+//-------------------------------------------------
+// keyonoff - signal key on/off to our operators
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::keyonoff(uint32_t states, keyon_type type, uint32_t chnum)
+{
+ 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 < 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());
+}
+
+
+//-------------------------------------------------
+// prepare - prepare for clocking
+//-------------------------------------------------
+
+template<class RegisterType>
+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 < m_op.size(); opnum++)
+ if (m_op[opnum] != nullptr)
+ if (m_op[opnum]->prepare())
+ active_mask |= 1 << opnum;
+
+ return (active_mask != 0);
+}
+
+
+//-------------------------------------------------
+// clock - master clock of all operators
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
+{
+ // clock the feedback through
+ m_feedback[0] = m_feedback[1];
+ m_feedback[1] = m_feedback_in;
+
+ for (uint32_t opnum = 0; opnum < m_op.size(); opnum++)
+ if (m_op[opnum] != nullptr)
+ m_op[opnum]->clock(env_counter, lfo_raw_pm);
+
+/*
+useful temporary code for envelope debugging
+if (m_choffs == 0x101)
+{
+ 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 ",
+ "PADSRV"[op.debug_eg_state()],
+ op.debug_eg_attenuation(),
+ op.debug_ssg_inverted() ? '-' : '+',
+ m_regs.op_ssg_eg_enable(op.opoffs()) ? '0' + m_regs.op_ssg_eg_mode(op.opoffs()) : ' ');
+ }
+printf(" -- ");
+}
+*/
+}
+
+
+//-------------------------------------------------
+// output_2op - combine 4 operators according to
+// the specified algorithm, returning a sum
+// according to the rshift and clipmax parameters,
+// which vary between different implementations
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::output_2op(output_data &output, uint32_t rshift, int32_t clipmax) const
+{
+ // The first 2 operators should be populated
+ assert(m_op[0] != nullptr);
+ assert(m_op[1] != nullptr);
+
+ // AM amount is the same across all operators; compute it once
+ uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
+
+ // operator 1 has optional self-feedback
+ int32_t opmod = 0;
+ uint32_t feedback = m_regs.ch_feedback(m_choffs);
+ if (feedback != 0)
+ opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
+
+ // compute the 14-bit volume/value of operator 1 and update the feedback
+ int32_t op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
+
+ // now that the feedback has been computed, skip the rest if all volumes
+ // are clear; no need to do all this work for nothing
+ if (m_regs.ch_output_any(m_choffs) == 0)
+ return;
+
+ // Algorithms for two-operator case:
+ // 0: O1 -> O2 -> out
+ // 1: (O1 + O2) -> out
+ int32_t result;
+ if (bitfield(m_regs.ch_algorithm(m_choffs), 0) == 0)
+ {
+ // some OPL chips use the previous sample for modulation instead of
+ // the current sample
+ opmod = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> 1;
+ result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift;
+ }
+ else
+ {
+ result = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> rshift;
+ result += m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift;
+ int32_t clipmin = -clipmax - 1;
+ result = clamp(result, clipmin, clipmax);
+ }
+
+ // add to the output
+ add_to_output(m_choffs, output, result);
+}
+
+
+//-------------------------------------------------
+// output_4op - combine 4 operators according to
+// the specified algorithm, returning a sum
+// according to the rshift and clipmax parameters,
+// which vary between different implementations
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::output_4op(output_data &output, uint32_t rshift, int32_t clipmax) const
+{
+ // all 4 operators should be populated
+ assert(m_op[0] != nullptr);
+ assert(m_op[1] != nullptr);
+ assert(m_op[2] != nullptr);
+ assert(m_op[3] != nullptr);
+
+ // AM amount is the same across all operators; compute it once
+ uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
+
+ // operator 1 has optional self-feedback
+ int32_t opmod = 0;
+ uint32_t feedback = m_regs.ch_feedback(m_choffs);
+ if (feedback != 0)
+ opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
+
+ // compute the 14-bit volume/value of operator 1 and update the feedback
+ int32_t op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
+
+ // now that the feedback has been computed, skip the rest if all volumes
+ // are clear; no need to do all this work for nothing
+ if (m_regs.ch_output_any(m_choffs) == 0)
+ return;
+
+ // OPM/OPN offer 8 different connection algorithms for 4 operators,
+ // and OPL3 offers 4 more, which we designate here as 8-11.
+ //
+ // The operators are computed in order, with the inputs pulled from
+ // an array of values (opout) that is populated as we go:
+ // 0 = 0
+ // 1 = O1
+ // 2 = O2
+ // 3 = O3
+ // 4 = (O4)
+ // 5 = O1+O2
+ // 6 = O1+O3
+ // 7 = O2+O3
+ //
+ // The s_algorithm_ops table describes the inputs and outputs of each
+ // algorithm as follows:
+ //
+ // ---------x use opout[x] as operator 2 input
+ // ------xxx- use opout[x] as operator 3 input
+ // ---xxx---- use opout[x] as operator 4 input
+ // --x------- include opout[1] in final sum
+ // -x-------- include opout[2] in final sum
+ // x--------- include opout[3] in final sum
+ #define ALGORITHM(op2in, op3in, op4in, op1out, op2out, op3out) \
+ ((op2in) | ((op3in) << 1) | ((op4in) << 4) | ((op1out) << 7) | ((op2out) << 8) | ((op3out) << 9))
+ static uint16_t const s_algorithm_ops[8+4] =
+ {
+ ALGORITHM(1,2,3, 0,0,0), // 0: O1 -> O2 -> O3 -> O4 -> out (O4)
+ ALGORITHM(0,5,3, 0,0,0), // 1: (O1 + O2) -> O3 -> O4 -> out (O4)
+ ALGORITHM(0,2,6, 0,0,0), // 2: (O1 + (O2 -> O3)) -> O4 -> out (O4)
+ ALGORITHM(1,0,7, 0,0,0), // 3: ((O1 -> O2) + O3) -> O4 -> out (O4)
+ ALGORITHM(1,0,3, 0,1,0), // 4: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4)
+ ALGORITHM(1,1,1, 0,1,1), // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out (O2+O3+O4)
+ ALGORITHM(1,0,0, 0,1,1), // 6: ((O1 -> O2) + O3 + O4) -> out (O2+O3+O4)
+ ALGORITHM(0,0,0, 1,1,1), // 7: (O1 + O2 + O3 + O4) -> out (O1+O2+O3+O4)
+ ALGORITHM(1,2,3, 0,0,0), // 8: O1 -> O2 -> O3 -> O4 -> out (O4) [same as 0]
+ ALGORITHM(0,2,3, 1,0,0), // 9: (O1 + (O2 -> O3 -> O4)) -> out (O1+O4) [unique]
+ ALGORITHM(1,0,3, 0,1,0), // 10: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) [same as 4]
+ ALGORITHM(0,2,0, 1,0,1) // 11: (O1 + (O2 -> O3) + O4) -> out (O1+O3+O4) [unique]
+ };
+ uint32_t algorithm_ops = s_algorithm_ops[m_regs.ch_algorithm(m_choffs)];
+
+ // populate the opout table
+ int16_t opout[8];
+ opout[0] = 0;
+ opout[1] = op1value;
+
+ // compute the 14-bit volume/value of operator 2
+ opmod = opout[bitfield(algorithm_ops, 0, 1)] >> 1;
+ opout[2] = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset);
+ opout[5] = opout[1] + opout[2];
+
+ // compute the 14-bit volume/value of operator 3
+ opmod = opout[bitfield(algorithm_ops, 1, 3)] >> 1;
+ opout[3] = m_op[2]->compute_volume(m_op[2]->phase() + opmod, am_offset);
+ opout[6] = opout[1] + opout[3];
+ opout[7] = opout[2] + opout[3];
+
+ // compute the 14-bit volume/value of operator 4; this could be a noise
+ // value on the OPM; all algorithms consume OP4 output at a minimum
+ int32_t result;
+ if (m_regs.noise_enable() && m_choffs == 7)
+ result = m_op[3]->compute_noise_volume(am_offset);
+ else
+ {
+ opmod = opout[bitfield(algorithm_ops, 4, 3)] >> 1;
+ result = m_op[3]->compute_volume(m_op[3]->phase() + opmod, am_offset);
+ }
+ result >>= rshift;
+
+ // optionally add OP1, OP2, OP3
+ int32_t clipmin = -clipmax - 1;
+ if (bitfield(algorithm_ops, 7) != 0)
+ result = clamp(result + (opout[1] >> rshift), clipmin, clipmax);
+ if (bitfield(algorithm_ops, 8) != 0)
+ result = clamp(result + (opout[2] >> rshift), clipmin, clipmax);
+ if (bitfield(algorithm_ops, 9) != 0)
+ result = clamp(result + (opout[3] >> rshift), clipmin, clipmax);
+
+ // add to the output
+ add_to_output(m_choffs, output, result);
+}
+
+
+//-------------------------------------------------
+// output_rhythm_ch6 - special case output
+// computation for OPL channel 6 in rhythm mode,
+// which outputs a Bass Drum instrument
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::output_rhythm_ch6(output_data &output, uint32_t rshift, int32_t clipmax) const
+{
+ // AM amount is the same across all operators; compute it once
+ uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
+
+ // Bass Drum: this uses operators 12 and 15 (i.e., channel 6)
+ // in an almost-normal way, except that if the algorithm is 1,
+ // the first operator is ignored instead of added in
+
+ // operator 1 has optional self-feedback
+ int32_t opmod = 0;
+ uint32_t feedback = m_regs.ch_feedback(m_choffs);
+ if (feedback != 0)
+ opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
+
+ // compute the 14-bit volume/value of operator 1 and update the feedback
+ int32_t opout1 = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
+
+ // compute the 14-bit volume/value of operator 2, which is the result
+ opmod = bitfield(m_regs.ch_algorithm(m_choffs), 0) ? 0 : (opout1 >> 1);
+ int32_t result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift;
+
+ // add to the output
+ add_to_output(m_choffs, output, result * 2);
+}
+
+
+//-------------------------------------------------
+// output_rhythm_ch7 - special case output
+// computation for OPL channel 7 in rhythm mode,
+// which outputs High Hat and Snare Drum
+// instruments
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::output_rhythm_ch7(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const
+{
+ // AM amount is the same across all operators; compute it once
+ uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
+ uint32_t noise_state = bitfield(m_regs.noise_state(), 0);
+
+ // High Hat: this uses the envelope from operator 13 (channel 7),
+ // and a combination of noise and the operator 13/17 phase select
+ // to compute the phase
+ uint32_t phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select)));
+ int32_t result = m_op[0]->compute_volume(phase, am_offset) >> rshift;
+
+ // Snare Drum: this uses the envelope from operator 16 (channel 7),
+ // and a combination of noise and operator 13 phase to pick a phase
+ uint32_t op13phase = m_op[0]->phase();
+ phase = (0x100 << bitfield(op13phase, 8)) ^ (noise_state << 8);
+ result += m_op[1]->compute_volume(phase, am_offset) >> rshift;
+ result = clamp(result, -clipmax - 1, clipmax);
+
+ // add to the output
+ add_to_output(m_choffs, output, result * 2);
+}
+
+
+//-------------------------------------------------
+// output_rhythm_ch8 - special case output
+// computation for OPL channel 8 in rhythm mode,
+// which outputs Tom Tom and Top Cymbal instruments
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_channel<RegisterType>::output_rhythm_ch8(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const
+{
+ // AM amount is the same across all operators; compute it once
+ uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
+
+ // Tom Tom: this is just a single operator processed normally
+ int32_t result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift;
+
+ // Top Cymbal: this uses the envelope from operator 17 (channel 8),
+ // and the operator 13/17 phase select to compute the phase
+ uint32_t phase = 0x100 | (phase_select << 9);
+ result += m_op[1]->compute_volume(phase, am_offset) >> rshift;
+ result = clamp(result, -clipmax - 1, clipmax);
+
+ // add to the output
+ add_to_output(m_choffs, output, result * 2);
+}
+
+
+
+//*********************************************************
+// FM ENGINE BASE
+//*********************************************************
+
+//-------------------------------------------------
+// fm_engine_base - constructor
+//-------------------------------------------------
+
+template<class RegisterType>
+fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
+ m_intf(intf),
+ m_env_counter(0),
+ m_status(0),
+ m_clock_prescale(RegisterType::DEFAULT_PRESCALE),
+ m_irq_mask(STATUS_TIMERA | STATUS_TIMERB),
+ m_irq_state(0),
+ m_timer_running{0,0},
+ m_total_clocks(0),
+ m_active_channels(ALL_CHANNELS),
+ m_modified_channels(ALL_CHANNELS),
+ m_prepare_count(0)
+{
+ // inform the interface of their engine
+ m_intf.m_engine = this;
+
+ // create the channels
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ m_channel[chnum] = std::make_unique<fm_channel<RegisterType>>(*this, RegisterType::channel_offset(chnum));
+
+ // create the operators
+ for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
+ m_operator[opnum] = std::make_unique<fm_operator<RegisterType>>(*this, RegisterType::operator_offset(opnum));
+
+#if (YMFM_DEBUG_LOG_WAVFILES)
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ m_wavfile[chnum].set_index(chnum);
+#endif
+
+ // do the initial operator assignment
+ assign_operators();
+}
+
+
+//-------------------------------------------------
+// reset - reset the overall state
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_engine_base<RegisterType>::reset()
+{
+ // reset all status bits
+ set_reset_status(0, 0xff);
+
+ // register type-specific initialization
+ m_regs.reset();
+
+ // explicitly write to the mode register since it has side-effects
+ // QUESTION: old cores initialize this to 0x30 -- who is right?
+ write(RegisterType::REG_MODE, 0);
+
+ // reset the channels
+ for (auto &chan : m_channel)
+ chan->reset();
+
+ // reset the operators
+ for (auto &op : m_operator)
+ op->reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_engine_base<RegisterType>::save_restore(ymfm_saved_state &state)
+{
+ // save our data
+ state.save_restore(m_env_counter);
+ state.save_restore(m_status);
+ state.save_restore(m_clock_prescale);
+ state.save_restore(m_irq_mask);
+ state.save_restore(m_irq_state);
+ state.save_restore(m_timer_running[0]);
+ state.save_restore(m_timer_running[1]);
+ state.save_restore(m_total_clocks);
+
+ // save the register/family data
+ m_regs.save_restore(state);
+
+ // save channel data
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ m_channel[chnum]->save_restore(state);
+
+ // save operator data
+ for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
+ m_operator[opnum]->save_restore(state);
+
+ // invalidate any caches
+ invalidate_caches();
+}
+
+
+//-------------------------------------------------
+// clock - iterate over all channels, clocking
+// them forward one step
+//-------------------------------------------------
+
+template<class RegisterType>
+uint32_t fm_engine_base<RegisterType>::clock(uint32_t chanmask)
+{
+ // update the clock counter
+ m_total_clocks++;
+
+ // if something was modified, prepare
+ // also prepare every 4k samples to catch ending notes
+ if (m_modified_channels != 0 || m_prepare_count++ >= 4096)
+ {
+ // reassign operators to channels if dynamic
+ if (RegisterType::DYNAMIC_OPS)
+ assign_operators();
+
+ // call each channel to prepare
+ m_active_channels = 0;
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ if (m_channel[chnum]->prepare())
+ m_active_channels |= 1 << chnum;
+
+ // reset the modified channels and prepare count
+ m_modified_channels = m_prepare_count = 0;
+ }
+
+ // if the envelope clock divider is 1, just increment by 4;
+ // otherwise, increment by 1 and manually wrap when we reach the divide count
+ if (RegisterType::EG_CLOCK_DIVIDER == 1)
+ m_env_counter += 4;
+ else if (bitfield(++m_env_counter, 0, 2) == RegisterType::EG_CLOCK_DIVIDER)
+ m_env_counter += 4 - RegisterType::EG_CLOCK_DIVIDER;
+
+ // clock the noise generator
+ int32_t lfo_raw_pm = m_regs.clock_noise_and_lfo();
+
+ // now update the state of all the channels and operators
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ m_channel[chnum]->clock(m_env_counter, lfo_raw_pm);
+
+ // return the envelope counter as it is used to clock ADPCM-A
+ return m_env_counter;
+}
+
+
+//-------------------------------------------------
+// output - compute a sum over the relevant
+// channels
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift, int32_t clipmax, uint32_t chanmask) const
+{
+ // mask out some channels for debug purposes
+ chanmask &= debug::GLOBAL_FM_CHANNEL_MASK;
+
+ // mask out inactive channels
+ if (!YMFM_DEBUG_LOG_WAVFILES)
+ chanmask &= m_active_channels;
+
+ // handle the rhythm case, where some of the operators are dedicated
+ // to percussion (this is an OPL-specific feature)
+ if (m_regs.rhythm_enable())
+ {
+ // we don't support the OPM noise channel here; ensure it is off
+ assert(m_regs.noise_enable() == 0);
+
+ // precompute the operator 13+17 phase selection value
+ uint32_t op13phase = m_operator[13]->phase();
+ uint32_t op17phase = m_operator[17]->phase();
+ uint32_t phase_select = (bitfield(op13phase, 2) ^ bitfield(op13phase, 7)) | bitfield(op13phase, 3) | (bitfield(op17phase, 5) ^ bitfield(op17phase, 3));
+
+ // sum over all the desired channels
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ {
+#if (YMFM_DEBUG_LOG_WAVFILES)
+ auto reference = output;
+#endif
+ if (chnum == 6)
+ m_channel[chnum]->output_rhythm_ch6(output, rshift, clipmax);
+ else if (chnum == 7)
+ m_channel[chnum]->output_rhythm_ch7(phase_select, output, rshift, clipmax);
+ else if (chnum == 8)
+ m_channel[chnum]->output_rhythm_ch8(phase_select, output, rshift, clipmax);
+ else if (m_channel[chnum]->is4op())
+ m_channel[chnum]->output_4op(output, rshift, clipmax);
+ else
+ m_channel[chnum]->output_2op(output, rshift, clipmax);
+#if (YMFM_DEBUG_LOG_WAVFILES)
+ m_wavfile[chnum].add(output, reference);
+#endif
+ }
+ }
+ else
+ {
+ // sum over all the desired channels
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ {
+#if (YMFM_DEBUG_LOG_WAVFILES)
+ auto reference = output;
+#endif
+ if (m_channel[chnum]->is4op())
+ m_channel[chnum]->output_4op(output, rshift, clipmax);
+ else
+ m_channel[chnum]->output_2op(output, rshift, clipmax);
+#if (YMFM_DEBUG_LOG_WAVFILES)
+ m_wavfile[chnum].add(output, reference);
+#endif
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the OPN registers
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_engine_base<RegisterType>::write(uint16_t regnum, uint8_t data)
+{
+ debug::log_fm_write("%03X = %02X\n", regnum, data);
+
+ // special case: writes to the mode register can impact IRQs;
+ // schedule these writes to ensure ordering with timers
+ if (regnum == RegisterType::REG_MODE)
+ {
+ m_intf.ymfm_sync_mode_write(data);
+ return;
+ }
+
+ // for now just mark all channels as modified
+ m_modified_channels = ALL_CHANNELS;
+
+ // most writes are passive, consumed only when needed
+ uint32_t keyon_channel;
+ uint32_t keyon_opmask;
+ if (m_regs.write(regnum, data, keyon_channel, keyon_opmask))
+ {
+ // handle writes to the keyon register(s)
+ if (keyon_channel < CHANNELS)
+ {
+ // normal channel on/off
+ m_channel[keyon_channel]->keyonoff(keyon_opmask, KEYON_NORMAL, keyon_channel);
+ }
+ else if (CHANNELS >= 9 && keyon_channel == RegisterType::RHYTHM_CHANNEL)
+ {
+ // special case for the OPL rhythm channels
+ m_channel[6]->keyonoff(bitfield(keyon_opmask, 4) ? 3 : 0, KEYON_RHYTHM, 6);
+ m_channel[7]->keyonoff(bitfield(keyon_opmask, 0) | (bitfield(keyon_opmask, 3) << 1), KEYON_RHYTHM, 7);
+ m_channel[8]->keyonoff(bitfield(keyon_opmask, 2) | (bitfield(keyon_opmask, 1) << 1), KEYON_RHYTHM, 8);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// status - return the current state of the
+// status flags
+//-------------------------------------------------
+
+template<class RegisterType>
+uint8_t fm_engine_base<RegisterType>::status() const
+{
+ return m_status & ~STATUS_BUSY & ~m_regs.status_mask();
+}
+
+
+//-------------------------------------------------
+// assign_operators - get the current mapping of
+// operators to channels and assign them all
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_engine_base<RegisterType>::assign_operators()
+{
+ typename RegisterType::operator_mapping map;
+ m_regs.operator_map(map);
+
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ for (uint32_t index = 0; index < 4; index++)
+ {
+ uint32_t opnum = bitfield(map.chan[chnum], 8 * index, 8);
+ m_channel[chnum]->assign(index, (opnum == 0xff) ? nullptr : m_operator[opnum].get());
+ }
+}
+
+
+//-------------------------------------------------
+// update_timer - update the state of the given
+// timer
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_engine_base<RegisterType>::update_timer(uint32_t tnum, uint32_t enable, int32_t delta_clocks)
+{
+ // 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 - 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;
+
+ // reset it
+ m_intf.ymfm_set_timer(tnum, period * OPERATORS * m_clock_prescale);
+ m_timer_running[tnum] = 1;
+ }
+
+ // if the timer is not live, ensure it is not enabled
+ else if (!enable)
+ {
+ m_intf.ymfm_set_timer(tnum, -1);
+ m_timer_running[tnum] = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// engine_timer_expired - timer has expired - signal
+// status and possibly IRQs
+//-------------------------------------------------
+
+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);
+ else if (tnum == 1 && m_regs.enable_timer_b())
+ set_reset_status(STATUS_TIMERB, 0);
+
+ // if timer A fired in CSM mode, trigger CSM on all relevant channels
+ if (tnum == 0 && m_regs.csm())
+ for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum))
+ {
+ m_channel[chnum]->keyonoff(0xf, KEYON_CSM, chnum);
+ m_modified_channels |= 1 << chnum;
+ }
+
+ // reset
+ m_timer_running[tnum] = false;
+ update_timer(tnum, 1, 0);
+}
+
+
+//-------------------------------------------------
+// check_interrupts - check the interrupt sources
+// for interrupts
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_engine_base<RegisterType>::engine_check_interrupts()
+{
+ // update the state
+ uint8_t old_state = m_irq_state;
+ m_irq_state = ((m_status & m_irq_mask & ~m_regs.status_mask()) != 0);
+
+ // set the IRQ status bit
+ if (m_irq_state)
+ m_status |= STATUS_IRQ;
+ else
+ m_status &= ~STATUS_IRQ;
+
+ // if changed, signal the new state
+ if (old_state != m_irq_state)
+ m_intf.ymfm_update_irq(m_irq_state ? true : false);
+}
+
+
+//-------------------------------------------------
+// engine_mode_write - handle a mode register write
+// via timer callback
+//-------------------------------------------------
+
+template<class RegisterType>
+void fm_engine_base<RegisterType>::engine_mode_write(uint8_t data)
+{
+ // mark all channels as modified
+ m_modified_channels = ALL_CHANNELS;
+
+ // actually write the mode register now
+ uint32_t dummy1, dummy2;
+ m_regs.write(RegisterType::REG_MODE, data, dummy1, dummy2);
+
+ // reset IRQ status -- when written, all other bits are ignored
+ // QUESTION: should this maybe just reset the IRQ bit and not all the bits?
+ // That is, check_interrupts would only set, this would only clear?
+ if (m_regs.irq_reset())
+ set_reset_status(0, 0x78);
+ else
+ {
+ // reset timer status
+ uint8_t reset_mask = 0;
+ if (m_regs.reset_timer_b())
+ reset_mask |= RegisterType::STATUS_TIMERB;
+ if (m_regs.reset_timer_a())
+ reset_mask |= RegisterType::STATUS_TIMERA;
+ set_reset_status(0, reset_mask);
+
+ // load timers; note that timer B gets a small negative adjustment because
+ // the *16 multiplier is free-running, so the first tick of the clock
+ // is a bit shorter
+ update_timer(1, m_regs.load_timer_b(), -(m_total_clocks & 15));
+ update_timer(0, m_regs.load_timer_a(), 0);
+ }
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_misc.cpp b/3rdparty/ymfm/src/ymfm_misc.cpp
new file mode 100644
index 00000000000..fd0575f55fb
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_misc.cpp
@@ -0,0 +1,175 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_misc.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// YM2149
+//*********************************************************
+
+//-------------------------------------------------
+// ym2149 - constructor
+//-------------------------------------------------
+
+ym2149::ym2149(ymfm_interface &intf) :
+ m_address(0),
+ m_ssg(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym2149::reset()
+{
+ // reset the engines
+ m_ssg.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym2149::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ m_ssg.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_data - read the data register
+//-------------------------------------------------
+
+uint8_t ym2149::read_data()
+{
+ return m_ssg.read(m_address & 0x0f);
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym2149::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 3) // BC2,BC1
+ {
+ case 0: // inactive
+ break;
+
+ case 1: // address
+ break;
+
+ case 2: // inactive
+ break;
+
+ case 3: // read
+ result = read_data();
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym2149::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2149::write_data(uint8_t data)
+{
+ m_ssg.write(m_address & 0x0f, data);
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2149::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 3) // BC2,BC1
+ {
+ case 0: // address
+ write_address(data);
+ break;
+
+ case 1: // inactive
+ break;
+
+ case 2: // write
+ write_data(data);
+ break;
+
+ case 3: // address
+ write_address(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate samples of SSG sound
+//-------------------------------------------------
+
+void ym2149::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the SSG
+ m_ssg.clock();
+
+ // YM2149 keeps the three SSG outputs independent
+ m_ssg.output(*output);
+ }
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_misc.h b/3rdparty/ymfm/src/ymfm_misc.h
new file mode 100644
index 00000000000..628d128f699
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_misc.h
@@ -0,0 +1,93 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_MISC_H
+#define YMFM_MISC_H
+
+#pragma once
+
+#include "ymfm.h"
+#include "ymfm_adpcm.h"
+#include "ymfm_ssg.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// SSG IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ym2149
+
+// ym2149 is just an SSG with no FM part, but we expose FM-like parts so that it
+// integrates smoothly with everything else; they just don't do anything
+class ym2149
+{
+public:
+ static constexpr uint32_t OUTPUTS = ssg_engine::OUTPUTS;
+ static constexpr uint32_t SSG_OUTPUTS = ssg_engine::OUTPUTS;
+ using output_data = ymfm_output<OUTPUTS>;
+
+ // constructor
+ ym2149(ymfm_interface &intf);
+
+ // configuration
+ void ssg_override(ssg_override &intf) { m_ssg.override(intf); }
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return input_clock / ssg_engine::CLOCK_DIVIDER / 8; }
+
+ // read access
+ uint8_t read_data();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ uint8_t m_address; // address register
+ ssg_engine m_ssg; // SSG engine
+};
+
+}
+
+#endif // YMFM_MISC_H
diff --git a/3rdparty/ymfm/src/ymfm_opl.cpp b/3rdparty/ymfm/src/ymfm_opl.cpp
new file mode 100644
index 00000000000..8e8025fd9cc
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opl.cpp
@@ -0,0 +1,2220 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_opl.h"
+#include "ymfm_fm.ipp"
+
+namespace ymfm
+{
+
+//-------------------------------------------------
+// opl_key_scale_atten - converts an
+// OPL concatenated block (3 bits) and fnum
+// (10 bits) into an attenuation offset; values
+// here are for 6dB/octave, in 0.75dB units
+// (matching total level LSB)
+//-------------------------------------------------
+
+inline uint32_t opl_key_scale_atten(uint32_t block, uint32_t fnum_4msb)
+{
+ // this table uses the top 4 bits of FNUM and are the maximal values
+ // (for when block == 7). Values for other blocks can be computed by
+ // subtracting 8 for each block below 7.
+ static uint8_t const fnum_to_atten[16] = { 0,24,32,37,40,43,45,47,48,50,51,52,53,54,55,56 };
+ int32_t result = fnum_to_atten[fnum_4msb] - 8 * (block ^ 7);
+ return std::max<int32_t>(0, result);
+}
+
+
+//*********************************************************
+// OPL REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// opl_registers_base - constructor
+//-------------------------------------------------
+
+template<int Revision>
+opl_registers_base<Revision>::opl_registers_base() :
+ m_lfo_am_counter(0),
+ m_lfo_pm_counter(0),
+ m_noise_lfsr(1),
+ m_lfo_am(0)
+{
+ // create these pointers to appease overzealous compilers checking array
+ // bounds in unreachable code (looking at you, clang)
+ uint16_t *wf0 = &m_waveform[0][0];
+ uint16_t *wf1 = &m_waveform[1 % WAVEFORMS][0];
+ uint16_t *wf2 = &m_waveform[2 % WAVEFORMS][0];
+ uint16_t *wf3 = &m_waveform[3 % WAVEFORMS][0];
+ uint16_t *wf4 = &m_waveform[4 % WAVEFORMS][0];
+ uint16_t *wf5 = &m_waveform[5 % WAVEFORMS][0];
+ uint16_t *wf6 = &m_waveform[6 % WAVEFORMS][0];
+ uint16_t *wf7 = &m_waveform[7 % WAVEFORMS][0];
+
+ // create the waveforms
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ wf0[index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15);
+
+ if (WAVEFORMS >= 4)
+ {
+ uint16_t zeroval = wf0[0];
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ {
+ wf1[index] = bitfield(index, 9) ? zeroval : wf0[index];
+ wf2[index] = wf0[index] & 0x7fff;
+ wf3[index] = bitfield(index, 8) ? zeroval : (wf0[index] & 0x7fff);
+ if (WAVEFORMS >= 8)
+ {
+ wf4[index] = bitfield(index, 9) ? zeroval : wf0[index * 2];
+ wf5[index] = bitfield(index, 9) ? zeroval : wf0[(index * 2) & 0x1ff];
+ wf6[index] = bitfield(index, 9) << 15;
+ wf7[index] = (bitfield(index, 9) ? (index ^ 0x13ff) : index) << 3;
+ }
+ }
+ }
+
+ // OPL3/OPL4 have dynamic operators, so initialize the fourop_enable value here
+ // since operator_map() is called right away, prior to reset()
+ if (Revision > 2)
+ m_regdata[0x104 % REGISTERS] = 0;
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+template<int Revision>
+void opl_registers_base<Revision>::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+template<int Revision>
+void opl_registers_base<Revision>::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_lfo_am_counter);
+ state.save_restore(m_lfo_pm_counter);
+ state.save_restore(m_lfo_am);
+ state.save_restore(m_noise_lfsr);
+ state.save_restore(m_regdata);
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPL this is fixed
+//-------------------------------------------------
+
+template<int Revision>
+void opl_registers_base<Revision>::operator_map(operator_mapping &dest) const
+{
+ if (Revision <= 2)
+ {
+ // OPL/OPL2 has a fixed map, all 2 operators
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 3 ), // Channel 0 operators
+ operator_list( 1, 4 ), // Channel 1 operators
+ operator_list( 2, 5 ), // Channel 2 operators
+ operator_list( 6, 9 ), // Channel 3 operators
+ operator_list( 7, 10 ), // Channel 4 operators
+ operator_list( 8, 11 ), // Channel 5 operators
+ operator_list( 12, 15 ), // Channel 6 operators
+ operator_list( 13, 16 ), // Channel 7 operators
+ operator_list( 14, 17 ), // Channel 8 operators
+ } };
+ dest = s_fixed_map;
+ }
+ else
+ {
+ // OPL3/OPL4 can be configured for 2 or 4 operators
+ uint32_t fourop = fourop_enable();
+
+ dest.chan[ 0] = bitfield(fourop, 0) ? operator_list( 0, 3, 6, 9 ) : operator_list( 0, 3 );
+ dest.chan[ 1] = bitfield(fourop, 1) ? operator_list( 1, 4, 7, 10 ) : operator_list( 1, 4 );
+ dest.chan[ 2] = bitfield(fourop, 2) ? operator_list( 2, 5, 8, 11 ) : operator_list( 2, 5 );
+ dest.chan[ 3] = bitfield(fourop, 0) ? operator_list() : operator_list( 6, 9 );
+ dest.chan[ 4] = bitfield(fourop, 1) ? operator_list() : operator_list( 7, 10 );
+ dest.chan[ 5] = bitfield(fourop, 2) ? operator_list() : operator_list( 8, 11 );
+ dest.chan[ 6] = operator_list( 12, 15 );
+ dest.chan[ 7] = operator_list( 13, 16 );
+ dest.chan[ 8] = operator_list( 14, 17 );
+
+ dest.chan[ 9] = bitfield(fourop, 3) ? operator_list( 18, 21, 24, 27 ) : operator_list( 18, 21 );
+ dest.chan[10] = bitfield(fourop, 4) ? operator_list( 19, 22, 25, 28 ) : operator_list( 19, 22 );
+ dest.chan[11] = bitfield(fourop, 5) ? operator_list( 20, 23, 26, 29 ) : operator_list( 20, 23 );
+ dest.chan[12] = bitfield(fourop, 3) ? operator_list() : operator_list( 24, 27 );
+ dest.chan[13] = bitfield(fourop, 4) ? operator_list() : operator_list( 25, 28 );
+ dest.chan[14] = bitfield(fourop, 5) ? operator_list() : operator_list( 26, 29 );
+ dest.chan[15] = operator_list( 30, 33 );
+ dest.chan[16] = operator_list( 31, 34 );
+ dest.chan[17] = operator_list( 32, 35 );
+ }
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array
+//-------------------------------------------------
+
+template<int Revision>
+bool opl_registers_base<Revision>::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
+{
+ assert(index < REGISTERS);
+
+ // writes to the mode register with high bit set ignore the low bits
+ if (index == REG_MODE && bitfield(data, 7) != 0)
+ m_regdata[index] |= 0x80;
+ else
+ m_regdata[index] = data;
+
+ // handle writes to the rhythm keyons
+ if (index == 0xbd)
+ {
+ channel = RHYTHM_CHANNEL;
+ opmask = bitfield(data, 5) ? bitfield(data, 0, 5) : 0;
+ return true;
+ }
+
+ // handle writes to the channel keyons
+ if ((index & 0xf0) == 0xb0)
+ {
+ channel = index & 0x0f;
+ if (channel < 9)
+ {
+ if (IsOpl3Plus)
+ channel += 9 * bitfield(index, 8);
+ opmask = bitfield(data, 5) ? 15 : 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+static int32_t opl_clock_noise_and_lfo(uint32_t &noise_lfsr, uint16_t &lfo_am_counter, uint16_t &lfo_pm_counter, uint8_t &lfo_am, uint32_t am_depth, uint32_t pm_depth)
+{
+ // OPL has a 23-bit noise generator for the rhythm section, running at
+ // a constant rate, used only for percussion input
+ noise_lfsr <<= 1;
+ noise_lfsr |= bitfield(noise_lfsr, 23) ^ bitfield(noise_lfsr, 9) ^ bitfield(noise_lfsr, 8) ^ bitfield(noise_lfsr, 1);
+
+ // OPL has two fixed-frequency LFOs, one for AM, one for PM
+
+ // the AM LFO has 210*64 steps; at a nominal 50kHz output,
+ // this equates to a period of 50000/(210*64) = 3.72Hz
+ uint32_t am_counter = lfo_am_counter++;
+ if (am_counter >= 210*64 - 1)
+ lfo_am_counter = 0;
+
+ // low 8 bits are fractional; depth 0 is divided by 2, while depth 1 is times 2
+ int shift = 9 - 2 * am_depth;
+
+ // AM value is the upper bits of the value, inverted across the midpoint
+ // to produce a triangle
+ lfo_am = ((am_counter < 105*64) ? am_counter : (210*64+63 - am_counter)) >> shift;
+
+ // the PM LFO has 8192 steps, or a nominal period of 6.1Hz
+ uint32_t pm_counter = lfo_pm_counter++;
+
+ // PM LFO is broken into 8 chunks, each lasting 1024 steps; the PM value
+ // depends on the upper bits of FNUM, so this value is a fraction and
+ // sign to apply to that value, as a 1.3 value
+ static int8_t const pm_scale[8] = { 8, 4, 0, -4, -8, -4, 0, 4 };
+ return pm_scale[bitfield(pm_counter, 10, 3)] >> (pm_depth ^ 1);
+}
+
+template<int Revision>
+int32_t opl_registers_base<Revision>::clock_noise_and_lfo()
+{
+ return opl_clock_noise_and_lfo(m_noise_lfsr, m_lfo_am_counter, m_lfo_pm_counter, m_lfo_am, lfo_am_depth(), lfo_pm_depth());
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data; note that this code is
+// also used by ymopna_registers, so it must
+// handle upper channels cleanly
+//-------------------------------------------------
+
+template<int Revision>
+void opl_registers_base<Revision>::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
+{
+ // set up the easy stuff
+ cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0];
+
+ // get frequency from the channel
+ uint32_t block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // compute the keycode: block_freq is:
+ //
+ // 111 |
+ // 21098|76543210
+ // BBBFF|FFFFFFFF
+ // ^^^??
+ //
+ // the 4-bit keycode uses the top 3 bits plus one of the next two bits
+ uint32_t keycode = bitfield(block_freq, 10, 3) << 1;
+
+ // lowest bit is determined by note_select(); note that it is
+ // actually reversed from what the manual says, however
+ keycode |= bitfield(block_freq, 9 - note_select(), 1);
+
+ // no detune adjustment on OPL
+ cache.detune = 0;
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15
+ uint32_t multiple = op_multiple(opoffs);
+ cache.multiple = ((multiple & 0xe) | bitfield(0xc2aa, multiple)) * 2;
+ if (cache.multiple == 0)
+ cache.multiple = 1;
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on block_freq, detune,
+ // and multiple, so compute it after we've done those
+ if (op_lfo_pm_enable(opoffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8
+ cache.total_level = op_total_level(opoffs) << 3;
+
+ // pre-add key scale level
+ uint32_t ksl = op_ksl(opoffs);
+ if (ksl != 0)
+ cache.total_level += opl_key_scale_atten(bitfield(block_freq, 10, 3), bitfield(block_freq, 6, 4)) << ksl;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // determine KSR adjustment for enevlope rates
+ uint32_t ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1));
+ cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[EG_SUSTAIN] = op_eg_sustain(opoffs) ? 0 : effective_rate(op_release_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[EG_DEPRESS] = 0x3f;
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+static uint32_t opl_compute_phase_step(uint32_t block_freq, uint32_t multiple, int32_t lfo_raw_pm)
+{
+ // OPL phase calculation has no detuning, but uses FNUMs like
+ // the OPN version, and computes PM a bit differently
+
+ // extract frequency number as a 12-bit fraction
+ uint32_t fnum = bitfield(block_freq, 0, 10) << 2;
+
+ // apply the phase adjustment based on the upper 3 bits
+ // of FNUM and the PM depth parameters
+ fnum += (lfo_raw_pm * bitfield(block_freq, 7, 3)) >> 1;
+
+ // keep fnum to 12 bits
+ fnum &= 0xfff;
+
+ // apply block shift to compute phase step
+ uint32_t block = bitfield(block_freq, 10, 3);
+ uint32_t phase_step = (fnum << block) >> 2;
+
+ // apply frequency multiplier (which is cached as an x.1 value)
+ return (phase_step * multiple) >> 1;
+}
+
+template<int Revision>
+uint32_t opl_registers_base<Revision>::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm)
+{
+ return opl_compute_phase_step(cache.block_freq, cache.multiple, op_lfo_pm_enable(opoffs) ? lfo_raw_pm : 0);
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+template<int Revision>
+std::string opl_registers_base<Revision>::log_keyon(uint32_t choffs, uint32_t opoffs)
+{
+ uint32_t chnum = (choffs & 15) + 9 * bitfield(choffs, 8);
+ uint32_t opnum = (opoffs & 31) - 2 * ((opoffs & 31) / 8) + 18 * bitfield(opoffs, 8);
+
+ char buffer[256];
+ int end = 0;
+
+ 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),
+ ch_algorithm(choffs),
+ op_multiple(opoffs),
+ op_total_level(opoffs),
+ op_ksr(opoffs),
+ note_select(),
+ op_ksl(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs),
+ op_eg_sustain(opoffs));
+
+ if (OUTPUTS > 1)
+ 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(&buffer[end], sizeof(buffer) - end, " am=%u", lfo_am_depth());
+ if (op_lfo_pm_enable(opoffs) != 0)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", lfo_pm_depth());
+ if (waveform_enable() && op_waveform(opoffs) != 0)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs));
+ if (is_rhythm(choffs))
+ 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(&buffer[end], sizeof(buffer) - end, " 4op");
+ }
+
+ return buffer;
+}
+
+
+//*********************************************************
+// OPLL SPECIFICS
+//*********************************************************
+
+//-------------------------------------------------
+// opll_registers - constructor
+//-------------------------------------------------
+
+opll_registers::opll_registers() :
+ m_lfo_am_counter(0),
+ m_lfo_pm_counter(0),
+ m_noise_lfsr(1),
+ m_lfo_am(0)
+{
+ // create the waveforms
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15);
+
+ uint16_t zeroval = m_waveform[0][0];
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[1][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index];
+
+ // initialize the instruments to something sane
+ for (uint32_t choffs = 0; choffs < CHANNELS; choffs++)
+ m_chinst[choffs] = &m_regdata[0];
+ for (uint32_t opoffs = 0; opoffs < OPERATORS; opoffs++)
+ m_opinst[opoffs] = &m_regdata[bitfield(opoffs, 0)];
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+void opll_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void opll_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_lfo_am_counter);
+ state.save_restore(m_lfo_pm_counter);
+ state.save_restore(m_lfo_am);
+ state.save_restore(m_noise_lfsr);
+ state.save_restore(m_regdata);
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPLL this is fixed
+//-------------------------------------------------
+
+void opll_registers::operator_map(operator_mapping &dest) const
+{
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 1 ), // Channel 0 operators
+ operator_list( 2, 3 ), // Channel 1 operators
+ operator_list( 4, 5 ), // Channel 2 operators
+ operator_list( 6, 7 ), // Channel 3 operators
+ operator_list( 8, 9 ), // Channel 4 operators
+ operator_list( 10, 11 ), // Channel 5 operators
+ operator_list( 12, 13 ), // Channel 6 operators
+ operator_list( 14, 15 ), // Channel 7 operators
+ operator_list( 16, 17 ), // Channel 8 operators
+ } };
+ dest = s_fixed_map;
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array;
+// note that this code is also used by
+// ymopl3_registers, so it must handle upper
+// channels cleanly
+//-------------------------------------------------
+
+bool opll_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
+{
+ // unclear the address is masked down to 6 bits or if writes above
+ // the register top are ignored; assuming the latter for now
+ if (index >= REGISTERS)
+ return false;
+
+ // write the new data
+ m_regdata[index] = data;
+
+ // handle writes to the rhythm keyons
+ if (index == 0x0e)
+ {
+ channel = RHYTHM_CHANNEL;
+ opmask = bitfield(data, 5) ? bitfield(data, 0, 5) : 0;
+ return true;
+ }
+
+ // handle writes to the channel keyons
+ if ((index & 0xf0) == 0x20)
+ {
+ channel = index & 0x0f;
+ if (channel < CHANNELS)
+ {
+ opmask = bitfield(data, 4) ? 3 : 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+int32_t opll_registers::clock_noise_and_lfo()
+{
+ // implementation is the same as OPL with fixed depths
+ return opl_clock_noise_and_lfo(m_noise_lfsr, m_lfo_am_counter, m_lfo_pm_counter, m_lfo_am, 1, 1);
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data; note that this code is
+// also used by ymopna_registers, so it must
+// handle upper channels cleanly
+//-------------------------------------------------
+
+void opll_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
+{
+ // first set up the instrument data
+ uint32_t instrument = ch_instrument(choffs);
+ if (rhythm_enable() && choffs >= 6)
+ m_chinst[choffs] = &m_instdata[8 * (15 + (choffs - 6))];
+ else
+ m_chinst[choffs] = (instrument == 0) ? &m_regdata[0] : &m_instdata[8 * (instrument - 1)];
+ m_opinst[opoffs] = m_chinst[choffs] + bitfield(opoffs, 0);
+
+ // set up the easy stuff
+ cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0];
+
+ // get frequency from the channel
+ uint32_t block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // compute the keycode: block_freq is:
+ //
+ // 11 |
+ // 1098|76543210
+ // BBBF|FFFFFFFF
+ // ^^^^
+ //
+ // the 4-bit keycode uses the top 4 bits
+ uint32_t keycode = bitfield(block_freq, 8, 4);
+
+ // no detune adjustment on OPLL
+ cache.detune = 0;
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15
+ uint32_t multiple = op_multiple(opoffs);
+ cache.multiple = ((multiple & 0xe) | bitfield(0xc2aa, multiple)) * 2;
+ if (cache.multiple == 0)
+ cache.multiple = 1;
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on
+ // block_freq, detune, and multiple, so compute it after we've done those
+ if (op_lfo_pm_enable(opoffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8; for non-rhythm operator 0, this is the total
+ // level from the instrument data; for other operators it is 4*volume
+ if (bitfield(opoffs, 0) == 1 || (rhythm_enable() && choffs >= 7))
+ cache.total_level = op_volume(opoffs) * 4;
+ else
+ cache.total_level = ch_total_level(choffs);
+ cache.total_level <<= 3;
+
+ // pre-add key scale level
+ uint32_t ksl = op_ksl(opoffs);
+ if (ksl != 0)
+ cache.total_level += opl_key_scale_atten(bitfield(block_freq, 9, 3), bitfield(block_freq, 5, 4)) << ksl;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // The envelope diagram in the YM2413 datasheet gives values for these
+ // in ms from 0->48dB. The attack/decay tables give values in ms from
+ // 0->96dB, so to pick an equivalent decay rate, we want to find the
+ // closest match that is 2x the 0->48dB value:
+ //
+ // DP = 10ms (0->48db) -> 20ms (0->96db); decay of 12 gives 19.20ms
+ // RR = 310ms (0->48db) -> 620ms (0->96db); decay of 7 gives 613.76ms
+ // RS = 1200ms (0->48db) -> 2400ms (0->96db); decay of 5 gives 2455.04ms
+ //
+ // The envelope diagram for percussive sounds (eg_sustain() == 0) also uses
+ // "RR" to mean both the constant RR above and the Release Rate specified in
+ // the instrument data. In this case, Relief Pitcher's credit sound bears out
+ // that the Release Rate is used during sustain, and that the constant RR
+ // (or RS) is used during the release phase.
+ constexpr uint8_t DP = 12 * 4;
+ constexpr uint8_t RR = 7 * 4;
+ constexpr uint8_t RS = 5 * 4;
+
+ // determine KSR adjustment for envelope rates
+ uint32_t ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1));
+ cache.eg_rate[EG_DEPRESS] = DP;
+ cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval);
+ if (op_eg_sustain(opoffs))
+ {
+ cache.eg_rate[EG_SUSTAIN] = 0;
+ cache.eg_rate[EG_RELEASE] = ch_sustain(choffs) ? RS : effective_rate(op_release_rate(opoffs) * 4, ksrval);
+ }
+ else
+ {
+ cache.eg_rate[EG_SUSTAIN] = effective_rate(op_release_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[EG_RELEASE] = ch_sustain(choffs) ? RS : RR;
+ }
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+uint32_t opll_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm)
+{
+ // phase step computation is the same as OPL but the block_freq has one
+ // more bit, which we shift in
+ return opl_compute_phase_step(cache.block_freq << 1, cache.multiple, op_lfo_pm_enable(opoffs) ? lfo_raw_pm : 0);
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
+{
+ uint32_t chnum = choffs;
+ uint32_t opnum = opoffs;
+
+ char buffer[256];
+ int end = 0;
+
+ 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),
+ ch_feedback(choffs),
+ op_multiple(opoffs));
+
+ if (bitfield(opoffs, 0) == 1 || (is_rhythm(choffs) && choffs >= 6))
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " vol=%X", op_volume(opoffs));
+ else
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " tl=%02X", ch_total_level(choffs));
+
+ 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),
+ op_decay_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs),
+ op_eg_sustain(opoffs),
+ ch_sustain(choffs));
+
+ if (op_lfo_am_enable(opoffs))
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " am=1");
+ if (op_lfo_pm_enable(opoffs))
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=1");
+ if (op_waveform(opoffs) != 0)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=1");
+ if (is_rhythm(choffs))
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " rhy=1");
+
+ return buffer;
+}
+
+
+
+//*********************************************************
+// YM3526
+//*********************************************************
+
+//-------------------------------------------------
+// ym3526 - constructor
+//-------------------------------------------------
+
+ym3526::ym3526(ymfm_interface &intf) :
+ m_address(0),
+ m_fm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym3526::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym3526::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ m_fm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym3526::read_status()
+{
+ return m_fm.status() | 0x06;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym3526::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 1)
+ {
+ case 0: // status port
+ result = read_status();
+ break;
+
+ case 1: // when A0=1 datasheet says "the data on the bus are not guaranteed"
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym3526::write_address(uint8_t data)
+{
+ // YM3526 doesn't expose a busy signal, and the datasheets don't indicate
+ // delays, but all other OPL chips need 12 cycles for address writes
+ m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale());
+
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym3526::write_data(uint8_t data)
+{
+ // YM3526 doesn't expose a busy signal, and the datasheets don't indicate
+ // delays, but all other OPL chips need 84 cycles for data writes
+ m_fm.intf().ymfm_set_busy_end(84 * m_fm.clock_prescale());
+
+ // write to FM
+ m_fm.write(m_address, data);
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym3526::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 1)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate samples of sound
+//-------------------------------------------------
+
+void ym3526::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; mixing details for YM3526 need verification
+ m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS);
+
+ // YM3526 uses an external DAC (YM3014) with mantissa/exponent format
+ // convert to 10.3 floating point value and back to simulate truncation
+ output->roundtrip_fp();
+ }
+}
+
+
+
+//*********************************************************
+// Y8950
+//*********************************************************
+
+//-------------------------------------------------
+// y8950 - constructor
+//-------------------------------------------------
+
+y8950::y8950(ymfm_interface &intf) :
+ m_address(0),
+ m_io_ddr(0),
+ m_fm(intf),
+ m_adpcm_b(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void y8950::reset()
+{
+ // reset the engines
+ m_fm.reset();
+ m_adpcm_b.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void y8950::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ state.save_restore(m_io_ddr);
+ m_fm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t y8950::read_status()
+{
+ // start with current FM status, masking out bits we might set
+ uint8_t status = m_fm.status() & ~(STATUS_ADPCM_B_EOS | STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_PLAYING);
+
+ // insert the live ADPCM status bits
+ uint8_t adpcm_status = m_adpcm_b.status();
+ if ((adpcm_status & adpcm_b_channel::STATUS_EOS) != 0)
+ status |= STATUS_ADPCM_B_EOS;
+ if ((adpcm_status & adpcm_b_channel::STATUS_BRDY) != 0)
+ status |= STATUS_ADPCM_B_BRDY;
+ if ((adpcm_status & adpcm_b_channel::STATUS_PLAYING) != 0)
+ status |= STATUS_ADPCM_B_PLAYING;
+
+ // run it through the FM engine to handle interrupts for us
+ return m_fm.set_reset_status(status, ~status);
+}
+
+
+//-------------------------------------------------
+// read_data - read the data port
+//-------------------------------------------------
+
+uint8_t y8950::read_data()
+{
+ uint8_t result = 0xff;
+ switch (m_address)
+ {
+ case 0x05: // keyboard in
+ result = m_fm.intf().ymfm_external_read(ACCESS_IO, 1);
+ break;
+
+ case 0x09: // ADPCM data
+ case 0x1a:
+ result = m_adpcm_b.read(m_address - 0x07);
+ break;
+
+ case 0x19: // I/O data
+ result = m_fm.intf().ymfm_external_read(ACCESS_IO, 0);
+ break;
+
+ default:
+ debug::log_unexpected_read_write("Unexpected read from Y8950 data port %02X\n", m_address);
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t y8950::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 1)
+ {
+ case 0: // status port
+ result = read_status();
+ break;
+
+ case 1: // when A0=1 datasheet says "the data on the bus are not guaranteed"
+ result = read_data();
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void y8950::write_address(uint8_t data)
+{
+ // Y8950 doesn't expose a busy signal, but it does indicate that
+ // address writes should be no faster than every 12 clocks
+ m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale());
+
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void y8950::write_data(uint8_t data)
+{
+ // Y8950 doesn't expose a busy signal, but it does indicate that
+ // data writes should be no faster than every 12 clocks for
+ // registers 00-1A, or every 84 clocks for other registers
+ m_fm.intf().ymfm_set_busy_end(((m_address <= 0x1a) ? 12 : 84) * m_fm.clock_prescale());
+
+ // handle special addresses
+ switch (m_address)
+ {
+ case 0x04: // IRQ control
+ m_fm.write(m_address, data);
+ read_status();
+ break;
+
+ case 0x06: // keyboard out
+ m_fm.intf().ymfm_external_write(ACCESS_IO, 1, data);
+ break;
+
+ case 0x08: // split FM/ADPCM-B
+ m_adpcm_b.write(m_address - 0x07, (data & 0x0f) | 0x80);
+ m_fm.write(m_address, data & 0xc0);
+ break;
+
+ case 0x07: // ADPCM-B registers
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ m_adpcm_b.write(m_address - 0x07, data);
+ break;
+
+ case 0x18: // I/O direction
+ m_io_ddr = data & 0x0f;
+ break;
+
+ case 0x19: // I/O data
+ m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data & m_io_ddr);
+ break;
+
+ default: // everything else to FM
+ m_fm.write(m_address, data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void y8950::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 1)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate samples of sound
+//-------------------------------------------------
+
+void y8950::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+ m_adpcm_b.clock();
+
+ // update the FM content; clipping need verification
+ m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS);
+
+ // mix in the ADPCM; ADPCM-B is stereo, but only one channel
+ // not sure how it's wired up internally
+ m_adpcm_b.output(*output, 3);
+
+ // Y8950 uses an external DAC (YM3014) with mantissa/exponent format
+ // convert to 10.3 floating point value and back to simulate truncation
+ output->roundtrip_fp();
+ }
+}
+
+
+
+//*********************************************************
+// YM3812
+//*********************************************************
+
+//-------------------------------------------------
+// ym3812 - constructor
+//-------------------------------------------------
+
+ym3812::ym3812(ymfm_interface &intf) :
+ m_address(0),
+ m_fm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym3812::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym3812::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ m_fm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym3812::read_status()
+{
+ return m_fm.status() | 0x06;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym3812::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 1)
+ {
+ case 0: // status port
+ result = read_status();
+ break;
+
+ case 1: // "inhibit" according to datasheet
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym3812::write_address(uint8_t data)
+{
+ // YM3812 doesn't expose a busy signal, but it does indicate that
+ // address writes should be no faster than every 12 clocks
+ m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale());
+
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym3812::write_data(uint8_t data)
+{
+ // YM3812 doesn't expose a busy signal, but it does indicate that
+ // data writes should be no faster than every 84 clocks
+ m_fm.intf().ymfm_set_busy_end(84 * m_fm.clock_prescale());
+
+ // write to FM
+ m_fm.write(m_address, data);
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym3812::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 1)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate samples of sound
+//-------------------------------------------------
+
+void ym3812::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; mixing details for YM3812 need verification
+ m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS);
+
+ // YM3812 uses an external DAC (YM3014) with mantissa/exponent format
+ // convert to 10.3 floating point value and back to simulate truncation
+ output->roundtrip_fp();
+ }
+}
+
+
+
+//*********************************************************
+// YMF262
+//*********************************************************
+
+//-------------------------------------------------
+// ymf262 - constructor
+//-------------------------------------------------
+
+ymf262::ymf262(ymfm_interface &intf) :
+ m_address(0),
+ m_fm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ymf262::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ymf262::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ m_fm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ymf262::read_status()
+{
+ return m_fm.status();
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ymf262::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 3)
+ {
+ case 0: // status port
+ result = read_status();
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ debug::log_unexpected_read_write("Unexpected read from YMF262 offset %d\n", offset & 3);
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ymf262::write_address(uint8_t data)
+{
+ // YMF262 doesn't expose a busy signal, but it does indicate that
+ // address writes should be no faster than every 32 clocks
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write_data - handle a write to the data
+// register
+//-------------------------------------------------
+
+void ymf262::write_data(uint8_t data)
+{
+ // YMF262 doesn't expose a busy signal, but it does indicate that
+ // data writes should be no faster than every 32 clocks
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+
+ // write to FM
+ m_fm.write(m_address, data);
+}
+
+
+//-------------------------------------------------
+// write_address_hi - handle a write to the upper
+// address register
+//-------------------------------------------------
+
+void ymf262::write_address_hi(uint8_t data)
+{
+ // YMF262 doesn't expose a busy signal, but it does indicate that
+ // address writes should be no faster than every 32 clocks
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+
+ // just set the address
+ m_address = data | 0x100;
+
+ // tests reveal that in compatibility mode, upper bit is masked
+ // except for register 0x105
+ if (m_fm.regs().newflag() == 0 && m_address != 0x105)
+ m_address &= 0xff;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ymf262::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 3)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+
+ case 2: // address port
+ write_address_hi(data);
+ break;
+
+ case 3: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate samples of sound
+//-------------------------------------------------
+
+void ymf262::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; mixing details for YMF262 need verification
+ m_fm.output(output->clear(), 0, 32767, fm_engine::ALL_CHANNELS);
+
+ // YMF262 output is 16-bit offset serial via YAC512 DAC
+ output->clamp16();
+ }
+}
+
+
+
+//*********************************************************
+// YMF289B
+//*********************************************************
+
+// YMF289B is a YMF262 with the following changes:
+// * "Power down" mode added
+// * Bulk register clear added
+// * Busy flag added to the status register
+// * Shorter busy times
+// * All registers can be read
+// * Only 2 outputs exposed
+
+//-------------------------------------------------
+// ymf289b - constructor
+//-------------------------------------------------
+
+ymf289b::ymf289b(ymfm_interface &intf) :
+ m_address(0),
+ m_fm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ymf289b::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ymf289b::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ m_fm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ymf289b::read_status()
+{
+ uint8_t result = m_fm.status();
+
+ // YMF289B adds a busy flag
+ if (ymf289b_mode() && m_fm.intf().ymfm_is_busy())
+ result |= STATUS_BUSY_FLAGS;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read_data - read the data register
+//-------------------------------------------------
+
+uint8_t ymf289b::read_data()
+{
+ uint8_t result = 0xff;
+
+ // YMF289B can read register data back
+ if (ymf289b_mode())
+ result = m_fm.regs().read(m_address);
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ymf289b::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 3)
+ {
+ case 0: // status port
+ result = read_status();
+ break;
+
+ case 1: // data port
+ result = read_data();
+ break;
+
+ case 2:
+ case 3:
+ debug::log_unexpected_read_write("Unexpected read from YMF289B offset %d\n", offset & 3);
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ymf289b::write_address(uint8_t data)
+{
+ m_address = data;
+
+ // count busy time
+ m_fm.intf().ymfm_set_busy_end(56);
+}
+
+
+//-------------------------------------------------
+// write_data - handle a write to the data
+// register
+//-------------------------------------------------
+
+void ymf289b::write_data(uint8_t data)
+{
+ // write to FM
+ m_fm.write(m_address, data);
+
+ // writes to 0x108 with the CLR flag set clear the registers
+ if (m_address == 0x108 && bitfield(data, 2) != 0)
+ m_fm.regs().reset();
+
+ // count busy time
+ m_fm.intf().ymfm_set_busy_end(56);
+}
+
+
+//-------------------------------------------------
+// write_address_hi - handle a write to the upper
+// address register
+//-------------------------------------------------
+
+void ymf289b::write_address_hi(uint8_t data)
+{
+ // just set the address
+ m_address = data | 0x100;
+
+ // tests reveal that in compatibility mode, upper bit is masked
+ // except for register 0x105
+ if (m_fm.regs().newflag() == 0 && m_address != 0x105)
+ m_address &= 0xff;
+
+ // count busy time
+ m_fm.intf().ymfm_set_busy_end(56);
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ymf289b::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 3)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+
+ case 2: // address port
+ write_address_hi(data);
+ break;
+
+ case 3: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate samples of sound
+//-------------------------------------------------
+
+void ymf289b::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; mixing details for YMF262 need verification
+ fm_engine::output_data full;
+ m_fm.output(full.clear(), 0, 32767, fm_engine::ALL_CHANNELS);
+
+ // YMF278B output is 16-bit offset serial via YAC512 DAC, but
+ // only 2 of the 4 outputs are exposed
+ output->data[0] = full.data[0];
+ output->data[1] = full.data[1];
+ output->clamp16();
+ }
+}
+
+
+
+//*********************************************************
+// YMF278B
+//*********************************************************
+
+//-------------------------------------------------
+// ymf278b - constructor
+//-------------------------------------------------
+
+ymf278b::ymf278b(ymfm_interface &intf) :
+ m_address(0),
+ m_fm_pos(0),
+ m_load_remaining(0),
+ m_next_status_id(false),
+ m_fm(intf),
+ m_pcm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ymf278b::reset()
+{
+ // reset the engines
+ m_fm.reset();
+ m_pcm.reset();
+
+ // next status read will return ID
+ m_next_status_id = true;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ymf278b::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ state.save_restore(m_fm_pos);
+ state.save_restore(m_load_remaining);
+ state.save_restore(m_next_status_id);
+ m_fm.save_restore(state);
+ m_pcm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ymf278b::read_status()
+{
+ uint8_t result;
+
+ // first status read after initialization returns a chip ID, which
+ // varies based on the "new" flags, indicating the mode
+ if (m_next_status_id)
+ {
+ if (m_fm.regs().new2flag())
+ result = 0x02;
+ else if (m_fm.regs().newflag())
+ result = 0x00;
+ else
+ result = 0x06;
+ m_next_status_id = false;
+ }
+ else
+ {
+ result = m_fm.status();
+ if (m_fm.intf().ymfm_is_busy())
+ result |= STATUS_BUSY;
+ if (m_load_remaining != 0)
+ result |= STATUS_LD;
+
+ // if new2 flag is not set, we're in OPL2 or OPL3 mode
+ if (!m_fm.regs().new2flag())
+ result &= ~(STATUS_BUSY | STATUS_LD);
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_data_pcm - handle a write to the PCM data
+// register
+//-------------------------------------------------
+
+uint8_t ymf278b::read_data_pcm()
+{
+ // read from PCM
+ if (bitfield(m_address, 9) != 0)
+ {
+ uint8_t result = m_pcm.read(m_address & 0xff);
+ if ((m_address & 0xff) == 0x02)
+ result |= 0x20;
+
+ return result;
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ymf278b::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 7)
+ {
+ case 0: // status port
+ result = read_status();
+ break;
+
+ case 5: // PCM data port
+ result = read_data_pcm();
+ break;
+
+ default:
+ debug::log_unexpected_read_write("Unexpected read from ymf278b offset %d\n", offset & 3);
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ymf278b::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write_data - handle a write to the data
+// register
+//-------------------------------------------------
+
+void ymf278b::write_data(uint8_t data)
+{
+ // write to FM
+ if (bitfield(m_address, 9) == 0)
+ {
+ uint8_t old = m_fm.regs().new2flag();
+ m_fm.write(m_address, data);
+
+ // changing NEW2 from 0->1 causes the next status read to
+ // return the chip ID
+ if (old == 0 && m_fm.regs().new2flag() != 0)
+ m_next_status_id = true;
+ }
+
+ // BUSY goes for 56 clocks on FM writes
+ m_fm.intf().ymfm_set_busy_end(56);
+}
+
+
+//-------------------------------------------------
+// write_address_hi - handle a write to the upper
+// address register
+//-------------------------------------------------
+
+void ymf278b::write_address_hi(uint8_t data)
+{
+ // just set the address
+ m_address = data | 0x100;
+
+ // YMF262, in compatibility mode, treats the upper bit as masked
+ // except for register 0x105; assuming YMF278B works the same way?
+ if (m_fm.regs().newflag() == 0 && m_address != 0x105)
+ m_address &= 0xff;
+}
+
+
+//-------------------------------------------------
+// write_address_pcm - handle a write to the upper
+// address register
+//-------------------------------------------------
+
+void ymf278b::write_address_pcm(uint8_t data)
+{
+ // just set the address
+ m_address = data | 0x200;
+}
+
+
+//-------------------------------------------------
+// write_data_pcm - handle a write to the PCM data
+// register
+//-------------------------------------------------
+
+void ymf278b::write_data_pcm(uint8_t data)
+{
+ // ignore data writes if new2 is not yet set
+ if (m_fm.regs().new2flag() == 0)
+ return;
+
+ // write to FM
+ if (bitfield(m_address, 9) != 0)
+ {
+ uint8_t addr = m_address & 0xff;
+ m_pcm.write(addr, data);
+
+ // writes to the waveform number cause loads to happen for "about 300usec"
+ // which is ~13 samples at the nominal output frequency of 44.1kHz
+ if (addr >= 0x08 && addr <= 0x1f)
+ m_load_remaining = 13;
+ }
+
+ // BUSY goes for 88 clocks on PCM writes
+ m_fm.intf().ymfm_set_busy_end(88);
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ymf278b::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 7)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+
+ case 2: // address port
+ write_address_hi(data);
+ break;
+
+ case 3: // data port
+ write_data(data);
+ break;
+
+ case 4: // PCM address port
+ write_address_pcm(data);
+ break;
+
+ case 5: // PCM address port
+ write_data_pcm(data);
+ break;
+
+ default:
+ debug::log_unexpected_read_write("Unexpected write to ymf278b offset %d\n", offset & 7);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ymf278b::generate(output_data *output, uint32_t numsamples)
+{
+ static const int16_t s_mix_scale[8] = { 0x7fa, 0x5a4, 0x3fd, 0x2d2, 0x1fe, 0x169, 0xff, 0 };
+ int32_t const pcm_l = s_mix_scale[m_pcm.regs().mix_pcm_l()];
+ int32_t const pcm_r = s_mix_scale[m_pcm.regs().mix_pcm_r()];
+ int32_t const fm_l = s_mix_scale[m_pcm.regs().mix_fm_l()];
+ int32_t const fm_r = s_mix_scale[m_pcm.regs().mix_fm_r()];
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm_pos += FM_EXTRA_SAMPLE_STEP;
+ if (m_fm_pos >= FM_EXTRA_SAMPLE_THRESH)
+ {
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+ m_fm_pos -= FM_EXTRA_SAMPLE_THRESH;
+ }
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+ m_pcm.clock(pcm_engine::ALL_CHANNELS);
+
+ // update the FM content; mixing details for YMF278B need verification
+ fm_engine::output_data fmout;
+ m_fm.output(fmout.clear(), 0, 32767, fm_engine::ALL_CHANNELS);
+
+ // update the PCM content
+ pcm_engine::output_data pcmout;
+ m_pcm.output(pcmout.clear(), pcm_engine::ALL_CHANNELS);
+
+ // DO0 output: FM channels 2+3 only
+ output->data[0] = fmout.data[2];
+ output->data[1] = fmout.data[3];
+
+ // DO1 output: wavetable channels 2+3 only
+ output->data[2] = pcmout.data[2];
+ output->data[3] = pcmout.data[3];
+
+ // DO2 output: mixed FM channels 0+1 and wavetable channels 0+1
+ output->data[4] = (fmout.data[0] * fm_l + pcmout.data[0] * pcm_l) >> 11;
+ output->data[5] = (fmout.data[1] * fm_r + pcmout.data[1] * pcm_r) >> 11;
+
+ // YMF278B output is 16-bit 2s complement serial
+ output->clamp16();
+ }
+
+ // decrement the load waiting count
+ if (m_load_remaining > 0)
+ m_load_remaining -= std::min(m_load_remaining, numsamples);
+}
+
+
+
+//*********************************************************
+// OPLL BASE
+//*********************************************************
+
+//-------------------------------------------------
+// opll_base - constructor
+//-------------------------------------------------
+
+opll_base::opll_base(ymfm_interface &intf, uint8_t const *instrument_data) :
+ m_address(0),
+ m_fm(intf)
+{
+ m_fm.regs().set_instrument_data(instrument_data);
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void opll_base::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void opll_base::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ m_fm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void opll_base::write_address(uint8_t data)
+{
+ // OPLL doesn't expose a busy signal, but datasheets are pretty consistent
+ // in indicating that address writes should be no faster than every 12 clocks
+ m_fm.intf().ymfm_set_busy_end(12);
+
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void opll_base::write_data(uint8_t data)
+{
+ // OPLL doesn't expose a busy signal, but datasheets are pretty consistent
+ // in indicating that address writes should be no faster than every 84 clocks
+ m_fm.intf().ymfm_set_busy_end(84);
+
+ // write to FM
+ m_fm.write(m_address, data);
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void opll_base::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 1)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void opll_base::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; OPLL has a built-in 9-bit DAC
+ m_fm.output(output->clear(), 5, 256, fm_engine::ALL_CHANNELS);
+
+ // final output is multiplexed; we don't simulate that here except
+ // to average over everything
+ output->data[0] = (output->data[0] * 128) / 9;
+ output->data[1] = (output->data[1] * 128) / 9;
+ }
+}
+
+
+
+//*********************************************************
+// YM2413
+//*********************************************************
+
+//-------------------------------------------------
+// ym2413 - constructor
+//-------------------------------------------------
+
+ym2413::ym2413(ymfm_interface &intf, uint8_t const *instrument_data) :
+ opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments)
+{
+};
+
+// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches
+uint8_t const ym2413::s_default_instruments[] =
+{
+ //April 2015 David Viens, tweaked May 19-21th 2015 Hubert Lamontagne
+ 0x71, 0x61, 0x1E, 0x17, 0xEF, 0x7F, 0x00, 0x17, //Violin
+ 0x13, 0x41, 0x1A, 0x0D, 0xF8, 0xF7, 0x23, 0x13, //Guitar
+ 0x13, 0x01, 0x99, 0x00, 0xF2, 0xC4, 0x11, 0x23, //Piano
+ 0x31, 0x61, 0x0E, 0x07, 0x98, 0x64, 0x70, 0x27, //Flute
+ 0x22, 0x21, 0x1E, 0x06, 0xBF, 0x76, 0x00, 0x28, //Clarinet
+ 0x31, 0x22, 0x16, 0x05, 0xE0, 0x71, 0x0F, 0x18, //Oboe
+ 0x21, 0x61, 0x1D, 0x07, 0x82, 0x8F, 0x10, 0x07, //Trumpet
+ 0x23, 0x21, 0x2D, 0x14, 0xFF, 0x7F, 0x00, 0x07, //Organ
+ 0x41, 0x61, 0x1B, 0x06, 0x64, 0x65, 0x10, 0x17, //Horn
+ 0x61, 0x61, 0x0B, 0x18, 0x85, 0xFF, 0x81, 0x07, //Synthesizer
+ 0x13, 0x01, 0x83, 0x11, 0xFA, 0xE4, 0x10, 0x04, //Harpsichord
+ 0x17, 0x81, 0x23, 0x07, 0xF8, 0xF8, 0x22, 0x12, //Vibraphone
+ 0x61, 0x50, 0x0C, 0x05, 0xF2, 0xF5, 0x29, 0x42, //Synthesizer Bass
+ 0x01, 0x01, 0x54, 0x03, 0xC3, 0x92, 0x03, 0x02, //Acoustic Bass
+ 0x41, 0x41, 0x89, 0x03, 0xF1, 0xE5, 0x11, 0x13, //Electric Guitar
+ 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1
+ 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2
+ 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3
+};
+
+
+
+//*********************************************************
+// YM2423
+//*********************************************************
+
+//-------------------------------------------------
+// ym2423 - constructor
+//-------------------------------------------------
+
+ym2423::ym2423(ymfm_interface &intf, uint8_t const *instrument_data) :
+ opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments)
+{
+};
+
+// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches
+uint8_t const ym2423::s_default_instruments[] =
+{
+ // May 4-6 2016 Hubert Lamontagne
+ // Doesn't seem to have any diff between opllx-x and opllx-y
+ // Drums seem identical to regular opll
+ 0x61, 0x61, 0x1B, 0x07, 0x94, 0x5F, 0x10, 0x06, //1 Strings Saw wave with vibrato Violin
+ 0x93, 0xB1, 0x51, 0x04, 0xF3, 0xF2, 0x70, 0xFB, //2 Guitar Jazz GuitarPiano
+ 0x41, 0x21, 0x11, 0x85, 0xF2, 0xF2, 0x70, 0x75, //3 Electric Guitar Same as OPLL No.15 Synth
+ 0x93, 0xB2, 0x28, 0x07, 0xF3, 0xF2, 0x70, 0xB4, //4 Electric Piano 2 Slow attack, tremoloDing-a-ling
+ 0x72, 0x31, 0x97, 0x05, 0x51, 0x6F, 0x60, 0x09, //5 Flute Same as OPLL No.4Clarinet
+ 0x13, 0x30, 0x18, 0x06, 0xF7, 0xF4, 0x50, 0x85, //6 Marimba Also be used as steel drumXyophone
+ 0x51, 0x31, 0x1C, 0x07, 0x51, 0x71, 0x20, 0x26, //7 Trumpet Same as OPLL No.7Trumpet
+ 0x41, 0xF4, 0x1B, 0x07, 0x74, 0x34, 0x00, 0x06, //8 Harmonica Harmonica synth
+ 0x50, 0x30, 0x4D, 0x03, 0x42, 0x65, 0x20, 0x06, //9 Tuba Tuba
+ 0x40, 0x20, 0x10, 0x85, 0xF3, 0xF5, 0x20, 0x04, //10 Synth Brass 2 Synth sweep
+ 0x61, 0x61, 0x1B, 0x07, 0xC5, 0x96, 0xF3, 0xF6, //11 Short Saw Saw wave with short envelopeSynth hit
+ 0xF9, 0xF1, 0xDC, 0x00, 0xF5, 0xF3, 0x77, 0xF2, //12 Vibraphone Bright vibraphoneVibes
+ 0x60, 0xA2, 0x91, 0x03, 0x94, 0xC1, 0xF7, 0xF7, //13 Electric Guitar 2 Clean guitar with feedbackHarmonic bass
+ 0x30, 0x30, 0x17, 0x06, 0xF3, 0xF1, 0xB7, 0xFC, //14 Synth Bass 2Snappy bass
+ 0x31, 0x36, 0x0D, 0x05, 0xF2, 0xF4, 0x27, 0x9C, //15 Sitar Also be used as ShamisenBanjo
+ 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1
+ 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2
+ 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3
+};
+
+
+
+//*********************************************************
+// YMF281
+//*********************************************************
+
+//-------------------------------------------------
+// ymf281 - constructor
+//-------------------------------------------------
+
+ymf281::ymf281(ymfm_interface &intf, uint8_t const *instrument_data) :
+ opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments)
+{
+};
+
+// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches
+uint8_t const ymf281::s_default_instruments[] =
+{
+ // May 14th 2015 Hubert Lamontagne
+ 0x72, 0x21, 0x1A, 0x07, 0xF6, 0x64, 0x01, 0x16, // Clarinet ~~ Electric String Square wave with vibrato
+ 0x00, 0x10, 0x45, 0x00, 0xF6, 0x83, 0x73, 0x63, // Synth Bass ~~ Bow wow Triangular wave
+ 0x13, 0x01, 0x96, 0x00, 0xF1, 0xF4, 0x31, 0x23, // Piano ~~ Electric Guitar Despite of its name, same as Piano of YM2413.
+ 0x71, 0x21, 0x0B, 0x0F, 0xF9, 0x64, 0x70, 0x17, // Flute ~~ Organ Sine wave
+ 0x02, 0x21, 0x1E, 0x06, 0xF9, 0x76, 0x00, 0x28, // Square Wave ~~ Clarinet Same as ones of YM2413.
+ 0x00, 0x61, 0x82, 0x0E, 0xF9, 0x61, 0x20, 0x27, // Space Oboe ~~ Saxophone Saw wave with vibrato
+ 0x21, 0x61, 0x1B, 0x07, 0x84, 0x8F, 0x10, 0x07, // Trumpet ~~ Trumpet Same as ones of YM2413.
+ 0x37, 0x32, 0xCA, 0x02, 0x66, 0x64, 0x47, 0x29, // Wow Bell ~~ Street Organ Calliope
+ 0x41, 0x41, 0x07, 0x03, 0xF5, 0x70, 0x51, 0xF5, // Electric Guitar ~~ Synth Brass Same as Synthesizer of YM2413.
+ 0x36, 0x01, 0x5E, 0x07, 0xF2, 0xF3, 0xF7, 0xF7, // Vibes ~~ Electric Piano Simulate of Rhodes Piano
+ 0x00, 0x00, 0x18, 0x06, 0xC5, 0xF3, 0x20, 0xF2, // Bass ~~ Bass Electric bass
+ 0x17, 0x81, 0x25, 0x07, 0xF7, 0xF3, 0x21, 0xF7, // Vibraphone ~~ Vibraphone Same as ones of YM2413.
+ 0x35, 0x64, 0x00, 0x00, 0xFF, 0xF3, 0x77, 0xF5, // Vibrato Bell ~~ Chime Bell
+ 0x11, 0x31, 0x00, 0x07, 0xDD, 0xF3, 0xFF, 0xFB, // Click Sine ~~ Tom Tom II Tom
+ 0x3A, 0x21, 0x00, 0x07, 0x95, 0x84, 0x0F, 0xF5, // Noise and Tone ~~ Noise for S.E.
+ 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1
+ 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2
+ 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3
+};
+
+
+
+//*********************************************************
+// DS1001
+//*********************************************************
+
+//-------------------------------------------------
+// ds1001 - constructor
+//-------------------------------------------------
+
+ds1001::ds1001(ymfm_interface &intf, uint8_t const *instrument_data) :
+ opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments)
+{
+};
+
+// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches
+uint8_t const ds1001::s_default_instruments[] =
+{
+ // May 15th 2015 Hubert Lamontagne & David Viens
+ 0x03, 0x21, 0x05, 0x06, 0xC8, 0x81, 0x42, 0x27, // Buzzy Bell
+ 0x13, 0x41, 0x14, 0x0D, 0xF8, 0xF7, 0x23, 0x12, // Guitar
+ 0x31, 0x11, 0x08, 0x08, 0xFA, 0xC2, 0x28, 0x22, // Wurly
+ 0x31, 0x61, 0x0C, 0x07, 0xF8, 0x64, 0x60, 0x27, // Flute
+ 0x22, 0x21, 0x1E, 0x06, 0xFF, 0x76, 0x00, 0x28, // Clarinet
+ 0x02, 0x01, 0x05, 0x00, 0xAC, 0xF2, 0x03, 0x02, // Synth
+ 0x21, 0x61, 0x1D, 0x07, 0x82, 0x8F, 0x10, 0x07, // Trumpet
+ 0x23, 0x21, 0x22, 0x17, 0xFF, 0x73, 0x00, 0x17, // Organ
+ 0x15, 0x11, 0x25, 0x00, 0x41, 0x71, 0x00, 0xF1, // Bells
+ 0x95, 0x01, 0x10, 0x0F, 0xB8, 0xAA, 0x50, 0x02, // Vibes
+ 0x17, 0xC1, 0x5E, 0x07, 0xFA, 0xF8, 0x22, 0x12, // Vibraphone
+ 0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x10, 0x16, // Tutti
+ 0x01, 0x02, 0xD3, 0x05, 0xF3, 0x92, 0x83, 0xF2, // Fretless
+ 0x61, 0x63, 0x0C, 0x00, 0xA4, 0xFF, 0x30, 0x06, // Synth Bass
+ 0x21, 0x62, 0x0D, 0x00, 0xA1, 0xFF, 0x50, 0x08, // Sweep
+ 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1
+ 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2
+ 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3
+};
+
+
+//*********************************************************
+// EXPLICIT INSTANTIATION
+//*********************************************************
+
+template class opl_registers_base<4>;
+template class fm_engine_base<opl_registers_base<4>>;
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_opl.h b/3rdparty/ymfm/src/ymfm_opl.h
new file mode 100644
index 00000000000..71b098e974a
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opl.h
@@ -0,0 +1,902 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_OPL_H
+#define YMFM_OPL_H
+
+#pragma once
+
+#include "ymfm.h"
+#include "ymfm_adpcm.h"
+#include "ymfm_fm.h"
+#include "ymfm_pcm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// REGISTER CLASSES
+//*********************************************************
+
+// ======================> opl_registers_base
+
+//
+// OPL/OPL2/OPL3/OPL4 register map:
+//
+// System-wide registers:
+// 01 xxxxxxxx Test register
+// --x----- Enable OPL compatibility mode [OPL2 only] (0 = enable)
+// 02 xxxxxxxx Timer A value (4 * OPN)
+// 03 xxxxxxxx Timer B value
+// 04 x------- RST
+// -x------ Mask timer A
+// --x----- Mask timer B
+// ------x- Load timer B
+// -------x Load timer A
+// 08 x------- CSM mode [OPL/OPL2 only]
+// -x------ Note select
+// BD x------- AM depth
+// -x------ PM depth
+// --x----- Rhythm enable
+// ---x---- Bass drum key on
+// ----x--- Snare drum key on
+// -----x-- Tom key on
+// ------x- Top cymbal key on
+// -------x High hat key on
+// 101 --xxxxxx Test register 2 [OPL3 only]
+// 104 --x----- Channel 6 4-operator mode [OPL3 only]
+// ---x---- Channel 5 4-operator mode [OPL3 only]
+// ----x--- Channel 4 4-operator mode [OPL3 only]
+// -----x-- Channel 3 4-operator mode [OPL3 only]
+// ------x- Channel 2 4-operator mode [OPL3 only]
+// -------x Channel 1 4-operator mode [OPL3 only]
+// 105 -------x New [OPL3 only]
+// ------x- New2 [OPL4 only]
+//
+// Per-channel registers (channel in address bits 0-3)
+// Note that all these apply to address+100 as well on OPL3+
+// A0-A8 xxxxxxxx F-number (low 8 bits)
+// B0-B8 --x----- Key on
+// ---xxx-- Block (octvate, 0-7)
+// ------xx F-number (high two bits)
+// C0-C8 x------- CHD output (to DO0 pin) [OPL3+ only]
+// -x------ CHC output (to DO0 pin) [OPL3+ only]
+// --x----- CHB output (mixed right, to DO2 pin) [OPL3+ only]
+// ---x---- CHA output (mixed left, to DO2 pin) [OPL3+ only]
+// ----xxx- Feedback level for operator 1 (0-7)
+// -------x Operator connection algorithm
+//
+// Per-operator registers (operator in bits 0-5)
+// Note that all these apply to address+100 as well on OPL3+
+// 20-35 x------- AM enable
+// -x------ PM enable (VIB)
+// --x----- EG type
+// ---x---- Key scale rate
+// ----xxxx Multiple value (0-15)
+// 40-55 xx------ Key scale level (0-3)
+// --xxxxxx Total level (0-63)
+// 60-75 xxxx---- Attack rate (0-15)
+// ----xxxx Decay rate (0-15)
+// 80-95 xxxx---- Sustain level (0-15)
+// ----xxxx Release rate (0-15)
+// E0-F5 ------xx Wave select (0-3) [OPL2 only]
+// -----xxx Wave select (0-7) [OPL3+ only]
+//
+
+template<int Revision>
+class opl_registers_base : public fm_registers_base
+{
+ static constexpr bool IsOpl2 = (Revision == 2);
+ static constexpr bool IsOpl2Plus = (Revision >= 2);
+ static constexpr bool IsOpl3Plus = (Revision >= 3);
+ static constexpr bool IsOpl4Plus = (Revision >= 4);
+
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = IsOpl3Plus ? 4 : 1;
+ static constexpr uint32_t CHANNELS = IsOpl3Plus ? 18 : 9;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+ static constexpr uint32_t OPERATORS = CHANNELS * 2;
+ static constexpr uint32_t WAVEFORMS = IsOpl3Plus ? 8 : (IsOpl2Plus ? 4 : 1);
+ static constexpr uint32_t REGISTERS = IsOpl3Plus ? 0x200 : 0x100;
+ static constexpr uint32_t REG_MODE = 0x04;
+ static constexpr uint32_t DEFAULT_PRESCALE = IsOpl4Plus ? 19 : (IsOpl3Plus ? 8 : 4);
+ static constexpr uint32_t EG_CLOCK_DIVIDER = 1;
+ static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
+ static constexpr bool DYNAMIC_OPS = IsOpl3Plus;
+ static constexpr bool MODULATOR_DELAY = !IsOpl3Plus;
+ static constexpr uint8_t STATUS_TIMERA = 0x40;
+ static constexpr uint8_t STATUS_TIMERB = 0x20;
+ static constexpr uint8_t STATUS_BUSY = 0;
+ static constexpr uint8_t STATUS_IRQ = 0x80;
+
+ // constructor
+ opl_registers_base();
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ if (!IsOpl3Plus)
+ return chnum;
+ else
+ return (chnum % 9) + 0x100 * (chnum / 9);
+ }
+
+ // map operator number to register offset
+ static constexpr uint32_t operator_offset(uint32_t opnum)
+ {
+ assert(opnum < OPERATORS);
+ if (!IsOpl3Plus)
+ return opnum + 2 * (opnum / 6);
+ else
+ return (opnum % 18) + 2 * ((opnum % 18) / 6) + 0x100 * (opnum / 18);
+ }
+
+ // return an array of operator indices for each channel
+ struct operator_mapping { uint32_t chan[CHANNELS]; };
+ void operator_map(operator_mapping &dest) const;
+
+ // OPL4 apparently can read back FM registers?
+ uint8_t read(uint16_t index) const { return m_regdata[index]; }
+
+ // handle writes to the register array
+ bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask);
+
+ // clock the noise and LFO, if present, returning LFO PM value
+ int32_t clock_noise_and_lfo();
+
+ // reset the LFO
+ void reset_lfo() { m_lfo_am_counter = m_lfo_pm_counter = 0; }
+
+ // return the AM offset from LFO for the given channel
+ // on OPL this is just a fixed value
+ uint32_t lfo_am_offset(uint32_t choffs) const { return m_lfo_am; }
+
+ // return LFO/noise states
+ uint32_t noise_state() const { return m_noise_lfsr >> 23; }
+
+ // caching helpers
+ void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache);
+
+ // compute the phase step, given a PM value
+ uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm);
+
+ // log a key-on event
+ std::string log_keyon(uint32_t choffs, uint32_t opoffs);
+
+ // system-wide registers
+ uint32_t test() const { return byte(0x01, 0, 8); }
+ uint32_t waveform_enable() const { return IsOpl2 ? byte(0x01, 5, 1) : (IsOpl3Plus ? 1 : 0); }
+ uint32_t timer_a_value() const { return byte(0x02, 0, 8) * 4; } // 8->10 bits
+ uint32_t timer_b_value() const { return byte(0x03, 0, 8); }
+ uint32_t status_mask() const { return byte(0x04, 0, 8) & 0x78; }
+ uint32_t irq_reset() const { return byte(0x04, 7, 1); }
+ uint32_t reset_timer_b() const { return byte(0x04, 7, 1) | byte(0x04, 5, 1); }
+ uint32_t reset_timer_a() const { return byte(0x04, 7, 1) | byte(0x04, 6, 1); }
+ uint32_t enable_timer_b() const { return 1; }
+ uint32_t enable_timer_a() const { return 1; }
+ uint32_t load_timer_b() const { return byte(0x04, 1, 1); }
+ uint32_t load_timer_a() const { return byte(0x04, 0, 1); }
+ uint32_t csm() const { return IsOpl3Plus ? 0 : byte(0x08, 7, 1); }
+ uint32_t note_select() const { return byte(0x08, 6, 1); }
+ uint32_t lfo_am_depth() const { return byte(0xbd, 7, 1); }
+ uint32_t lfo_pm_depth() const { return byte(0xbd, 6, 1); }
+ uint32_t rhythm_enable() const { return byte(0xbd, 5, 1); }
+ uint32_t rhythm_keyon() const { return byte(0xbd, 4, 0); }
+ uint32_t newflag() const { return IsOpl3Plus ? byte(0x105, 0, 1) : 0; }
+ uint32_t new2flag() const { return IsOpl4Plus ? byte(0x105, 1, 1) : 0; }
+ uint32_t fourop_enable() const { return IsOpl3Plus ? byte(0x104, 0, 6) : 0; }
+
+ // per-channel registers
+ uint32_t ch_block_freq(uint32_t choffs) const { return word(0xb0, 0, 5, 0xa0, 0, 8, choffs); }
+ uint32_t ch_feedback(uint32_t choffs) const { return byte(0xc0, 1, 3, choffs); }
+ uint32_t ch_algorithm(uint32_t choffs) const { return byte(0xc0, 0, 1, choffs) | (IsOpl3Plus ? (8 | (byte(0xc3, 0, 1, choffs) << 1)) : 0); }
+ uint32_t ch_output_any(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 4, 4) : 1; }
+ uint32_t ch_output_0(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 4, 1) : 1; }
+ uint32_t ch_output_1(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 5, 1) : (IsOpl3Plus ? 1 : 0); }
+ uint32_t ch_output_2(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 6, 1) : 0; }
+ uint32_t ch_output_3(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 7, 1) : 0; }
+
+ // per-operator registers
+ uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0x20, 7, 1, opoffs); }
+ uint32_t op_lfo_pm_enable(uint32_t opoffs) const { return byte(0x20, 6, 1, opoffs); }
+ uint32_t op_eg_sustain(uint32_t opoffs) const { return byte(0x20, 5, 1, opoffs); }
+ uint32_t op_ksr(uint32_t opoffs) const { return byte(0x20, 4, 1, opoffs); }
+ uint32_t op_multiple(uint32_t opoffs) const { return byte(0x20, 0, 4, opoffs); }
+ uint32_t op_ksl(uint32_t opoffs) const { uint32_t temp = byte(0x40, 6, 2, opoffs); return bitfield(temp, 1) | (bitfield(temp, 0) << 1); }
+ uint32_t op_total_level(uint32_t opoffs) const { return byte(0x40, 0, 6, opoffs); }
+ uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x60, 4, 4, opoffs); }
+ uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0x60, 0, 4, opoffs); }
+ uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0x80, 4, 4, opoffs); }
+ uint32_t op_release_rate(uint32_t opoffs) const { return byte(0x80, 0, 4, opoffs); }
+ uint32_t op_waveform(uint32_t opoffs) const { return waveform_enable() ? byte(0xe0, 0, newflag() ? 3 : 2, opoffs) : 0; }
+
+protected:
+ // return a bitfield extracted from a byte
+ uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const
+ {
+ return bitfield(m_regdata[offset + extra_offset], start, count);
+ }
+
+ // return a bitfield extracted from a pair of bytes, MSBs listed first
+ uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const
+ {
+ return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset);
+ }
+
+ // helper to determine if the this channel is an active rhythm channel
+ bool is_rhythm(uint32_t choffs) const
+ {
+ return rhythm_enable() && (choffs >= 6 && choffs <= 8);
+ }
+
+ // internal state
+ uint16_t m_lfo_am_counter; // LFO AM counter
+ uint16_t m_lfo_pm_counter; // LFO PM counter
+ uint32_t m_noise_lfsr; // noise LFSR state
+ uint8_t m_lfo_am; // current LFO AM value
+ uint8_t m_regdata[REGISTERS]; // register data
+ uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms
+};
+
+using opl_registers = opl_registers_base<1>;
+using opl2_registers = opl_registers_base<2>;
+using opl3_registers = opl_registers_base<3>;
+using opl4_registers = opl_registers_base<4>;
+
+
+
+// ======================> opll_registers
+
+//
+// OPLL register map:
+//
+// System-wide registers:
+// 0E --x----- Rhythm enable
+// ---x---- Bass drum key on
+// ----x--- Snare drum key on
+// -----x-- Tom key on
+// ------x- Top cymbal key on
+// -------x High hat key on
+// 0F xxxxxxxx Test register
+//
+// Per-channel registers (channel in address bits 0-3)
+// 10-18 xxxxxxxx F-number (low 8 bits)
+// 20-28 --x----- Sustain on
+// ---x---- Key on
+// --- xxx- Block (octvate, 0-7)
+// -------x F-number (high bit)
+// 30-38 xxxx---- Instrument selection
+// ----xxxx Volume
+//
+// User instrument registers (for carrier, modulator operators)
+// 00-01 x------- AM enable
+// -x------ PM enable (VIB)
+// --x----- EG type
+// ---x---- Key scale rate
+// ----xxxx Multiple value (0-15)
+// 02 xx------ Key scale level (carrier, 0-3)
+// --xxxxxx Total level (modulator, 0-63)
+// 03 xx------ Key scale level (modulator, 0-3)
+// ---x---- Rectified wave (carrier)
+// ----x--- Rectified wave (modulator)
+// -----xxx Feedback level for operator 1 (0-7)
+// 04-05 xxxx---- Attack rate (0-15)
+// ----xxxx Decay rate (0-15)
+// 06-07 xxxx---- Sustain level (0-15)
+// ----xxxx Release rate (0-15)
+//
+// Internal (fake) registers:
+// 40-48 xxxxxxxx Current instrument base address
+// 4E-5F xxxxxxxx Current instrument base address + operator slot (0/1)
+// 70-FF xxxxxxxx Data for instruments (1-16 plus 3 drums)
+//
+
+class opll_registers : public fm_registers_base
+{
+public:
+ static constexpr uint32_t OUTPUTS = 2;
+ static constexpr uint32_t CHANNELS = 9;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+ static constexpr uint32_t OPERATORS = CHANNELS * 2;
+ static constexpr uint32_t WAVEFORMS = 2;
+ static constexpr uint32_t REGISTERS = 0x40;
+ static constexpr uint32_t REG_MODE = 0x3f;
+ static constexpr uint32_t DEFAULT_PRESCALE = 4;
+ static constexpr uint32_t EG_CLOCK_DIVIDER = 1;
+ static constexpr uint32_t CSM_TRIGGER_MASK = 0;
+ static constexpr bool EG_HAS_DEPRESS = true;
+ static constexpr bool MODULATOR_DELAY = true;
+ static constexpr uint8_t STATUS_TIMERA = 0;
+ static constexpr uint8_t STATUS_TIMERB = 0;
+ static constexpr uint8_t STATUS_BUSY = 0;
+ static constexpr uint8_t STATUS_IRQ = 0;
+
+ // OPLL-specific constants
+ static constexpr uint32_t INSTDATA_SIZE = 0x90;
+
+ // constructor
+ opll_registers();
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ return chnum;
+ }
+
+ // map operator number to register offset
+ static constexpr uint32_t operator_offset(uint32_t opnum)
+ {
+ assert(opnum < OPERATORS);
+ return opnum;
+ }
+
+ // return an array of operator indices for each channel
+ struct operator_mapping { uint32_t chan[CHANNELS]; };
+ void operator_map(operator_mapping &dest) const;
+
+ // read a register value
+ uint8_t read(uint16_t index) const { return m_regdata[index]; }
+
+ // handle writes to the register array
+ bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask);
+
+ // clock the noise and LFO, if present, returning LFO PM value
+ int32_t clock_noise_and_lfo();
+
+ // reset the LFO
+ void reset_lfo() { m_lfo_am_counter = m_lfo_pm_counter = 0; }
+
+ // return the AM offset from LFO for the given channel
+ // on OPL this is just a fixed value
+ uint32_t lfo_am_offset(uint32_t choffs) const { return m_lfo_am; }
+
+ // return LFO/noise states
+ uint32_t noise_state() const { return m_noise_lfsr >> 23; }
+
+ // caching helpers
+ void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache);
+
+ // compute the phase step, given a PM value
+ uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm);
+
+ // log a key-on event
+ std::string log_keyon(uint32_t choffs, uint32_t opoffs);
+
+ // set the instrument data
+ void set_instrument_data(uint8_t const *data)
+ {
+ std::copy_n(data, INSTDATA_SIZE, &m_instdata[0]);
+ }
+
+ // system-wide registers
+ uint32_t rhythm_enable() const { return byte(0x0e, 5, 1); }
+ uint32_t rhythm_keyon() const { return byte(0x0e, 4, 0); }
+ uint32_t test() const { return byte(0x0f, 0, 8); }
+ uint32_t waveform_enable() const { return 1; }
+ uint32_t timer_a_value() const { return 0; }
+ uint32_t timer_b_value() const { return 0; }
+ uint32_t status_mask() const { return 0; }
+ uint32_t irq_reset() const { return 0; }
+ uint32_t reset_timer_b() const { return 0; }
+ uint32_t reset_timer_a() const { return 0; }
+ uint32_t enable_timer_b() const { return 0; }
+ uint32_t enable_timer_a() const { return 0; }
+ uint32_t load_timer_b() const { return 0; }
+ uint32_t load_timer_a() const { return 0; }
+ uint32_t csm() const { return 0; }
+
+ // per-channel registers
+ uint32_t ch_block_freq(uint32_t choffs) const { return word(0x20, 0, 4, 0x10, 0, 8, choffs); }
+ uint32_t ch_sustain(uint32_t choffs) const { return byte(0x20, 5, 1, choffs); }
+ uint32_t ch_total_level(uint32_t choffs) const { return instchbyte(0x02, 0, 6, choffs); }
+ uint32_t ch_feedback(uint32_t choffs) const { return instchbyte(0x03, 0, 3, choffs); }
+ uint32_t ch_algorithm(uint32_t choffs) const { return 0; }
+ uint32_t ch_instrument(uint32_t choffs) const { return byte(0x30, 4, 4, choffs); }
+ uint32_t ch_output_any(uint32_t choffs) const { return 1; }
+ uint32_t ch_output_0(uint32_t choffs) const { return !is_rhythm(choffs); }
+ uint32_t ch_output_1(uint32_t choffs) const { return is_rhythm(choffs); }
+ uint32_t ch_output_2(uint32_t choffs) const { return 0; }
+ uint32_t ch_output_3(uint32_t choffs) const { return 0; }
+
+ // per-operator registers
+ uint32_t op_lfo_am_enable(uint32_t opoffs) const { return instopbyte(0x00, 7, 1, opoffs); }
+ uint32_t op_lfo_pm_enable(uint32_t opoffs) const { return instopbyte(0x00, 6, 1, opoffs); }
+ uint32_t op_eg_sustain(uint32_t opoffs) const { return instopbyte(0x00, 5, 1, opoffs); }
+ uint32_t op_ksr(uint32_t opoffs) const { return instopbyte(0x00, 4, 1, opoffs); }
+ uint32_t op_multiple(uint32_t opoffs) const { return instopbyte(0x00, 0, 4, opoffs); }
+ uint32_t op_ksl(uint32_t opoffs) const { return instopbyte(0x02, 6, 2, opoffs); }
+ uint32_t op_waveform(uint32_t opoffs) const { return instchbyte(0x03, 3 + bitfield(opoffs, 0), 1, opoffs >> 1); }
+ uint32_t op_attack_rate(uint32_t opoffs) const { return instopbyte(0x04, 4, 4, opoffs); }
+ uint32_t op_decay_rate(uint32_t opoffs) const { return instopbyte(0x04, 0, 4, opoffs); }
+ uint32_t op_sustain_level(uint32_t opoffs) const { return instopbyte(0x06, 4, 4, opoffs); }
+ uint32_t op_release_rate(uint32_t opoffs) const { return instopbyte(0x06, 0, 4, opoffs); }
+ uint32_t op_volume(uint32_t opoffs) const { return byte(0x30, 4 * bitfield(~opoffs, 0), 4, opoffs >> 1); }
+
+private:
+ // return a bitfield extracted from a byte
+ uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const
+ {
+ return bitfield(m_regdata[offset + extra_offset], start, count);
+ }
+
+ // return a bitfield extracted from a pair of bytes, MSBs listed first
+ uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const
+ {
+ return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset);
+ }
+
+ // helpers to read from instrument channel/operator data
+ uint32_t instchbyte(uint32_t offset, uint32_t start, uint32_t count, uint32_t choffs) const { return bitfield(m_chinst[choffs][offset], start, count); }
+ uint32_t instopbyte(uint32_t offset, uint32_t start, uint32_t count, uint32_t opoffs) const { return bitfield(m_opinst[opoffs][offset], start, count); }
+
+ // helper to determine if the this channel is an active rhythm channel
+ bool is_rhythm(uint32_t choffs) const
+ {
+ return rhythm_enable() && choffs >= 6;
+ }
+
+ // internal state
+ uint16_t m_lfo_am_counter; // LFO AM counter
+ uint16_t m_lfo_pm_counter; // LFO PM counter
+ uint32_t m_noise_lfsr; // noise LFSR state
+ uint8_t m_lfo_am; // current LFO AM value
+ uint8_t const *m_chinst[CHANNELS]; // pointer to instrument data for each channel
+ uint8_t const *m_opinst[OPERATORS]; // pointer to instrument data for each operator
+ uint8_t m_regdata[REGISTERS]; // register data
+ uint8_t m_instdata[INSTDATA_SIZE]; // instrument data
+ uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms
+};
+
+
+
+//*********************************************************
+// OPL IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ym3526
+
+class ym3526
+{
+public:
+ using fm_engine = fm_engine_base<opl_registers>;
+ using output_data = fm_engine::output_data;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+
+ // constructor
+ ym3526(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate samples of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+protected:
+ // internal state
+ uint8_t m_address; // address register
+ fm_engine m_fm; // core FM engine
+};
+
+
+// ======================> y8950
+
+class y8950
+{
+public:
+ using fm_engine = fm_engine_base<opl_registers>;
+ using output_data = fm_engine::output_data;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+
+ static constexpr uint8_t STATUS_ADPCM_B_PLAYING = 0x01;
+ static constexpr uint8_t STATUS_ADPCM_B_BRDY = 0x08;
+ static constexpr uint8_t STATUS_ADPCM_B_EOS = 0x10;
+ static constexpr uint8_t ALL_IRQS = STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_EOS | fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB;
+
+ // constructor
+ y8950(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read_data();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate samples of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ uint8_t m_address; // address register
+ uint8_t m_io_ddr; // data direction register for I/O
+ fm_engine m_fm; // core FM engine
+ adpcm_b_engine m_adpcm_b; // ADPCM-B engine
+};
+
+
+
+//*********************************************************
+// OPL2 IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ym3812
+
+class ym3812
+{
+public:
+ using fm_engine = fm_engine_base<opl2_registers>;
+ using output_data = fm_engine::output_data;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+
+ // constructor
+ ym3812(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate samples of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ uint8_t m_address; // address register
+ fm_engine m_fm; // core FM engine
+};
+
+
+
+//*********************************************************
+// OPL3 IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ymf262
+
+class ymf262
+{
+public:
+ using fm_engine = fm_engine_base<opl3_registers>;
+ using output_data = fm_engine::output_data;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+
+ // constructor
+ ymf262(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write_address_hi(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate samples of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ uint16_t m_address; // address register
+ fm_engine m_fm; // core FM engine
+};
+
+
+// ======================> ymf289b
+
+class ymf289b
+{
+ static constexpr uint8_t STATUS_BUSY_FLAGS = 0x05;
+
+public:
+ using fm_engine = fm_engine_base<opl3_registers>;
+ using output_data = fm_engine::output_data;
+ static constexpr uint32_t OUTPUTS = 2;
+
+ // constructor
+ ymf289b(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read_data();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write_address_hi(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate samples of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal helpers
+ bool ymf289b_mode() { return ((m_fm.regs().read(0x105) & 0x04) != 0); }
+
+ // internal state
+ uint16_t m_address; // address register
+ fm_engine m_fm; // core FM engine
+};
+
+
+
+//*********************************************************
+// OPL4 IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ymf278b
+
+class ymf278b
+{
+ // Using the nominal datasheet frequency of 33.868MHz, the output of the
+ // chip will be clock/768 = 44.1kHz. However, the FM engine is clocked
+ // internally at clock/(19*36), or 49.515kHz, so the FM output needs to
+ // be downsampled. We treat this as needing to clock the FM engine an
+ // extra tick every few samples. The exact ratio is 768/(19*36) or
+ // 768/684 = 192/171. So if we always clock the FM once, we'll have
+ // 192/171 - 1 = 21/171 left. Thus we count 21 for each sample and when
+ // it gets above 171, we tick an extra time.
+ static constexpr uint32_t FM_EXTRA_SAMPLE_THRESH = 171;
+ static constexpr uint32_t FM_EXTRA_SAMPLE_STEP = 192 - FM_EXTRA_SAMPLE_THRESH;
+
+public:
+ using fm_engine = fm_engine_base<opl4_registers>;
+ static constexpr uint32_t OUTPUTS = 6;
+ using output_data = ymfm_output<OUTPUTS>;
+
+ static constexpr uint8_t STATUS_BUSY = 0x01;
+ static constexpr uint8_t STATUS_LD = 0x02;
+
+ // constructor
+ ymf278b(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return input_clock / 768; }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read_data_pcm();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write_address_hi(uint8_t data);
+ void write_address_pcm(uint8_t data);
+ void write_data_pcm(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate samples of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ uint16_t m_address; // address register
+ uint32_t m_fm_pos; // FM resampling position
+ uint32_t m_load_remaining; // how many more samples until LD flag clears
+ bool m_next_status_id; // flag to track which status ID to return
+ fm_engine m_fm; // core FM engine
+ pcm_engine m_pcm; // core PCM engine
+};
+
+
+
+//*********************************************************
+// OPLL IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> opll_base
+
+class opll_base
+{
+public:
+ using fm_engine = fm_engine_base<opll_registers>;
+ using output_data = fm_engine::output_data;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+
+ // constructor
+ opll_base(ymfm_interface &intf, uint8_t const *data);
+
+ // configuration
+ void set_instrument_data(uint8_t const *data) { m_fm.regs().set_instrument_data(data); }
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access -- doesn't really have any, but provide these for consistency
+ uint8_t read_status() { return 0x00; }
+ uint8_t read(uint32_t offset) { return 0x00; }
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate samples of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ uint8_t m_address; // address register
+ fm_engine m_fm; // core FM engine
+};
+
+
+// ======================> ym2413
+
+class ym2413 : public opll_base
+{
+public:
+ // constructor
+ ym2413(ymfm_interface &intf, uint8_t const *instrument_data = nullptr);
+
+private:
+ // internal state
+ static uint8_t const s_default_instruments[];
+};
+
+
+// ======================> ym2413
+
+class ym2423 : public opll_base
+{
+public:
+ // constructor
+ ym2423(ymfm_interface &intf, uint8_t const *instrument_data = nullptr);
+
+private:
+ // internal state
+ static uint8_t const s_default_instruments[];
+};
+
+
+// ======================> ymf281
+
+class ymf281 : public opll_base
+{
+public:
+ // constructor
+ ymf281(ymfm_interface &intf, uint8_t const *instrument_data = nullptr);
+
+private:
+ // internal state
+ static uint8_t const s_default_instruments[];
+};
+
+
+// ======================> ds1001
+
+class ds1001 : public opll_base
+{
+public:
+ // constructor
+ ds1001(ymfm_interface &intf, uint8_t const *instrument_data = nullptr);
+
+private:
+ // internal state
+ static uint8_t const s_default_instruments[];
+};
+
+}
+
+#endif // YMFM_OPL_H
diff --git a/3rdparty/ymfm/src/ymfm_opm.cpp b/3rdparty/ymfm/src/ymfm_opm.cpp
new file mode 100644
index 00000000000..03f54fb9039
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opm.cpp
@@ -0,0 +1,539 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_opm.h"
+#include "ymfm_fm.ipp"
+
+namespace ymfm
+{
+
+//*********************************************************
+// OPM REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// opm_registers - constructor
+//-------------------------------------------------
+
+opm_registers::opm_registers() :
+ m_lfo_counter(0),
+ m_noise_lfsr(1),
+ m_noise_counter(0),
+ m_noise_state(0),
+ m_noise_lfo(0),
+ m_lfo_am(0)
+{
+ // create the waveforms
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15);
+
+ // create the LFO waveforms; AM in the low 8 bits, PM in the upper 8
+ // waveforms are adjusted to match the pictures in the application manual
+ for (uint32_t index = 0; index < LFO_WAVEFORM_LENGTH; index++)
+ {
+ // waveform 0 is a sawtooth
+ uint8_t am = index ^ 0xff;
+ 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 = 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 = bitfield(index, 6) ? am : ~am;
+ m_lfo_waveform[2][index] = am | (pm << 8);
+
+ // waveform 3 is noise; it is filled in dynamically
+ m_lfo_waveform[3][index] = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+void opm_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+
+ // enable output on both channels by default
+ m_regdata[0x20] = m_regdata[0x21] = m_regdata[0x22] = m_regdata[0x23] = 0xc0;
+ m_regdata[0x24] = m_regdata[0x25] = m_regdata[0x26] = m_regdata[0x27] = 0xc0;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void opm_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_lfo_counter);
+ state.save_restore(m_lfo_am);
+ state.save_restore(m_noise_lfsr);
+ state.save_restore(m_noise_counter);
+ state.save_restore(m_noise_state);
+ state.save_restore(m_noise_lfo);
+ state.save_restore(m_regdata);
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPM this is fixed
+//-------------------------------------------------
+
+void opm_registers::operator_map(operator_mapping &dest) const
+{
+ // Note that the channel index order is 0,2,1,3, so we bitswap the index.
+ //
+ // This is because the order in the map is:
+ // carrier 1, carrier 2, modulator 1, modulator 2
+ //
+ // But when wiring up the connections, the more natural order is:
+ // carrier 1, modulator 1, carrier 2, modulator 2
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 16, 8, 24 ), // Channel 0 operators
+ operator_list( 1, 17, 9, 25 ), // Channel 1 operators
+ operator_list( 2, 18, 10, 26 ), // Channel 2 operators
+ operator_list( 3, 19, 11, 27 ), // Channel 3 operators
+ operator_list( 4, 20, 12, 28 ), // Channel 4 operators
+ operator_list( 5, 21, 13, 29 ), // Channel 5 operators
+ operator_list( 6, 22, 14, 30 ), // Channel 6 operators
+ operator_list( 7, 23, 15, 31 ), // Channel 7 operators
+ } };
+ dest = s_fixed_map;
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array
+//-------------------------------------------------
+
+bool opm_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
+{
+ assert(index < REGISTERS);
+
+ // LFO AM/PM depth are written to the same register (0x19);
+ // redirect the PM depth to an unused neighbor (0x1a)
+ if (index == 0x19)
+ m_regdata[index + bitfield(data, 7)] = data;
+ else if (index != 0x1a)
+ m_regdata[index] = data;
+
+ // handle writes to the key on index
+ if (index == 0x08)
+ {
+ channel = bitfield(data, 0, 3);
+ opmask = bitfield(data, 3, 4);
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+int32_t opm_registers::clock_noise_and_lfo()
+{
+ // base noise frequency is measured at 2x 1/2 FM frequency; this
+ // means each tick counts as two steps against the noise counter
+ uint32_t freq = noise_frequency();
+ for (int rep = 0; rep < 2; rep++)
+ {
+ // evidence seems to suggest the LFSR is clocked continually and just
+ // sampled at the noise frequency for output purposes; note that the
+ // low 8 bits are the most recent 8 bits of history while bits 8-24
+ // contain the 17 bit LFSR state
+ m_noise_lfsr <<= 1;
+ m_noise_lfsr |= bitfield(m_noise_lfsr, 17) ^ bitfield(m_noise_lfsr, 14) ^ 1;
+
+ // compare against the frequency and latch when we exceed it
+ if (m_noise_counter++ >= freq)
+ {
+ m_noise_counter = 0;
+ m_noise_state = bitfield(m_noise_lfsr, 17);
+ }
+ }
+
+ // treat the rate as a 4.4 floating-point step value with implied
+ // leading 1; this matches exactly the frequencies in the application
+ // manual, though it might not be implemented exactly this way on chip
+ uint32_t rate = lfo_rate();
+ m_lfo_counter += (0x10 | bitfield(rate, 0, 4)) << bitfield(rate, 4, 4);
+
+ // bit 1 of the test register is officially undocumented but has been
+ // discovered to hold the LFO in reset while active
+ if (lfo_reset())
+ m_lfo_counter = 0;
+
+ // now pull out the non-fractional LFO value
+ uint32_t lfo = bitfield(m_lfo_counter, 22, 8);
+
+ // fill in the noise entry 1 ahead of our current position; this
+ // ensures the current value remains stable for a full LFO clock
+ // and effectively latches the running value when the LFO advances
+ uint32_t lfo_noise = bitfield(m_noise_lfsr, 17, 8);
+ m_lfo_waveform[3][(lfo + 1) & 0xff] = lfo_noise | (lfo_noise << 8);
+
+ // fetch the AM/PM values based on the waveform; AM is unsigned and
+ // encoded in the low 8 bits, while PM signed and encoded in the upper
+ // 8 bits
+ int32_t ampm = m_lfo_waveform[lfo_waveform()][lfo];
+
+ // apply depth to the AM value and store for later
+ m_lfo_am = ((ampm & 0xff) * lfo_am_depth()) >> 7;
+
+ // apply depth to the PM value and return it
+ return ((ampm >> 8) * int32_t(lfo_pm_depth())) >> 7;
+}
+
+
+//-------------------------------------------------
+// lfo_am_offset - return the AM offset from LFO
+// for the given channel
+//-------------------------------------------------
+
+uint32_t opm_registers::lfo_am_offset(uint32_t choffs) const
+{
+ // OPM maps AM quite differently from OPN
+
+ // shift value for AM sensitivity is [*, 0, 1, 2],
+ // mapping to values of [0, 23.9, 47.8, and 95.6dB]
+ uint32_t am_sensitivity = ch_lfo_am_sens(choffs);
+ if (am_sensitivity == 0)
+ return 0;
+
+ // QUESTION: see OPN note below for the dB range mapping; it applies
+ // here as well
+
+ // raw LFO AM value on OPM is 0-FF, which is already a factor of 2
+ // larger than the OPN below, putting our staring point at 2x theirs;
+ // this works out since our minimum is 2x their maximum
+ return m_lfo_am << (am_sensitivity - 1);
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data
+//-------------------------------------------------
+
+void opm_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
+{
+ // set up the easy stuff
+ cache.waveform = &m_waveform[0][0];
+
+ // get frequency from the channel
+ uint32_t block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // compute the keycode: block_freq is:
+ //
+ // BBBCCCCFFFFFF
+ // ^^^^^
+ //
+ // the 5-bit keycode is just the top 5 bits (block + top 2 bits
+ // of the key code)
+ uint32_t keycode = bitfield(block_freq, 8, 5);
+
+ // detune adjustment
+ cache.detune = detune_adjustment(op_detune(opoffs), keycode);
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ cache.multiple = op_multiple(opoffs) * 2;
+ if (cache.multiple == 0)
+ cache.multiple = 1;
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on
+ // block_freq, detune, and multiple, so compute it after we've done those
+ if (lfo_pm_depth() == 0 || ch_lfo_pm_sens(choffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8
+ cache.total_level = op_total_level(opoffs) << 3;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // determine KSR adjustment for enevlope rates
+ uint32_t ksrval = keycode >> (op_ksr(opoffs) ^ 3);
+ cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval);
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+uint32_t opm_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm)
+{
+ // OPM logic is rather unique here, due to extra detune
+ // and the use of key codes (not to be confused with keycode)
+
+ // start with coarse detune delta; table uses cents value from
+ // manual, converted into 1/64ths
+ static const int16_t s_detune2_delta[4] = { 0, (600*64+50)/100, (781*64+50)/100, (950*64+50)/100 };
+ int32_t delta = s_detune2_delta[op_detune2(opoffs)];
+
+ // add in the PM delta
+ uint32_t pm_sensitivity = ch_lfo_pm_sens(choffs);
+ if (pm_sensitivity != 0)
+ {
+ // raw PM value is -127..128 which is +/- 200 cents
+ // manual gives these magnitudes in cents:
+ // 0, +/-5, +/-10, +/-20, +/-50, +/-100, +/-400, +/-700
+ // this roughly corresponds to shifting the 200-cent value:
+ // 0 >> 5, >> 4, >> 3, >> 2, >> 1, << 1, << 2
+ if (pm_sensitivity < 6)
+ delta += lfo_raw_pm >> (6 - pm_sensitivity);
+ else
+ delta += uint32_t(lfo_raw_pm) << (pm_sensitivity - 5);
+ }
+
+ // apply delta and convert to a frequency number
+ uint32_t phase_step = opm_key_code_to_phase_step(cache.block_freq, delta);
+
+ // apply detune based on the keycode
+ phase_step += cache.detune;
+
+ // apply frequency multiplier (which is cached as an x.1 value)
+ return (phase_step * cache.multiple) >> 1;
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
+{
+ uint32_t chnum = choffs;
+ uint32_t opnum = opoffs;
+
+ char buffer[256];
+ int end = 0;
+
+ 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),
+ op_detune(opoffs),
+ ch_feedback(choffs),
+ ch_algorithm(choffs),
+ op_multiple(opoffs),
+ op_total_level(opoffs),
+ op_ksr(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_sustain_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs),
+ ch_output_0(choffs) ? 'L' : '-',
+ ch_output_1(choffs) ? 'R' : '-');
+
+ bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0);
+ if (am)
+ 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(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth());
+ if (am || pm)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]);
+ if (noise_enable() && opoffs == 31)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1");
+
+ return buffer;
+}
+
+
+
+//*********************************************************
+// YM2151
+//*********************************************************
+
+//-------------------------------------------------
+// ym2151 - constructor
+//-------------------------------------------------
+
+ym2151::ym2151(ymfm_interface &intf, opm_variant variant) :
+ m_variant(variant),
+ m_address(0),
+ m_fm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym2151::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym2151::save_restore(ymfm_saved_state &state)
+{
+ m_fm.save_restore(state);
+ state.save_restore(m_address);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym2151::read_status()
+{
+ uint8_t result = m_fm.status();
+ if (m_fm.intf().ymfm_is_busy())
+ result |= fm_engine::STATUS_BUSY;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym2151::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 1)
+ {
+ case 0: // data port (unused)
+ debug::log_unexpected_read_write("Unexpected read from YM2151 offset %d\n", offset & 3);
+ break;
+
+ case 1: // status port, YM2203 compatible
+ result = read_status();
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym2151::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2151::write_data(uint8_t data)
+{
+ // write the FM register
+ m_fm.write(m_address, data);
+
+ // special cases
+ if (m_address == 0x1b)
+ {
+ // writes to register 0x1B send the upper 2 bits to the output lines
+ m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data >> 6);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2151::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 1)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ym2151::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; OPM is full 14-bit with no intermediate clipping
+ m_fm.output(output->clear(), 0, 32767, fm_engine::ALL_CHANNELS);
+
+ // YM2151 uses an external DAC (YM3012) with mantissa/exponent format
+ // convert to 10.3 floating point value and back to simulate truncation
+ output->roundtrip_fp();
+ }
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_opm.h b/3rdparty/ymfm/src/ymfm_opm.h
new file mode 100644
index 00000000000..b126135d4ff
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opm.h
@@ -0,0 +1,322 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_OPM_H
+#define YMFM_OPM_H
+
+#pragma once
+
+#include "ymfm.h"
+#include "ymfm_fm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// REGISTER CLASSES
+//*********************************************************
+
+// ======================> opm_registers
+
+//
+// OPM register map:
+//
+// System-wide registers:
+// 01 xxxxxx-x Test register
+// ------x- LFO reset
+// 08 -x------ Key on/off operator 4
+// --x----- Key on/off operator 3
+// ---x---- Key on/off operator 2
+// ----x--- Key on/off operator 1
+// -----xxx Channel select
+// 0F x------- Noise enable
+// ---xxxxx Noise frequency
+// 10 xxxxxxxx Timer A value (upper 8 bits)
+// 11 ------xx Timer A value (lower 2 bits)
+// 12 xxxxxxxx Timer B value
+// 14 x------- CSM mode
+// --x----- Reset timer B
+// ---x---- Reset timer A
+// ----x--- Enable timer B
+// -----x-- Enable timer A
+// ------x- Load timer B
+// -------x Load timer A
+// 18 xxxxxxxx LFO frequency
+// 19 0xxxxxxx AM LFO depth
+// 1xxxxxxx PM LFO depth
+// 1B xx------ CT (2 output data lines)
+// ------xx LFO waveform
+//
+// Per-channel registers (channel in address bits 0-2)
+// 20-27 x------- Pan right
+// -x------ Pan left
+// --xxx--- Feedback level for operator 1 (0-7)
+// -----xxx Operator connection algorithm (0-7)
+// 28-2F -xxxxxxx Key code
+// 30-37 xxxxxx-- Key fraction
+// 38-3F -xxx---- LFO PM sensitivity
+// ------xx LFO AM shift
+//
+// Per-operator registers (channel in address bits 0-2, operator in bits 3-4)
+// 40-5F -xxx---- Detune value (0-7)
+// ----xxxx Multiple value (0-15)
+// 60-7F -xxxxxxx Total level (0-127)
+// 80-9F xx------ Key scale rate (0-3)
+// ---xxxxx Attack rate (0-31)
+// A0-BF x------- LFO AM enable
+// ---xxxxx Decay rate (0-31)
+// C0-DF xx------ Detune 2 value (0-3)
+// ---xxxxx Sustain rate (0-31)
+// E0-FF xxxx---- Sustain level (0-15)
+// ----xxxx Release rate (0-15)
+//
+// Internal (fake) registers:
+// 1A -xxxxxxx PM depth
+//
+
+class opm_registers : public fm_registers_base
+{
+ // LFO waveforms are 256 entries long
+ static constexpr uint32_t LFO_WAVEFORM_LENGTH = 256;
+
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = 2;
+ static constexpr uint32_t CHANNELS = 8;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+ static constexpr uint32_t OPERATORS = CHANNELS * 4;
+ static constexpr uint32_t WAVEFORMS = 1;
+ static constexpr uint32_t REGISTERS = 0x100;
+ static constexpr uint32_t DEFAULT_PRESCALE = 2;
+ static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
+ static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
+ static constexpr uint32_t REG_MODE = 0x14;
+ static constexpr uint8_t STATUS_TIMERA = 0x01;
+ static constexpr uint8_t STATUS_TIMERB = 0x02;
+ static constexpr uint8_t STATUS_BUSY = 0x80;
+ static constexpr uint8_t STATUS_IRQ = 0;
+
+ // constructor
+ opm_registers();
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ return chnum;
+ }
+
+ // map operator number to register offset
+ static constexpr uint32_t operator_offset(uint32_t opnum)
+ {
+ assert(opnum < OPERATORS);
+ return opnum;
+ }
+
+ // return an array of operator indices for each channel
+ struct operator_mapping { uint32_t chan[CHANNELS]; };
+ void operator_map(operator_mapping &dest) const;
+
+ // handle writes to the register array
+ bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask);
+
+ // clock the noise and LFO, if present, returning LFO PM value
+ int32_t clock_noise_and_lfo();
+
+ // return the AM offset from LFO for the given channel
+ uint32_t lfo_am_offset(uint32_t choffs) const;
+
+ // return the current noise state, gated by the noise clock
+ uint32_t noise_state() const { return m_noise_state; }
+
+ // caching helpers
+ void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache);
+
+ // compute the phase step, given a PM value
+ uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm);
+
+ // log a key-on event
+ std::string log_keyon(uint32_t choffs, uint32_t opoffs);
+
+ // system-wide registers
+ uint32_t test() const { return byte(0x01, 0, 8); }
+ uint32_t lfo_reset() const { return byte(0x01, 1, 1); }
+ uint32_t noise_frequency() const { return byte(0x0f, 0, 5) ^ 0x1f; }
+ uint32_t noise_enable() const { return byte(0x0f, 7, 1); }
+ uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); }
+ uint32_t timer_b_value() const { return byte(0x12, 0, 8); }
+ uint32_t csm() const { return byte(0x14, 7, 1); }
+ uint32_t reset_timer_b() const { return byte(0x14, 5, 1); }
+ uint32_t reset_timer_a() const { return byte(0x14, 4, 1); }
+ uint32_t enable_timer_b() const { return byte(0x14, 3, 1); }
+ uint32_t enable_timer_a() const { return byte(0x14, 2, 1); }
+ uint32_t load_timer_b() const { return byte(0x14, 1, 1); }
+ uint32_t load_timer_a() const { return byte(0x14, 0, 1); }
+ uint32_t lfo_rate() const { return byte(0x18, 0, 8); }
+ uint32_t lfo_am_depth() const { return byte(0x19, 0, 7); }
+ uint32_t lfo_pm_depth() const { return byte(0x1a, 0, 7); }
+ uint32_t output_bits() const { return byte(0x1b, 6, 2); }
+ uint32_t lfo_waveform() const { return byte(0x1b, 0, 2); }
+
+ // per-channel registers
+ uint32_t ch_output_any(uint32_t choffs) const { return byte(0x20, 6, 2, choffs); }
+ uint32_t ch_output_0(uint32_t choffs) const { return byte(0x20, 6, 1, choffs); }
+ uint32_t ch_output_1(uint32_t choffs) const { return byte(0x20, 7, 1, choffs); }
+ uint32_t ch_output_2(uint32_t choffs) const { return 0; }
+ uint32_t ch_output_3(uint32_t choffs) const { return 0; }
+ uint32_t ch_feedback(uint32_t choffs) const { return byte(0x20, 3, 3, choffs); }
+ uint32_t ch_algorithm(uint32_t choffs) const { return byte(0x20, 0, 3, choffs); }
+ uint32_t ch_block_freq(uint32_t choffs) const { return word(0x28, 0, 7, 0x30, 2, 6, choffs); }
+ uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return byte(0x38, 4, 3, choffs); }
+ uint32_t ch_lfo_am_sens(uint32_t choffs) const { return byte(0x38, 0, 2, choffs); }
+
+ // per-operator registers
+ uint32_t op_detune(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); }
+ uint32_t op_multiple(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); }
+ uint32_t op_total_level(uint32_t opoffs) const { return byte(0x60, 0, 7, opoffs); }
+ uint32_t op_ksr(uint32_t opoffs) const { return byte(0x80, 6, 2, opoffs); }
+ uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x80, 0, 5, opoffs); }
+ uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0xa0, 7, 1, opoffs); }
+ uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0xa0, 0, 5, opoffs); }
+ uint32_t op_detune2(uint32_t opoffs) const { return byte(0xc0, 6, 2, opoffs); }
+ uint32_t op_sustain_rate(uint32_t opoffs) const { return byte(0xc0, 0, 5, opoffs); }
+ uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0xe0, 4, 4, opoffs); }
+ uint32_t op_release_rate(uint32_t opoffs) const { return byte(0xe0, 0, 4, opoffs); }
+
+protected:
+ // return a bitfield extracted from a byte
+ uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const
+ {
+ return bitfield(m_regdata[offset + extra_offset], start, count);
+ }
+
+ // return a bitfield extracted from a pair of bytes, MSBs listed first
+ uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const
+ {
+ return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset);
+ }
+
+ // internal state
+ uint32_t m_lfo_counter; // LFO counter
+ uint32_t m_noise_lfsr; // noise LFSR state
+ uint8_t m_noise_counter; // noise counter
+ uint8_t m_noise_state; // latched noise state
+ uint8_t m_noise_lfo; // latched LFO noise value
+ uint8_t m_lfo_am; // current LFO AM value
+ uint8_t m_regdata[REGISTERS]; // register data
+ int16_t m_lfo_waveform[4][LFO_WAVEFORM_LENGTH]; // LFO waveforms; AM in low 8, PM in upper 8
+ uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms
+};
+
+
+
+//*********************************************************
+// OPM IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ym2151
+
+class ym2151
+{
+public:
+ using fm_engine = fm_engine_base<opm_registers>;
+ using output_data = fm_engine::output_data;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+
+ // constructor
+ ym2151(ymfm_interface &intf) : ym2151(intf, VARIANT_YM2151) { }
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // variants
+ enum opm_variant
+ {
+ VARIANT_YM2151,
+ VARIANT_YM2164
+ };
+
+ // internal constructor
+ ym2151(ymfm_interface &intf, opm_variant variant);
+
+ // internal state
+ opm_variant m_variant; // chip variant
+ uint8_t m_address; // address register
+ fm_engine m_fm; // core FM engine
+};
+
+
+
+//*********************************************************
+// OPP IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ym2164
+
+// the YM2164 is almost 100% functionally identical to the YM2151, except
+// it apparently has some mystery registers in the 00-07 range, and timer
+// B's frequency is half that of the 2151
+class ym2164 : public ym2151
+{
+public:
+ // constructor
+ ym2164(ymfm_interface &intf) : ym2151(intf, VARIANT_YM2164) { }
+};
+
+}
+
+
+#endif // YMFM_OPM_H
diff --git a/3rdparty/ymfm/src/ymfm_opn.cpp b/3rdparty/ymfm/src/ymfm_opn.cpp
new file mode 100644
index 00000000000..9a8f5e99feb
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opn.cpp
@@ -0,0 +1,2485 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_opn.h"
+#include "ymfm_fm.ipp"
+
+namespace ymfm
+{
+
+//*********************************************************
+// OPN/OPNA REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// opn_registers_base - constructor
+//-------------------------------------------------
+
+template<bool IsOpnA>
+opn_registers_base<IsOpnA>::opn_registers_base() :
+ m_lfo_counter(0),
+ m_lfo_am(0)
+{
+ // create the waveforms
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15);
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+template<bool IsOpnA>
+void opn_registers_base<IsOpnA>::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+ if (IsOpnA)
+ {
+ // enable output on both channels by default
+ m_regdata[0xb4] = m_regdata[0xb5] = m_regdata[0xb6] = 0xc0;
+ m_regdata[0x1b4] = m_regdata[0x1b5] = m_regdata[0x1b6] = 0xc0;
+ }
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+template<bool IsOpnA>
+void opn_registers_base<IsOpnA>::save_restore(ymfm_saved_state &state)
+{
+ if (IsOpnA)
+ {
+ state.save_restore(m_lfo_counter);
+ state.save_restore(m_lfo_am);
+ }
+ state.save_restore(m_regdata);
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPN this is fixed
+//-------------------------------------------------
+
+template<>
+void opn_registers_base<false>::operator_map(operator_mapping &dest) const
+{
+ // Note that the channel index order is 0,2,1,3, so we bitswap the index.
+ //
+ // This is because the order in the map is:
+ // carrier 1, carrier 2, modulator 1, modulator 2
+ //
+ // But when wiring up the connections, the more natural order is:
+ // carrier 1, modulator 1, carrier 2, modulator 2
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 6, 3, 9 ), // Channel 0 operators
+ operator_list( 1, 7, 4, 10 ), // Channel 1 operators
+ operator_list( 2, 8, 5, 11 ), // Channel 2 operators
+ } };
+ dest = s_fixed_map;
+}
+
+template<>
+void opn_registers_base<true>::operator_map(operator_mapping &dest) const
+{
+ // Note that the channel index order is 0,2,1,3, so we bitswap the index.
+ //
+ // This is because the order in the map is:
+ // carrier 1, carrier 2, modulator 1, modulator 2
+ //
+ // But when wiring up the connections, the more natural order is:
+ // carrier 1, modulator 1, carrier 2, modulator 2
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 6, 3, 9 ), // Channel 0 operators
+ operator_list( 1, 7, 4, 10 ), // Channel 1 operators
+ operator_list( 2, 8, 5, 11 ), // Channel 2 operators
+ operator_list( 12, 18, 15, 21 ), // Channel 3 operators
+ operator_list( 13, 19, 16, 22 ), // Channel 4 operators
+ operator_list( 14, 20, 17, 23 ), // Channel 5 operators
+ } };
+ dest = s_fixed_map;
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array
+//-------------------------------------------------
+
+template<bool IsOpnA>
+bool opn_registers_base<IsOpnA>::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
+{
+ assert(index < REGISTERS);
+
+ // writes in the 0xa0-af/0x1a0-af region are handled as latched pairs
+ // borrow unused registers 0xb8-bf as temporary holding locations
+ if ((index & 0xf0) == 0xa0)
+ {
+ if (bitfield(index, 0, 2) == 3)
+ return false;
+
+ uint32_t latchindex = 0xb8 | bitfield(index, 3);
+
+ // writes to the upper half just latch (only low 6 bits matter)
+ if (bitfield(index, 2))
+ m_regdata[latchindex] = data & 0x3f;
+
+ // writes to the lower half also apply said latch
+ else
+ {
+ m_regdata[index] = data;
+ m_regdata[index | 4] = m_regdata[latchindex];
+ }
+ return false;
+ }
+ else if ((index & 0xf8) == 0xb8)
+ {
+ // registers 0xb8-0xbf are used internally
+ return false;
+ }
+
+ // everything else is normal
+ m_regdata[index] = data;
+
+ // handle writes to the key on index
+ if (index == 0x28)
+ {
+ channel = bitfield(data, 0, 2);
+ if (channel == 3)
+ return false;
+ if (IsOpnA)
+ channel += bitfield(data, 2, 1) * 3;
+ opmask = bitfield(data, 4, 4);
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+template<bool IsOpnA>
+int32_t opn_registers_base<IsOpnA>::clock_noise_and_lfo()
+{
+ // OPN has no noise generation
+
+ // if LFO not enabled (not present on OPN), quick exit with 0s
+ if (!IsOpnA || !lfo_enable())
+ {
+ m_lfo_counter = 0;
+
+ // special case: if LFO is disabled on OPNA, it basically just keeps the counter
+ // at 0; since position 0 gives an AM value of 0x3f, it is important to reflect
+ // that here; for example, MegaDrive Venom plays some notes with LFO globally
+ // disabled but enabling LFO on the operators, and it expects this added attenutation
+ m_lfo_am = IsOpnA ? 0x3f : 0x00;
+ return 0;
+ }
+
+ // this table is based on converting the frequencies in the applications
+ // manual to clock dividers, based on the assumption of a 7-bit LFO value
+ static uint8_t const lfo_max_count[8] = { 109, 78, 72, 68, 63, 45, 9, 6 };
+ uint32_t subcount = uint8_t(m_lfo_counter++);
+
+ // when we cross the divider count, add enough to zero it and cause an
+ // increment at bit 8; the 7-bit value lives from bits 8-14
+ if (subcount >= lfo_max_count[lfo_rate()])
+ {
+ // note: to match the published values this should be 0x100 - subcount;
+ // however, tests on the hardware and nuked bear out an off-by-one
+ // error exists that causes the max LFO rate to be faster than published
+ m_lfo_counter += 0x101 - subcount;
+ }
+
+ // AM value is 7 bits, staring at bit 8; grab the low 6 directly
+ m_lfo_am = bitfield(m_lfo_counter, 8, 6);
+
+ // first half of the AM period (bit 6 == 0) is inverted
+ if (bitfield(m_lfo_counter, 8+6) == 0)
+ m_lfo_am ^= 0x3f;
+
+ // PM value is 5 bits, starting at bit 10; grab the low 3 directly
+ int32_t pm = bitfield(m_lfo_counter, 10, 3);
+
+ // PM is reflected based on bit 3
+ if (bitfield(m_lfo_counter, 10+3))
+ pm ^= 7;
+
+ // PM is negated based on bit 4
+ return bitfield(m_lfo_counter, 10+4) ? -pm : pm;
+}
+
+
+//-------------------------------------------------
+// lfo_am_offset - return the AM offset from LFO
+// for the given channel
+//-------------------------------------------------
+
+template<bool IsOpnA>
+uint32_t opn_registers_base<IsOpnA>::lfo_am_offset(uint32_t choffs) const
+{
+ // shift value for AM sensitivity is [7, 3, 1, 0],
+ // mapping to values of [0, 1.4, 5.9, and 11.8dB]
+ uint32_t am_shift = (1 << (ch_lfo_am_sens(choffs) ^ 3)) - 1;
+
+ // QUESTION: max sensitivity should give 11.8dB range, but this value
+ // is directly added to an x.8 attenuation value, which will only give
+ // 126/256 or ~4.9dB range -- what am I missing? The calculation below
+ // matches several other emulators, including the Nuked implemenation.
+
+ // raw LFO AM value on OPN is 0-3F, scale that up by a factor of 2
+ // (giving 7 bits) before applying the final shift
+ return (m_lfo_am << 1) >> am_shift;
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data
+//-------------------------------------------------
+
+template<bool IsOpnA>
+void opn_registers_base<IsOpnA>::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
+{
+ // set up the easy stuff
+ cache.waveform = &m_waveform[0][0];
+
+ // get frequency from the channel
+ uint32_t block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // if multi-frequency mode is enabled and this is channel 2,
+ // fetch one of the special frequencies
+ if (multi_freq() && choffs == 2)
+ {
+ if (opoffs == 2)
+ block_freq = cache.block_freq = multi_block_freq(1);
+ else if (opoffs == 10)
+ block_freq = cache.block_freq = multi_block_freq(2);
+ else if (opoffs == 6)
+ block_freq = cache.block_freq = multi_block_freq(0);
+ }
+
+ // compute the keycode: block_freq is:
+ //
+ // BBBFFFFFFFFFFF
+ // ^^^^???
+ //
+ // the 5-bit keycode uses the top 4 bits plus a magic formula
+ // for the final bit
+ uint32_t keycode = bitfield(block_freq, 10, 4) << 1;
+
+ // lowest bit is determined by a mix of next lower FNUM bits
+ // according to this equation from the YM2608 manual:
+ //
+ // (F11 & (F10 | F9 | F8)) | (!F11 & F10 & F9 & F8)
+ //
+ // for speed, we just look it up in a 16-bit constant
+ keycode |= bitfield(0xfe80, bitfield(block_freq, 7, 4));
+
+ // detune adjustment
+ cache.detune = detune_adjustment(op_detune(opoffs), keycode);
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ cache.multiple = op_multiple(opoffs) * 2;
+ if (cache.multiple == 0)
+ cache.multiple = 1;
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on
+ // block_freq, detune, and multiple, so compute it after we've done those
+ if (!IsOpnA || lfo_enable() == 0 || ch_lfo_pm_sens(choffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8
+ cache.total_level = op_total_level(opoffs) << 3;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // determine KSR adjustment for enevlope rates
+ uint32_t ksrval = keycode >> (op_ksr(opoffs) ^ 3);
+ cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval);
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+template<bool IsOpnA>
+uint32_t opn_registers_base<IsOpnA>::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm)
+{
+ // OPN phase calculation has only a single detune parameter
+ // and uses FNUMs instead of keycodes
+
+ // extract frequency number (low 11 bits of block_freq)
+ uint32_t fnum = bitfield(cache.block_freq, 0, 11) << 1;
+
+ // if there's a non-zero PM sensitivity, compute the adjustment
+ uint32_t pm_sensitivity = ch_lfo_pm_sens(choffs);
+ if (pm_sensitivity != 0)
+ {
+ // apply the phase adjustment based on the upper 7 bits
+ // of FNUM and the PM depth parameters
+ fnum += opn_lfo_pm_phase_adjustment(bitfield(cache.block_freq, 4, 7), pm_sensitivity, lfo_raw_pm);
+
+ // keep fnum to 12 bits
+ fnum &= 0xfff;
+ }
+
+ // apply block shift to compute phase step
+ uint32_t block = bitfield(cache.block_freq, 11, 3);
+ uint32_t phase_step = (fnum << block) >> 2;
+
+ // apply detune based on the keycode
+ phase_step += cache.detune;
+
+ // clamp to 17 bits in case detune overflows
+ // QUESTION: is this specific to the YM2612/3438?
+ phase_step &= 0x1ffff;
+
+ // apply frequency multiplier (which is cached as an x.1 value)
+ return (phase_step * cache.multiple) >> 1;
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+template<bool IsOpnA>
+std::string opn_registers_base<IsOpnA>::log_keyon(uint32_t choffs, uint32_t opoffs)
+{
+ uint32_t chnum = (choffs & 3) + 3 * bitfield(choffs, 8);
+ uint32_t opnum = (opoffs & 15) - ((opoffs & 15) / 4) + 12 * bitfield(opoffs, 8);
+
+ uint32_t block_freq = ch_block_freq(choffs);
+ if (multi_freq() && choffs == 2)
+ {
+ if (opoffs == 2)
+ block_freq = multi_block_freq(1);
+ else if (opoffs == 10)
+ block_freq = multi_block_freq(2);
+ else if (opoffs == 6)
+ block_freq = multi_block_freq(0);
+ }
+
+ char buffer[256];
+ int end = 0;
+
+ 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),
+ ch_feedback(choffs),
+ ch_algorithm(choffs),
+ op_multiple(opoffs),
+ op_total_level(opoffs),
+ op_ksr(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_sustain_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs));
+
+ if (OUTPUTS > 1)
+ 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(&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(&buffer[end], sizeof(buffer) - end, " am=%u", ch_lfo_am_sens(choffs));
+ bool pm = (ch_lfo_pm_sens(choffs) != 0);
+ if (pm)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs));
+ if (am || pm)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate());
+ if (multi_freq() && choffs == 2)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " multi=1");
+
+ return buffer;
+}
+
+
+
+//*********************************************************
+// SSG RESAMPLER
+//*********************************************************
+
+//-------------------------------------------------
+// add_last - helper to add the last computed
+// value to the sums, applying the given scale
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::add_last(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale)
+{
+ sum0 += m_last.data[0] * scale;
+ sum1 += m_last.data[1] * scale;
+ sum2 += m_last.data[2] * scale;
+}
+
+
+//-------------------------------------------------
+// clock_and_add - helper to clock a new value
+// and then add it to the sums, applying the
+// given scale
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::clock_and_add(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale)
+{
+ m_ssg.clock();
+ m_ssg.output(m_last);
+ add_last(sum0, sum1, sum2, scale);
+}
+
+
+//-------------------------------------------------
+// write_to_output - helper to write the sums to
+// the appropriate outputs, applying the given
+// divisor to the final result
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::write_to_output(OutputType *output, int32_t sum0, int32_t sum1, int32_t sum2, int32_t divisor)
+{
+ if (MixTo1)
+ {
+ // mixing to one, apply a 2/3 factor to prevent overflow
+ output->data[FirstOutput] = (sum0 + sum1 + sum2) * 2 / (3 * divisor);
+ }
+ else
+ {
+ // write three outputs in a row
+ output->data[FirstOutput + 0] = sum0 / divisor;
+ output->data[FirstOutput + 1] = sum1 / divisor;
+ output->data[FirstOutput + 2] = sum2 / divisor;
+ }
+
+ // track the sample index here
+ m_sampindex++;
+}
+
+
+//-------------------------------------------------
+// ssg_resampler - constructor
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+ssg_resampler<OutputType, FirstOutput, MixTo1>::ssg_resampler(ssg_engine &ssg) :
+ m_ssg(ssg),
+ m_sampindex(0),
+ m_resampler(&ssg_resampler::resample_nop)
+{
+ m_last.clear();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_sampindex);
+ state.save_restore(m_last.data);
+}
+
+
+//-------------------------------------------------
+// configure - configure a new ratio
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::configure(uint8_t outsamples, uint8_t srcsamples)
+{
+ switch (outsamples * 10 + srcsamples)
+ {
+ case 4*10 + 1: /* 4:1 */ m_resampler = &ssg_resampler::resample_n_1<4>; break;
+ case 2*10 + 1: /* 2:1 */ m_resampler = &ssg_resampler::resample_n_1<2>; break;
+ case 4*10 + 3: /* 4:3 */ m_resampler = &ssg_resampler::resample_4_3; break;
+ case 1*10 + 1: /* 1:1 */ m_resampler = &ssg_resampler::resample_n_1<1>; break;
+ case 2*10 + 3: /* 2:3 */ m_resampler = &ssg_resampler::resample_2_3; break;
+ case 1*10 + 3: /* 1:3 */ m_resampler = &ssg_resampler::resample_1_n<3>; break;
+ case 2*10 + 9: /* 2:9 */ m_resampler = &ssg_resampler::resample_2_9; break;
+ case 1*10 + 6: /* 1:6 */ m_resampler = &ssg_resampler::resample_1_n<6>; break;
+ case 0*10 + 0: /* 0:0 */ m_resampler = &ssg_resampler::resample_nop; break;
+ default: assert(false); break;
+ }
+}
+
+
+//-------------------------------------------------
+// resample_n_1 - resample SSG output to the
+// target at a rate of 1 SSG sample to every
+// n output sample
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+template<int Multiplier>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_n_1(OutputType *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ if (m_sampindex % Multiplier == 0)
+ {
+ m_ssg.clock();
+ m_ssg.output(m_last);
+ }
+ write_to_output(output, m_last.data[0], m_last.data[1], m_last.data[2]);
+ }
+}
+
+
+//-------------------------------------------------
+// resample_1_n - resample SSG output to the
+// target at a rate of n SSG samples to every
+// 1 output sample
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+template<int Divisor>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_1_n(OutputType *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ int32_t sum0 = 0, sum1 = 0, sum2 = 0;
+ for (int rep = 0; rep < Divisor; rep++)
+ clock_and_add(sum0, sum1, sum2);
+ write_to_output(output, sum0, sum1, sum2, Divisor);
+ }
+}
+
+
+//-------------------------------------------------
+// resample_2_9 - resample SSG output to the
+// target at a rate of 9 SSG samples to every
+// 2 output samples
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_2_9(OutputType *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ int32_t sum0 = 0, sum1 = 0, sum2 = 0;
+ if (bitfield(m_sampindex, 0) != 0)
+ add_last(sum0, sum1, sum2, 1);
+ clock_and_add(sum0, sum1, sum2, 2);
+ clock_and_add(sum0, sum1, sum2, 2);
+ clock_and_add(sum0, sum1, sum2, 2);
+ clock_and_add(sum0, sum1, sum2, 2);
+ if (bitfield(m_sampindex, 0) == 0)
+ clock_and_add(sum0, sum1, sum2, 1);
+ write_to_output(output, sum0, sum1, sum2, 9);
+ }
+}
+
+
+//-------------------------------------------------
+// resample_2_3 - resample SSG output to the
+// target at a rate of 3 SSG samples to every
+// 2 output samples
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_2_3(OutputType *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ int32_t sum0 = 0, sum1 = 0, sum2 = 0;
+ if (bitfield(m_sampindex, 0) == 0)
+ {
+ clock_and_add(sum0, sum1, sum2, 2);
+ clock_and_add(sum0, sum1, sum2, 1);
+ }
+ else
+ {
+ add_last(sum0, sum1, sum2, 1);
+ clock_and_add(sum0, sum1, sum2, 2);
+ }
+ write_to_output(output, sum0, sum1, sum2, 3);
+ }
+}
+
+
+//-------------------------------------------------
+// resample_4_3 - resample SSG output to the
+// target at a rate of 3 SSG samples to every
+// 4 output samples
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_4_3(OutputType *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ int32_t sum0 = 0, sum1 = 0, sum2 = 0;
+ int32_t step = bitfield(m_sampindex, 0, 2);
+ add_last(sum0, sum1, sum2, step);
+ if (step != 3)
+ clock_and_add(sum0, sum1, sum2, 3 - step);
+ write_to_output(output, sum0, sum1, sum2, 3);
+ }
+}
+
+
+//-------------------------------------------------
+// resample_nop - no-op resampler
+//-------------------------------------------------
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_nop(OutputType *output, uint32_t numsamples)
+{
+ // nothing to do except increment the sample index
+ m_sampindex += numsamples;
+}
+
+
+
+//*********************************************************
+// YM2203
+//*********************************************************
+
+//-------------------------------------------------
+// ym2203 - constructor
+//-------------------------------------------------
+
+ym2203::ym2203(ymfm_interface &intf) :
+ m_fidelity(OPN_FIDELITY_MAX),
+ m_address(0),
+ m_fm(intf),
+ m_ssg(intf),
+ m_ssg_resampler(m_ssg)
+{
+ m_last_fm.clear();
+ update_prescale(m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym2203::reset()
+{
+ // reset the engines
+ m_fm.reset();
+ m_ssg.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym2203::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ state.save_restore(m_last_fm.data);
+
+ m_fm.save_restore(state);
+ m_ssg.save_restore(state);
+ m_ssg_resampler.save_restore(state);
+
+ update_prescale(m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym2203::read_status()
+{
+ uint8_t result = m_fm.status();
+ if (m_fm.intf().ymfm_is_busy())
+ result |= fm_engine::STATUS_BUSY;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read_data - read the data register
+//-------------------------------------------------
+
+uint8_t ym2203::read_data()
+{
+ uint8_t result = 0;
+ if (m_address < 0x10)
+ {
+ // 00-0F: Read from SSG
+ result = m_ssg.read(m_address & 0x0f);
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym2203::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 1)
+ {
+ case 0: // status port
+ result = read_status();
+ break;
+
+ case 1: // data port (only SSG)
+ result = read_data();
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym2203::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+
+ // special case: update the prescale
+ if (m_address >= 0x2d && m_address <= 0x2f)
+ {
+ // 2D-2F: prescaler select
+ if (m_address == 0x2d)
+ update_prescale(6);
+ else if (m_address == 0x2e && m_fm.clock_prescale() == 6)
+ update_prescale(3);
+ else if (m_address == 0x2f)
+ update_prescale(2);
+ }
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2203::write_data(uint8_t data)
+{
+ if (m_address < 0x10)
+ {
+ // 00-0F: write to SSG
+ m_ssg.write(m_address & 0x0f, data);
+ }
+ else
+ {
+ // 10-FF: write to FM
+ m_fm.write(m_address, data);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2203::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 1)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ym2203::generate(output_data *output, uint32_t numsamples)
+{
+ // FM output is just repeated the prescale number of times; note that
+ // 0 is a special 1.5 case
+ if (m_fm_samples_per_output != 0)
+ {
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0)
+ clock_fm();
+ output->data[0] = m_last_fm.data[0];
+ }
+ }
+ else
+ {
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3;
+ if (step == 0)
+ clock_fm();
+ output->data[0] = m_last_fm.data[0];
+ if (step == 1)
+ {
+ clock_fm();
+ output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2;
+ }
+ }
+ }
+
+ // resample the SSG as configured
+ m_ssg_resampler.resample(output - numsamples, numsamples);
+}
+
+
+//-------------------------------------------------
+// update_prescale - update the prescale value,
+// recomputing derived values
+//-------------------------------------------------
+
+void ym2203::update_prescale(uint8_t prescale)
+{
+ // tell the FM engine
+ m_fm.set_clock_prescale(prescale);
+ m_ssg.prescale_changed();
+
+ // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum-----
+ // rate = clock/24 rate = clock/12 rate = clock/4
+ // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate
+ // 6 3:1 2:3 6:1 4:3 18:1 4:1
+ // 3 1.5:1 1:3 3:1 2:3 9:1 2:1
+ // 2 1:1 1:6 2:1 1:3 6:1 1:1
+
+ // compute the number of FM samples per output sample, and select the
+ // resampler function
+ if (m_fidelity == OPN_FIDELITY_MIN)
+ {
+ switch (prescale)
+ {
+ default:
+ case 6: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break;
+ case 3: m_fm_samples_per_output = 0; m_ssg_resampler.configure(1, 3); break;
+ case 2: m_fm_samples_per_output = 1; m_ssg_resampler.configure(1, 6); break;
+ }
+ }
+ else if (m_fidelity == OPN_FIDELITY_MED)
+ {
+ switch (prescale)
+ {
+ default:
+ case 6: m_fm_samples_per_output = 6; m_ssg_resampler.configure(4, 3); break;
+ case 3: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break;
+ case 2: m_fm_samples_per_output = 2; m_ssg_resampler.configure(1, 3); break;
+ }
+ }
+ else
+ {
+ switch (prescale)
+ {
+ default:
+ case 6: m_fm_samples_per_output = 18; m_ssg_resampler.configure(4, 1); break;
+ case 3: m_fm_samples_per_output = 9; m_ssg_resampler.configure(2, 1); break;
+ case 2: m_fm_samples_per_output = 6; m_ssg_resampler.configure(1, 1); break;
+ }
+ }
+
+ // if overriding the SSG, override the configuration with the nop
+ // resampler to at least keep the sample index moving forward
+ if (m_ssg.overridden())
+ m_ssg_resampler.configure(0, 0);
+}
+
+
+//-------------------------------------------------
+// clock_fm - clock FM state
+//-------------------------------------------------
+
+void ym2203::clock_fm()
+{
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; OPN is full 14-bit with no intermediate clipping
+ m_fm.output(m_last_fm.clear(), 0, 32767, fm_engine::ALL_CHANNELS);
+
+ // convert to 10.3 floating point value for the DAC and back
+ m_last_fm.roundtrip_fp();
+}
+
+
+
+//*********************************************************
+// YM2608
+//*********************************************************
+
+//-------------------------------------------------
+// ym2608 - constructor
+//-------------------------------------------------
+
+ym2608::ym2608(ymfm_interface &intf) :
+ m_fidelity(OPN_FIDELITY_MAX),
+ m_address(0),
+ m_irq_enable(0x1f),
+ m_flag_control(0x1c),
+ m_fm(intf),
+ m_ssg(intf),
+ m_ssg_resampler(m_ssg),
+ m_adpcm_a(intf, 0),
+ m_adpcm_b(intf)
+{
+ m_last_fm.clear();
+ update_prescale(m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym2608::reset()
+{
+ // reset the engines
+ m_fm.reset();
+ m_ssg.reset();
+ m_adpcm_a.reset();
+ m_adpcm_b.reset();
+
+ // configure ADPCM percussion sounds; these are present in an embedded ROM
+ m_adpcm_a.set_start_end(0, 0x0000, 0x01bf); // bass drum
+ m_adpcm_a.set_start_end(1, 0x01c0, 0x043f); // snare drum
+ m_adpcm_a.set_start_end(2, 0x0440, 0x1b7f); // top cymbal
+ m_adpcm_a.set_start_end(3, 0x1b80, 0x1cff); // high hat
+ m_adpcm_a.set_start_end(4, 0x1d00, 0x1f7f); // tom tom
+ m_adpcm_a.set_start_end(5, 0x1f80, 0x1fff); // rim shot
+
+ // initialize our special interrupt states, then read the upper status
+ // register, which updates the IRQs
+ m_irq_enable = 0x1f;
+ m_flag_control = 0x1c;
+ read_status_hi();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym2608::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ state.save_restore(m_irq_enable);
+ state.save_restore(m_flag_control);
+ state.save_restore(m_last_fm.data);
+
+ m_fm.save_restore(state);
+ m_ssg.save_restore(state);
+ m_ssg_resampler.save_restore(state);
+ m_adpcm_a.save_restore(state);
+ m_adpcm_b.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym2608::read_status()
+{
+ uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB);
+ if (m_fm.intf().ymfm_is_busy())
+ result |= fm_engine::STATUS_BUSY;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read_data - read the data register
+//-------------------------------------------------
+
+uint8_t ym2608::read_data()
+{
+ uint8_t result = 0;
+ if (m_address < 0x10)
+ {
+ // 00-0F: Read from SSG
+ result = m_ssg.read(m_address & 0x0f);
+ }
+ else if (m_address == 0xff)
+ {
+ // FF: ID code
+ result = 1;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// read_status_hi - read the extended status
+// register
+//-------------------------------------------------
+
+uint8_t ym2608::read_status_hi()
+{
+ // fetch regular status
+ uint8_t status = m_fm.status() & ~(STATUS_ADPCM_B_EOS | STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_PLAYING);
+
+ // fetch ADPCM-B status, and merge in the bits
+ uint8_t adpcm_status = m_adpcm_b.status();
+ if ((adpcm_status & adpcm_b_channel::STATUS_EOS) != 0)
+ status |= STATUS_ADPCM_B_EOS;
+ if ((adpcm_status & adpcm_b_channel::STATUS_BRDY) != 0)
+ status |= STATUS_ADPCM_B_BRDY;
+ if ((adpcm_status & adpcm_b_channel::STATUS_PLAYING) != 0)
+ status |= STATUS_ADPCM_B_PLAYING;
+
+ // turn off any bits that have been requested to be masked
+ status &= ~(m_flag_control & 0x1f);
+
+ // update the status so that IRQs are propagated
+ m_fm.set_reset_status(status, ~status);
+
+ // merge in the busy flag
+ if (m_fm.intf().ymfm_is_busy())
+ status |= fm_engine::STATUS_BUSY;
+ return status;
+}
+
+
+//-------------------------------------------------
+// read_data_hi - read the upper data register
+//-------------------------------------------------
+
+uint8_t ym2608::read_data_hi()
+{
+ uint8_t result = 0;
+ if ((m_address & 0xff) < 0x10)
+ {
+ // 00-0F: Read from ADPCM-B
+ result = m_adpcm_b.read(m_address & 0x0f);
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym2608::read(uint32_t offset)
+{
+ uint8_t result = 0;
+ switch (offset & 3)
+ {
+ case 0: // status port, YM2203 compatible
+ result = read_status();
+ break;
+
+ case 1: // data port (only SSG)
+ result = read_data();
+ break;
+
+ case 2: // status port, extended
+ result = read_status_hi();
+ break;
+
+ case 3: // ADPCM-B data
+ result = read_data_hi();
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym2608::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+
+ // special case: update the prescale
+ if (m_address >= 0x2d && m_address <= 0x2f)
+ {
+ // 2D-2F: prescaler select
+ if (m_address == 0x2d)
+ update_prescale(6);
+ else if (m_address == 0x2e && m_fm.clock_prescale() == 6)
+ update_prescale(3);
+ else if (m_address == 0x2f)
+ update_prescale(2);
+ }
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the data register
+//-------------------------------------------------
+
+void ym2608::write_data(uint8_t data)
+{
+ // ignore if paired with upper address
+ if (bitfield(m_address, 8))
+ return;
+
+ if (m_address < 0x10)
+ {
+ // 00-0F: write to SSG
+ m_ssg.write(m_address & 0x0f, data);
+ }
+ else if (m_address < 0x20)
+ {
+ // 10-1F: write to ADPCM-A
+ m_adpcm_a.write(m_address & 0x0f, data);
+ }
+ else if (m_address == 0x29)
+ {
+ // 29: special IRQ mask register
+ m_irq_enable = data;
+ m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f);
+ }
+ else
+ {
+ // 20-28, 2A-FF: write to FM
+ m_fm.write(m_address, data);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write_address_hi - handle a write to the upper
+// address register
+//-------------------------------------------------
+
+void ym2608::write_address_hi(uint8_t data)
+{
+ // just set the address
+ m_address = 0x100 | data;
+}
+
+
+//-------------------------------------------------
+// write_data_hi - handle a write to the upper
+// data register
+//-------------------------------------------------
+
+void ym2608::write_data_hi(uint8_t data)
+{
+ // ignore if paired with upper address
+ if (!bitfield(m_address, 8))
+ return;
+
+ if (m_address < 0x110)
+ {
+ // 100-10F: write to ADPCM-B
+ m_adpcm_b.write(m_address & 0x0f, data);
+ }
+ else if (m_address == 0x110)
+ {
+ // 110: IRQ flag control
+ if (bitfield(data, 7))
+ m_fm.set_reset_status(0, 0xff);
+ else
+ {
+ m_flag_control = data;
+ m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f);
+ }
+ }
+ else
+ {
+ // 111-1FF: write to FM
+ m_fm.write(m_address, data);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2608::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 3)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+
+ case 2: // upper address port
+ write_address_hi(data);
+ break;
+
+ case 3: // upper data port
+ write_data_hi(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ym2608::generate(output_data *output, uint32_t numsamples)
+{
+ // FM output is just repeated the prescale number of times; note that
+ // 0 is a special 1.5 case
+ if (m_fm_samples_per_output != 0)
+ {
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0)
+ clock_fm_and_adpcm();
+ output->data[0] = m_last_fm.data[0];
+ output->data[1] = m_last_fm.data[1];
+ }
+ }
+ else
+ {
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3;
+ if (step == 0)
+ clock_fm_and_adpcm();
+ output->data[0] = m_last_fm.data[0];
+ output->data[1] = m_last_fm.data[1];
+ if (step == 1)
+ {
+ clock_fm_and_adpcm();
+ output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2;
+ output->data[1] = (output->data[1] + m_last_fm.data[1]) / 2;
+ }
+ }
+ }
+
+ // resample the SSG as configured
+ m_ssg_resampler.resample(output - numsamples, numsamples);
+}
+
+
+//-------------------------------------------------
+// update_prescale - update the prescale value,
+// recomputing derived values
+//-------------------------------------------------
+
+void ym2608::update_prescale(uint8_t prescale)
+{
+ // tell the FM engine
+ m_fm.set_clock_prescale(prescale);
+ m_ssg.prescale_changed();
+
+ // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum-----
+ // rate = clock/48 rate = clock/24 rate = clock/8
+ // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate
+ // 6 3:1 2:3 6:1 4:3 18:1 4:1
+ // 3 1.5:1 1:3 3:1 2:3 9:1 2:1
+ // 2 1:1 1:6 2:1 1:3 6:1 1:1
+
+ // compute the number of FM samples per output sample, and select the
+ // resampler function
+ if (m_fidelity == OPN_FIDELITY_MIN)
+ {
+ switch (prescale)
+ {
+ default:
+ case 6: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break;
+ case 3: m_fm_samples_per_output = 0; m_ssg_resampler.configure(1, 3); break;
+ case 2: m_fm_samples_per_output = 1; m_ssg_resampler.configure(1, 6); break;
+ }
+ }
+ else if (m_fidelity == OPN_FIDELITY_MED)
+ {
+ switch (prescale)
+ {
+ default:
+ case 6: m_fm_samples_per_output = 6; m_ssg_resampler.configure(4, 3); break;
+ case 3: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break;
+ case 2: m_fm_samples_per_output = 2; m_ssg_resampler.configure(1, 3); break;
+ }
+ }
+ else
+ {
+ switch (prescale)
+ {
+ default:
+ case 6: m_fm_samples_per_output = 18; m_ssg_resampler.configure(4, 1); break;
+ case 3: m_fm_samples_per_output = 9; m_ssg_resampler.configure(2, 1); break;
+ case 2: m_fm_samples_per_output = 6; m_ssg_resampler.configure(1, 1); break;
+ }
+ }
+
+ // if overriding the SSG, override the configuration with the nop
+ // resampler to at least keep the sample index moving forward
+ if (m_ssg.overridden())
+ m_ssg_resampler.configure(0, 0);
+}
+
+
+//-------------------------------------------------
+// clock_fm_and_adpcm - clock FM and ADPCM state
+//-------------------------------------------------
+
+void ym2608::clock_fm_and_adpcm()
+{
+ // top bit of the IRQ enable flags controls 3-channel vs 6-channel mode
+ uint32_t fmmask = bitfield(m_irq_enable, 7) ? 0x3f : 0x07;
+
+ // clock the system
+ uint32_t env_counter = m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // clock the ADPCM-A engine on every envelope cycle
+ // (channels 4 and 5 clock every 2 envelope clocks)
+ if (bitfield(env_counter, 0, 2) == 0)
+ m_adpcm_a.clock(bitfield(env_counter, 2) ? 0x0f : 0x3f);
+
+ // clock the ADPCM-B engine every cycle
+ m_adpcm_b.clock();
+
+ // update the FM content; OPNA is 13-bit with no intermediate clipping
+ m_fm.output(m_last_fm.clear(), 1, 32767, fmmask);
+
+ // mix in the ADPCM and clamp
+ m_adpcm_a.output(m_last_fm, 0x3f);
+ m_adpcm_b.output(m_last_fm, 1);
+ m_last_fm.clamp16();
+}
+
+
+//*********************************************************
+// YMF288
+//*********************************************************
+
+// YMF288 is a YM2608 with the following changes:
+// * ADPCM-B part removed
+// * prescaler removed (fixed at 6)
+// * CSM removed
+// * Low power mode added
+// * SSG tone frequency is altered in some way? (explicitly DC for Tp 0-7, also double volume in some cases)
+// * I/O ports removed
+// * Shorter busy times
+// * All registers can be read
+
+//-------------------------------------------------
+// ymf288 - constructor
+//-------------------------------------------------
+
+ymf288::ymf288(ymfm_interface &intf) :
+ m_fidelity(OPN_FIDELITY_MAX),
+ m_address(0),
+ m_irq_enable(0x03),
+ m_flag_control(0x03),
+ m_fm(intf),
+ m_ssg(intf),
+ m_ssg_resampler(m_ssg),
+ m_adpcm_a(intf, 0)
+{
+ m_last_fm.clear();
+ update_prescale();
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ymf288::reset()
+{
+ // reset the engines
+ m_fm.reset();
+ m_ssg.reset();
+ m_adpcm_a.reset();
+
+ // configure ADPCM percussion sounds; these are present in an embedded ROM
+ m_adpcm_a.set_start_end(0, 0x0000, 0x01bf); // bass drum
+ m_adpcm_a.set_start_end(1, 0x01c0, 0x043f); // snare drum
+ m_adpcm_a.set_start_end(2, 0x0440, 0x1b7f); // top cymbal
+ m_adpcm_a.set_start_end(3, 0x1b80, 0x1cff); // high hat
+ m_adpcm_a.set_start_end(4, 0x1d00, 0x1f7f); // tom tom
+ m_adpcm_a.set_start_end(5, 0x1f80, 0x1fff); // rim shot
+
+ // initialize our special interrupt states, then read the upper status
+ // register, which updates the IRQs
+ m_irq_enable = 0x03;
+ m_flag_control = 0x00;
+ read_status_hi();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ymf288::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ state.save_restore(m_irq_enable);
+ state.save_restore(m_flag_control);
+ state.save_restore(m_last_fm.data);
+
+ m_fm.save_restore(state);
+ m_ssg.save_restore(state);
+ m_ssg_resampler.save_restore(state);
+ m_adpcm_a.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ymf288::read_status()
+{
+ uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB);
+ if (m_fm.intf().ymfm_is_busy())
+ result |= fm_engine::STATUS_BUSY;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read_data - read the data register
+//-------------------------------------------------
+
+uint8_t ymf288::read_data()
+{
+ uint8_t result = 0;
+ if (m_address < 0x0e)
+ {
+ // 00-0D: Read from SSG
+ result = m_ssg.read(m_address & 0x0f);
+ }
+ else if (m_address < 0x10)
+ {
+ // 0E-0F: I/O ports not supported
+ result = 0xff;
+ }
+ else if (m_address == 0xff)
+ {
+ // FF: ID code
+ result = 2;
+ }
+ else if (ymf288_mode())
+ {
+ // registers are readable in YMF288 mode
+ result = m_fm.regs().read(m_address);
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// read_status_hi - read the extended status
+// register
+//-------------------------------------------------
+
+uint8_t ymf288::read_status_hi()
+{
+ // fetch regular status
+ uint8_t status = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB);
+
+ // turn off any bits that have been requested to be masked
+ status &= ~(m_flag_control & 0x03);
+
+ // update the status so that IRQs are propagated
+ m_fm.set_reset_status(status, ~status);
+
+ // merge in the busy flag
+ if (m_fm.intf().ymfm_is_busy())
+ status |= fm_engine::STATUS_BUSY;
+ return status;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ymf288::read(uint32_t offset)
+{
+ uint8_t result = 0;
+ switch (offset & 3)
+ {
+ case 0: // status port, YM2203 compatible
+ result = read_status();
+ break;
+
+ case 1: // data port
+ result = read_data();
+ break;
+
+ case 2: // status port, extended
+ result = read_status_hi();
+ break;
+
+ case 3: // unmapped
+ debug::log_unexpected_read_write("Unexpected read from YMF288 offset %d\n", offset & 3);
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ymf288::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+
+ // in YMF288 mode, busy is signaled after address writes too
+ if (ymf288_mode())
+ m_fm.intf().ymfm_set_busy_end(16);
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the data register
+//-------------------------------------------------
+
+void ymf288::write_data(uint8_t data)
+{
+ // ignore if paired with upper address
+ if (bitfield(m_address, 8))
+ return;
+
+ // wait times are shorter in YMF288 mode
+ int busy_cycles = ymf288_mode() ? 16 : 32 * m_fm.clock_prescale();
+ if (m_address < 0x0e)
+ {
+ // 00-0D: write to SSG
+ m_ssg.write(m_address & 0x0f, data);
+ }
+ else if (m_address < 0x10)
+ {
+ // 0E-0F: I/O ports not supported
+ }
+ else if (m_address < 0x20)
+ {
+ // 10-1F: write to ADPCM-A
+ m_adpcm_a.write(m_address & 0x0f, data);
+ busy_cycles = 32 * m_fm.clock_prescale();
+ }
+ else if (m_address == 0x27)
+ {
+ // 27: mode register; CSM isn't supported so disable it
+ data &= 0x7f;
+ m_fm.write(m_address, data);
+ }
+ else if (m_address == 0x29)
+ {
+ // 29: special IRQ mask register
+ m_irq_enable = data;
+ m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x03);
+ }
+ else
+ {
+ // 20-27, 2A-FF: write to FM
+ m_fm.write(m_address, data);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(busy_cycles);
+}
+
+
+//-------------------------------------------------
+// write_address_hi - handle a write to the upper
+// address register
+//-------------------------------------------------
+
+void ymf288::write_address_hi(uint8_t data)
+{
+ // just set the address
+ m_address = 0x100 | data;
+
+ // in YMF288 mode, busy is signaled after address writes too
+ if (ymf288_mode())
+ m_fm.intf().ymfm_set_busy_end(16);
+}
+
+
+//-------------------------------------------------
+// write_data_hi - handle a write to the upper
+// data register
+//-------------------------------------------------
+
+void ymf288::write_data_hi(uint8_t data)
+{
+ // ignore if paired with upper address
+ if (!bitfield(m_address, 8))
+ return;
+
+ // wait times are shorter in YMF288 mode
+ int busy_cycles = ymf288_mode() ? 16 : 32 * m_fm.clock_prescale();
+ if (m_address == 0x110)
+ {
+ // 110: IRQ flag control
+ if (bitfield(data, 7))
+ m_fm.set_reset_status(0, 0xff);
+ else
+ {
+ m_flag_control = data;
+ m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x03);
+ }
+ }
+ else
+ {
+ // 100-10F,111-1FF: write to FM
+ m_fm.write(m_address, data);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(busy_cycles);
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ymf288::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 3)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+
+ case 2: // upper address port
+ write_address_hi(data);
+ break;
+
+ case 3: // upper data port
+ write_data_hi(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ymf288::generate(output_data *output, uint32_t numsamples)
+{
+ // FM output is just repeated the prescale number of times; note that
+ // 0 is a special 1.5 case
+ if (m_fm_samples_per_output != 0)
+ {
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0)
+ clock_fm_and_adpcm();
+ output->data[0] = m_last_fm.data[0];
+ output->data[1] = m_last_fm.data[1];
+ }
+ }
+ else
+ {
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3;
+ if (step == 0)
+ clock_fm_and_adpcm();
+ output->data[0] = m_last_fm.data[0];
+ output->data[1] = m_last_fm.data[1];
+ if (step == 1)
+ {
+ clock_fm_and_adpcm();
+ output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2;
+ output->data[1] = (output->data[1] + m_last_fm.data[1]) / 2;
+ }
+ }
+ }
+
+ // resample the SSG as configured
+ m_ssg_resampler.resample(output - numsamples, numsamples);
+}
+
+
+//-------------------------------------------------
+// update_prescale - update the prescale value,
+// recomputing derived values
+//-------------------------------------------------
+
+void ymf288::update_prescale()
+{
+ // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum-----
+ // rate = clock/144 rate = clock/144 rate = clock/16
+ // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate
+ // 6 1:1 2:9 1:1 2:9 9:1 2:1
+
+ // compute the number of FM samples per output sample, and select the
+ // resampler function
+ if (m_fidelity == OPN_FIDELITY_MIN || m_fidelity == OPN_FIDELITY_MED)
+ {
+ m_fm_samples_per_output = 1;
+ m_ssg_resampler.configure(2, 9);
+ }
+ else
+ {
+ m_fm_samples_per_output = 9;
+ m_ssg_resampler.configure(2, 1);
+ }
+
+ // if overriding the SSG, override the configuration with the nop
+ // resampler to at least keep the sample index moving forward
+ if (m_ssg.overridden())
+ m_ssg_resampler.configure(0, 0);
+}
+
+
+//-------------------------------------------------
+// clock_fm_and_adpcm - clock FM and ADPCM state
+//-------------------------------------------------
+
+void ymf288::clock_fm_and_adpcm()
+{
+ // top bit of the IRQ enable flags controls 3-channel vs 6-channel mode
+ uint32_t fmmask = bitfield(m_irq_enable, 7) ? 0x3f : 0x07;
+
+ // clock the system
+ uint32_t env_counter = m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // clock the ADPCM-A engine on every envelope cycle
+ // (channels 4 and 5 clock every 2 envelope clocks)
+ if (bitfield(env_counter, 0, 2) == 0)
+ m_adpcm_a.clock(bitfield(env_counter, 2) ? 0x0f : 0x3f);
+
+ // update the FM content; OPNA is 13-bit with no intermediate clipping
+ m_fm.output(m_last_fm.clear(), 1, 32767, fmmask);
+
+ // mix in the ADPCM
+ m_adpcm_a.output(m_last_fm, 0x3f);
+}
+
+
+
+//*********************************************************
+// YM2610
+//*********************************************************
+
+//-------------------------------------------------
+// ym2610 - constructor
+//-------------------------------------------------
+
+ym2610::ym2610(ymfm_interface &intf, uint8_t channel_mask) :
+ m_fidelity(OPN_FIDELITY_MAX),
+ m_address(0),
+ m_fm_mask(channel_mask),
+ m_eos_status(0x00),
+ m_flag_mask(EOS_FLAGS_MASK),
+ m_fm(intf),
+ m_ssg(intf),
+ m_ssg_resampler(m_ssg),
+ m_adpcm_a(intf, 8),
+ m_adpcm_b(intf, 8)
+{
+ update_prescale();
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym2610::reset()
+{
+ // reset the engines
+ m_fm.reset();
+ m_ssg.reset();
+ m_adpcm_a.reset();
+ m_adpcm_b.reset();
+
+ // initialize our special interrupt states
+ m_eos_status = 0x00;
+ m_flag_mask = EOS_FLAGS_MASK;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym2610::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ state.save_restore(m_eos_status);
+ state.save_restore(m_flag_mask);
+
+ m_fm.save_restore(state);
+ m_ssg.save_restore(state);
+ m_ssg_resampler.save_restore(state);
+ m_adpcm_a.save_restore(state);
+ m_adpcm_b.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym2610::read_status()
+{
+ uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB);
+ if (m_fm.intf().ymfm_is_busy())
+ result |= fm_engine::STATUS_BUSY;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read_data - read the data register
+//-------------------------------------------------
+
+uint8_t ym2610::read_data()
+{
+ uint8_t result = 0;
+ if (m_address < 0x0e)
+ {
+ // 00-0D: Read from SSG
+ result = m_ssg.read(m_address & 0x0f);
+ }
+ else if (m_address < 0x10)
+ {
+ // 0E-0F: I/O ports not supported
+ result = 0xff;
+ }
+ else if (m_address == 0xff)
+ {
+ // FF: ID code
+ result = 1;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// read_status_hi - read the extended status
+// register
+//-------------------------------------------------
+
+uint8_t ym2610::read_status_hi()
+{
+ return m_eos_status & m_flag_mask;
+}
+
+
+//-------------------------------------------------
+// read_data_hi - read the upper data register
+//-------------------------------------------------
+
+uint8_t ym2610::read_data_hi()
+{
+ uint8_t result = 0;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym2610::read(uint32_t offset)
+{
+ uint8_t result = 0;
+ switch (offset & 3)
+ {
+ case 0: // status port, YM2203 compatible
+ result = read_status();
+ break;
+
+ case 1: // data port (only SSG)
+ result = read_data();
+ break;
+
+ case 2: // status port, extended
+ result = read_status_hi();
+ break;
+
+ case 3: // ADPCM-B data
+ result = read_data_hi();
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym2610::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the data register
+//-------------------------------------------------
+
+void ym2610::write_data(uint8_t data)
+{
+ // ignore if paired with upper address
+ if (bitfield(m_address, 8))
+ return;
+
+ if (m_address < 0x0e)
+ {
+ // 00-0D: write to SSG
+ m_ssg.write(m_address & 0x0f, data);
+ }
+ else if (m_address < 0x10)
+ {
+ // 0E-0F: I/O ports not supported
+ }
+ else if (m_address < 0x1c)
+ {
+ // 10-1B: write to ADPCM-B
+ // YM2610 effectively forces external mode on, and disables recording
+ if (m_address == 0x10)
+ data = (data | 0x20) & ~0x40;
+ m_adpcm_b.write(m_address & 0x0f, data);
+ }
+ else if (m_address == 0x1c)
+ {
+ // 1C: EOS flag reset
+ m_flag_mask = ~data & EOS_FLAGS_MASK;
+ m_eos_status &= ~(data & EOS_FLAGS_MASK);
+ }
+ else
+ {
+ // 1D-FF: write to FM
+ m_fm.write(m_address, data);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write_address_hi - handle a write to the upper
+// address register
+//-------------------------------------------------
+
+void ym2610::write_address_hi(uint8_t data)
+{
+ // just set the address
+ m_address = 0x100 | data;
+}
+
+
+//-------------------------------------------------
+// write_data_hi - handle a write to the upper
+// data register
+//-------------------------------------------------
+
+void ym2610::write_data_hi(uint8_t data)
+{
+ // ignore if paired with upper address
+ if (!bitfield(m_address, 8))
+ return;
+
+ if (m_address < 0x130)
+ {
+ // 100-12F: write to ADPCM-A
+ m_adpcm_a.write(m_address & 0x3f, data);
+ }
+ else
+ {
+ // 130-1FF: write to FM
+ m_fm.write(m_address, data);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2610::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 3)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+
+ case 2: // upper address port
+ write_address_hi(data);
+ break;
+
+ case 3: // upper data port
+ write_data_hi(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ym2610::generate(output_data *output, uint32_t numsamples)
+{
+ // FM output is just repeated the prescale number of times
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0)
+ clock_fm_and_adpcm();
+ output->data[0] = m_last_fm.data[0];
+ output->data[1] = m_last_fm.data[1];
+ }
+
+ // resample the SSG as configured
+ m_ssg_resampler.resample(output - numsamples, numsamples);
+}
+
+
+//-------------------------------------------------
+// update_prescale - update the prescale value,
+// recomputing derived values
+//-------------------------------------------------
+
+void ym2610::update_prescale()
+{
+ // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum-----
+ // rate = clock/144 rate = clock/144 rate = clock/16
+ // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate
+ // 6 1:1 2:9 1:1 2:9 9:1 2:1
+
+ // compute the number of FM samples per output sample, and select the
+ // resampler function
+ if (m_fidelity == OPN_FIDELITY_MIN || m_fidelity == OPN_FIDELITY_MED)
+ {
+ m_fm_samples_per_output = 1;
+ m_ssg_resampler.configure(2, 9);
+ }
+ else
+ {
+ m_fm_samples_per_output = 9;
+ m_ssg_resampler.configure(2, 1);
+ }
+
+ // if overriding the SSG, override the configuration with the nop
+ // resampler to at least keep the sample index moving forward
+ if (m_ssg.overridden())
+ m_ssg_resampler.configure(0, 0);
+}
+
+
+//-------------------------------------------------
+// clock_fm_and_adpcm - clock FM and ADPCM state
+//-------------------------------------------------
+
+void ym2610::clock_fm_and_adpcm()
+{
+ // clock the system
+ uint32_t env_counter = m_fm.clock(m_fm_mask);
+
+ // clock the ADPCM-A engine on every envelope cycle
+ if (bitfield(env_counter, 0, 2) == 0)
+ m_eos_status |= m_adpcm_a.clock(0x3f);
+
+ // clock the ADPCM-B engine every cycle
+ m_adpcm_b.clock();
+
+ // we track the last ADPCM-B EOS value in bit 6 (which is hidden from callers);
+ // if it changed since the last sample, update the visible EOS state in bit 7
+ uint8_t live_eos = ((m_adpcm_b.status() & adpcm_b_channel::STATUS_EOS) != 0) ? 0x40 : 0x00;
+ if (((live_eos ^ m_eos_status) & 0x40) != 0)
+ m_eos_status = (m_eos_status & ~0xc0) | live_eos | (live_eos << 1);
+
+ // update the FM content; OPNB is 13-bit with no intermediate clipping
+ m_fm.output(m_last_fm.clear(), 1, 32767, m_fm_mask);
+
+ // mix in the ADPCM and clamp
+ m_adpcm_a.output(m_last_fm, 0x3f);
+ m_adpcm_b.output(m_last_fm, 1);
+ m_last_fm.clamp16();
+}
+
+
+
+//*********************************************************
+// YM2612
+//*********************************************************
+
+//-------------------------------------------------
+// ym2612 - constructor
+//-------------------------------------------------
+
+ym2612::ym2612(ymfm_interface &intf) :
+ m_address(0),
+ m_dac_data(0),
+ m_dac_enable(0),
+ m_fm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym2612::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym2612::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_address);
+ state.save_restore(m_dac_data);
+ state.save_restore(m_dac_enable);
+ m_fm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym2612::read_status()
+{
+ uint8_t result = m_fm.status();
+ if (m_fm.intf().ymfm_is_busy())
+ result |= fm_engine::STATUS_BUSY;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym2612::read(uint32_t offset)
+{
+ uint8_t result = 0;
+ switch (offset & 3)
+ {
+ case 0: // status port, YM2203 compatible
+ result = read_status();
+ break;
+
+ case 1: // data port (unused)
+ case 2: // status port, extended
+ case 3: // data port (unused)
+ debug::log_unexpected_read_write("Unexpected read from YM2612 offset %d\n", offset & 3);
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym2612::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write_data - handle a write to the data
+// register
+//-------------------------------------------------
+
+void ym2612::write_data(uint8_t data)
+{
+ // ignore if paired with upper address
+ if (bitfield(m_address, 8))
+ return;
+
+ if (m_address == 0x2a)
+ {
+ // 2A: DAC data (most significant 8 bits)
+ m_dac_data = (m_dac_data & ~0x1fe) | ((data ^ 0x80) << 1);
+ }
+ else if (m_address == 0x2b)
+ {
+ // 2B: DAC enable (bit 7)
+ m_dac_enable = bitfield(data, 7);
+ }
+ else if (m_address == 0x2c)
+ {
+ // 2C: test/low DAC bit
+ m_dac_data = (m_dac_data & ~1) | bitfield(data, 3);
+ }
+ else
+ {
+ // 00-29, 2D-FF: write to FM
+ m_fm.write(m_address, data);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write_address_hi - handle a write to the upper
+// address register
+//-------------------------------------------------
+
+void ym2612::write_address_hi(uint8_t data)
+{
+ // just set the address
+ m_address = 0x100 | data;
+}
+
+
+//-------------------------------------------------
+// write_data_hi - handle a write to the upper
+// data register
+//-------------------------------------------------
+
+void ym2612::write_data_hi(uint8_t data)
+{
+ // ignore if paired with upper address
+ if (!bitfield(m_address, 8))
+ return;
+
+ // 100-1FF: write to FM
+ m_fm.write(m_address, data);
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2612::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 3)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+
+ case 2: // upper address port
+ write_address_hi(data);
+ break;
+
+ case 3: // upper data port
+ write_data_hi(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ym2612::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // sum individual channels to apply DAC discontinuity on each
+ output->clear();
+ output_data temp;
+
+ // first do FM-only channels; OPN2 is 9-bit with intermediate clipping
+ int const last_fm_channel = m_dac_enable ? 5 : 6;
+ for (int chan = 0; chan < last_fm_channel; chan++)
+ {
+ m_fm.output(temp.clear(), 5, 256, 1 << chan);
+ output->data[0] += dac_discontinuity(temp.data[0]);
+ output->data[1] += dac_discontinuity(temp.data[1]);
+ }
+
+ // add in DAC
+ if (m_dac_enable)
+ {
+ // DAC enabled: start with DAC value then add the first 5 channels only
+ int32_t dacval = dac_discontinuity(int16_t(m_dac_data << 7) >> 7);
+ output->data[0] += m_fm.regs().ch_output_0(0x102) ? dacval : dac_discontinuity(0);
+ output->data[1] += m_fm.regs().ch_output_1(0x102) ? dacval : dac_discontinuity(0);
+ }
+
+ // output is technically multiplexed rather than mixed, but that requires
+ // a better sound mixer than we usually have, so just average over the six
+ // channels; also apply a 64/65 factor to account for the discontinuity
+ // adjustment above
+ output->data[0] = (output->data[0] * 128) * 64 / (6 * 65);
+ output->data[1] = (output->data[1] * 128) * 64 / (6 * 65);
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ym3438::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // first do FM-only channels; OPN2C is 9-bit with intermediate clipping
+ if (!m_dac_enable)
+ {
+ // DAC disabled: all 6 channels sum together
+ m_fm.output(output->clear(), 5, 256, fm_engine::ALL_CHANNELS);
+ }
+ else
+ {
+ // DAC enabled: start with DAC value then add the first 5 channels only
+ int32_t dacval = int16_t(m_dac_data << 7) >> 7;
+ output->data[0] = m_fm.regs().ch_output_0(0x102) ? dacval : 0;
+ output->data[1] = m_fm.regs().ch_output_1(0x102) ? dacval : 0;
+ m_fm.output(*output, 5, 256, fm_engine::ALL_CHANNELS ^ (1 << 5));
+ }
+
+ // YM3438 doesn't have the same DAC discontinuity, though its output is
+ // multiplexed like the YM2612
+ output->data[0] = (output->data[0] * 128) / 6;
+ output->data[1] = (output->data[1] * 128) / 6;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ymf276::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // first do FM-only channels; OPN2L is 14-bit with intermediate clipping
+ if (!m_dac_enable)
+ {
+ // DAC disabled: all 6 channels sum together
+ m_fm.output(output->clear(), 0, 8191, fm_engine::ALL_CHANNELS);
+ }
+ else
+ {
+ // DAC enabled: start with DAC value then add the first 5 channels only
+ int32_t dacval = int16_t(m_dac_data << 7) >> 7;
+ output->data[0] = m_fm.regs().ch_output_0(0x102) ? dacval : 0;
+ output->data[1] = m_fm.regs().ch_output_1(0x102) ? dacval : 0;
+ m_fm.output(*output, 0, 8191, fm_engine::ALL_CHANNELS ^ (1 << 5));
+ }
+
+ // YMF276 is properly mixed; it shifts down 1 bit before clamping
+ output->data[0] = clamp(output->data[0] >> 1, -32768, 32767);
+ output->data[1] = clamp(output->data[1] >> 1, -32768, 32767);
+ }
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_opn.h b/3rdparty/ymfm/src/ymfm_opn.h
new file mode 100644
index 00000000000..daed8b0bf24
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opn.h
@@ -0,0 +1,802 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_OPN_H
+#define YMFM_OPN_H
+
+#pragma once
+
+#include "ymfm.h"
+#include "ymfm_adpcm.h"
+#include "ymfm_fm.h"
+#include "ymfm_ssg.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// REGISTER CLASSES
+//*********************************************************
+
+// ======================> opn_registers_base
+
+//
+// OPN register map:
+//
+// System-wide registers:
+// 21 xxxxxxxx Test register
+// 22 ----x--- LFO enable [OPNA+ only]
+// -----xxx LFO rate [OPNA+ only]
+// 24 xxxxxxxx Timer A value (upper 8 bits)
+// 25 ------xx Timer A value (lower 2 bits)
+// 26 xxxxxxxx Timer B value
+// 27 xx------ CSM/Multi-frequency mode for channel #2
+// --x----- Reset timer B
+// ---x---- Reset timer A
+// ----x--- Enable timer B
+// -----x-- Enable timer A
+// ------x- Load timer B
+// -------x Load timer A
+// 28 x------- Key on/off operator 4
+// -x------ Key on/off operator 3
+// --x----- Key on/off operator 2
+// ---x---- Key on/off operator 1
+// ------xx Channel select
+//
+// Per-channel registers (channel in address bits 0-1)
+// Note that all these apply to address+100 as well on OPNA+
+// A0-A3 xxxxxxxx Frequency number lower 8 bits
+// A4-A7 --xxx--- Block (0-7)
+// -----xxx Frequency number upper 3 bits
+// B0-B3 --xxx--- Feedback level for operator 1 (0-7)
+// -----xxx Operator connection algorithm (0-7)
+// B4-B7 x------- Pan left [OPNA]
+// -x------ Pan right [OPNA]
+// --xx---- LFO AM shift (0-3) [OPNA+ only]
+// -----xxx LFO PM depth (0-7) [OPNA+ only]
+//
+// Per-operator registers (channel in address bits 0-1, operator in bits 2-3)
+// Note that all these apply to address+100 as well on OPNA+
+// 30-3F -xxx---- Detune value (0-7)
+// ----xxxx Multiple value (0-15)
+// 40-4F -xxxxxxx Total level (0-127)
+// 50-5F xx------ Key scale rate (0-3)
+// ---xxxxx Attack rate (0-31)
+// 60-6F x------- LFO AM enable [OPNA]
+// ---xxxxx Decay rate (0-31)
+// 70-7F ---xxxxx Sustain rate (0-31)
+// 80-8F xxxx---- Sustain level (0-15)
+// ----xxxx Release rate (0-15)
+// 90-9F ----x--- SSG-EG enable
+// -----xxx SSG-EG envelope (0-7)
+//
+// Special multi-frequency registers (channel implicitly #2; operator in address bits 0-1)
+// A8-AB xxxxxxxx Frequency number lower 8 bits
+// AC-AF --xxx--- Block (0-7)
+// -----xxx Frequency number upper 3 bits
+//
+// Internal (fake) registers:
+// B8-BB --xxxxxx Latched frequency number upper bits (from A4-A7)
+// BC-BF --xxxxxx Latched frequency number upper bits (from AC-AF)
+//
+
+template<bool IsOpnA>
+class opn_registers_base : public fm_registers_base
+{
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = IsOpnA ? 2 : 1;
+ static constexpr uint32_t CHANNELS = IsOpnA ? 6 : 3;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+ static constexpr uint32_t OPERATORS = CHANNELS * 4;
+ static constexpr uint32_t WAVEFORMS = 1;
+ static constexpr uint32_t REGISTERS = IsOpnA ? 0x200 : 0x100;
+ static constexpr uint32_t REG_MODE = 0x27;
+ static constexpr uint32_t DEFAULT_PRESCALE = 6;
+ static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
+ static constexpr bool EG_HAS_SSG = true;
+ static constexpr bool MODULATOR_DELAY = false;
+ static constexpr uint32_t CSM_TRIGGER_MASK = 1 << 2;
+ static constexpr uint8_t STATUS_TIMERA = 0x01;
+ static constexpr uint8_t STATUS_TIMERB = 0x02;
+ static constexpr uint8_t STATUS_BUSY = 0x80;
+ static constexpr uint8_t STATUS_IRQ = 0;
+
+ // constructor
+ opn_registers_base();
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ if (!IsOpnA)
+ return chnum;
+ else
+ return (chnum % 3) + 0x100 * (chnum / 3);
+ }
+
+ // map operator number to register offset
+ static constexpr uint32_t operator_offset(uint32_t opnum)
+ {
+ assert(opnum < OPERATORS);
+ if (!IsOpnA)
+ return opnum + opnum / 3;
+ else
+ return (opnum % 12) + ((opnum % 12) / 3) + 0x100 * (opnum / 12);
+ }
+
+ // return an array of operator indices for each channel
+ struct operator_mapping { uint32_t chan[CHANNELS]; };
+ void operator_map(operator_mapping &dest) const;
+
+ // read a register value
+ uint8_t read(uint16_t index) const { return m_regdata[index]; }
+
+ // handle writes to the register array
+ bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask);
+
+ // clock the noise and LFO, if present, returning LFO PM value
+ int32_t clock_noise_and_lfo();
+
+ // reset the LFO
+ void reset_lfo() { m_lfo_counter = 0; }
+
+ // return the AM offset from LFO for the given channel
+ uint32_t lfo_am_offset(uint32_t choffs) const;
+
+ // return LFO/noise states
+ uint32_t noise_state() const { return 0; }
+
+ // caching helpers
+ void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache);
+
+ // compute the phase step, given a PM value
+ uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm);
+
+ // log a key-on event
+ std::string log_keyon(uint32_t choffs, uint32_t opoffs);
+
+ // system-wide registers
+ uint32_t test() const { return byte(0x21, 0, 8); }
+ uint32_t lfo_enable() const { return IsOpnA ? byte(0x22, 3, 1) : 0; }
+ uint32_t lfo_rate() const { return IsOpnA ? byte(0x22, 0, 3) : 0; }
+ uint32_t timer_a_value() const { return word(0x24, 0, 8, 0x25, 0, 2); }
+ uint32_t timer_b_value() const { return byte(0x26, 0, 8); }
+ uint32_t csm() const { return (byte(0x27, 6, 2) == 2); }
+ uint32_t multi_freq() const { return (byte(0x27, 6, 2) != 0); }
+ uint32_t reset_timer_b() const { return byte(0x27, 5, 1); }
+ uint32_t reset_timer_a() const { return byte(0x27, 4, 1); }
+ uint32_t enable_timer_b() const { return byte(0x27, 3, 1); }
+ uint32_t enable_timer_a() const { return byte(0x27, 2, 1); }
+ uint32_t load_timer_b() const { return byte(0x27, 1, 1); }
+ uint32_t load_timer_a() const { return byte(0x27, 0, 1); }
+ uint32_t multi_block_freq(uint32_t num) const { return word(0xac, 0, 6, 0xa8, 0, 8, num); }
+
+ // per-channel registers
+ uint32_t ch_block_freq(uint32_t choffs) const { return word(0xa4, 0, 6, 0xa0, 0, 8, choffs); }
+ uint32_t ch_feedback(uint32_t choffs) const { return byte(0xb0, 3, 3, choffs); }
+ uint32_t ch_algorithm(uint32_t choffs) const { return byte(0xb0, 0, 3, choffs); }
+ uint32_t ch_output_any(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 6, 2, choffs) : 1; }
+ uint32_t ch_output_0(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 7, 1, choffs) : 1; }
+ uint32_t ch_output_1(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 6, 1, choffs) : 0; }
+ uint32_t ch_output_2(uint32_t choffs) const { return 0; }
+ uint32_t ch_output_3(uint32_t choffs) const { return 0; }
+ uint32_t ch_lfo_am_sens(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 4, 2, choffs) : 0; }
+ uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 0, 3, choffs) : 0; }
+
+ // per-operator registers
+ uint32_t op_detune(uint32_t opoffs) const { return byte(0x30, 4, 3, opoffs); }
+ uint32_t op_multiple(uint32_t opoffs) const { return byte(0x30, 0, 4, opoffs); }
+ uint32_t op_total_level(uint32_t opoffs) const { return byte(0x40, 0, 7, opoffs); }
+ uint32_t op_ksr(uint32_t opoffs) const { return byte(0x50, 6, 2, opoffs); }
+ uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x50, 0, 5, opoffs); }
+ uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0x60, 0, 5, opoffs); }
+ uint32_t op_lfo_am_enable(uint32_t opoffs) const { return IsOpnA ? byte(0x60, 7, 1, opoffs) : 0; }
+ uint32_t op_sustain_rate(uint32_t opoffs) const { return byte(0x70, 0, 5, opoffs); }
+ uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0x80, 4, 4, opoffs); }
+ uint32_t op_release_rate(uint32_t opoffs) const { return byte(0x80, 0, 4, opoffs); }
+ uint32_t op_ssg_eg_enable(uint32_t opoffs) const { return byte(0x90, 3, 1, opoffs); }
+ uint32_t op_ssg_eg_mode(uint32_t opoffs) const { return byte(0x90, 0, 3, opoffs); }
+
+protected:
+ // return a bitfield extracted from a byte
+ uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const
+ {
+ return bitfield(m_regdata[offset + extra_offset], start, count);
+ }
+
+ // return a bitfield extracted from a pair of bytes, MSBs listed first
+ uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const
+ {
+ return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset);
+ }
+
+ // internal state
+ uint32_t m_lfo_counter; // LFO counter
+ uint8_t m_lfo_am; // current LFO AM value
+ uint8_t m_regdata[REGISTERS]; // register data
+ uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms
+};
+
+using opn_registers = opn_registers_base<false>;
+using opna_registers = opn_registers_base<true>;
+
+
+
+//*********************************************************
+// OPN IMPLEMENTATION CLASSES
+//*********************************************************
+
+// A note about prescaling and sample rates.
+//
+// YM2203, YM2608, and YM2610 contain an onboard SSG (basically, a YM2149).
+// In order to properly generate sound at fully fidelity, the output sample
+// rate of the YM2149 must be input_clock / 8. This is much higher than the
+// FM needs, but in the interest of keeping things simple, the OPN generate
+// functions will output at the higher rate and just replicate the last FM
+// sample as many times as needed.
+//
+// To make things even more complicated, the YM2203 and YM2608 allow for
+// software-controlled prescaling, which affects the FM and SSG clocks in
+// different ways. There are three settings: divide by 6/4 (FM/SSG); divide
+// by 3/2; and divide by 2/1.
+//
+// Thus, the minimum output sample rate needed by each part of the chip
+// varies with the prescale as follows:
+//
+// ---- YM2203 ----- ---- YM2608 ----- ---- YM2610 -----
+// Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate
+// 6 /72 /16 /144 /32 /144 /32
+// 3 /36 /8 /72 /16
+// 2 /24 /4 /48 /8
+//
+// If we standardized on the fastest SSG rate, we'd end up with the following
+// (ratios are output_samples:source_samples):
+//
+// ---- YM2203 ----- ---- YM2608 ----- ---- YM2610 -----
+// rate = clock/4 rate = clock/8 rate = clock/16
+// Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate
+// 6 18:1 4:1 18:1 4:1 9:1 2:1
+// 3 9:1 2:1 9:1 2:1
+// 2 6:1 1:1 6:1 1:1
+//
+// However, that's a pretty big performance hit for minimal gain. Going to
+// the other extreme, we could standardize on the fastest FM rate, but then
+// at least one prescale case (3) requires the FM to be smeared across two
+// output samples:
+//
+// ---- YM2203 ----- ---- YM2608 ----- ---- YM2610 -----
+// rate = clock/24 rate = clock/48 rate = clock/144
+// Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate
+// 6 3:1 2:3 3:1 2:3 1:1 2:9
+// 3 1.5:1 1:3 1.5:1 1:3
+// 2 1:1 1:6 1:1 1:6
+//
+// Stepping back one factor of 2 addresses that issue:
+//
+// ---- YM2203 ----- ---- YM2608 ----- ---- YM2610 -----
+// rate = clock/12 rate = clock/24 rate = clock/144
+// Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate
+// 6 6:1 4:3 6:1 4:3 1:1 2:9
+// 3 3:1 2:3 3:1 2:3
+// 2 2:1 1:3 2:1 1:3
+//
+// This gives us three levels of output fidelity:
+// OPN_FIDELITY_MAX -- highest sample rate, using fastest SSG rate
+// OPN_FIDELITY_MIN -- lowest sample rate, using fastest FM rate
+// OPN_FIDELITY_MED -- medium sample rate such that FM is never smeared
+//
+// At the maximum clocks for YM2203/YM2608 (4Mhz/8MHz), these rates will
+// end up as:
+// OPN_FIDELITY_MAX = 1000kHz
+// OPN_FIDELITY_MIN = 166kHz
+// OPN_FIEDLITY_MED = 333kHz
+
+
+// ======================> opn_fidelity
+
+enum opn_fidelity : uint8_t
+{
+ OPN_FIDELITY_MAX,
+ OPN_FIDELITY_MIN,
+ OPN_FIDELITY_MED,
+
+ OPN_FIDELITY_DEFAULT = OPN_FIDELITY_MAX
+};
+
+
+// ======================> ssg_resampler
+
+template<typename OutputType, int FirstOutput, bool MixTo1>
+class ssg_resampler
+{
+private:
+ // helper to add the last computed value to the sums, applying the given scale
+ void add_last(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale = 1);
+
+ // helper to clock a new value and then add it to the sums, applying the given scale
+ void clock_and_add(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale = 1);
+
+ // helper to write the sums to the appropriate outputs, applying the given
+ // divisor to the final result
+ void write_to_output(OutputType *output, int32_t sum0, int32_t sum1, int32_t sum2, int32_t divisor = 1);
+
+public:
+ // constructor
+ ssg_resampler(ssg_engine &ssg);
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // get the current sample index
+ uint32_t sampindex() const { return m_sampindex; }
+
+ // configure the ratio
+ void configure(uint8_t outsamples, uint8_t srcsamples);
+
+ // resample
+ void resample(OutputType *output, uint32_t numsamples)
+ {
+ (this->*m_resampler)(output, numsamples);
+ }
+
+private:
+ // resample SSG output to the target at a rate of 1 SSG sample
+ // to every n output samples
+ template<int Multiplier>
+ void resample_n_1(OutputType *output, uint32_t numsamples);
+
+ // resample SSG output to the target at a rate of n SSG samples
+ // to every 1 output sample
+ template<int Divisor>
+ void resample_1_n(OutputType *output, uint32_t numsamples);
+
+ // resample SSG output to the target at a rate of 9 SSG samples
+ // to every 2 output samples
+ void resample_2_9(OutputType *output, uint32_t numsamples);
+
+ // resample SSG output to the target at a rate of 3 SSG samples
+ // to every 1 output sample
+ void resample_1_3(OutputType *output, uint32_t numsamples);
+
+ // resample SSG output to the target at a rate of 3 SSG samples
+ // to every 2 output samples
+ void resample_2_3(OutputType *output, uint32_t numsamples);
+
+ // resample SSG output to the target at a rate of 3 SSG samples
+ // to every 4 output samples
+ void resample_4_3(OutputType *output, uint32_t numsamples);
+
+ // no-op resampler
+ void resample_nop(OutputType *output, uint32_t numsamples);
+
+ // define a pointer type
+ using resample_func = void (ssg_resampler::*)(OutputType *output, uint32_t numsamples);
+
+ // internal state
+ ssg_engine &m_ssg;
+ uint32_t m_sampindex;
+ resample_func m_resampler;
+ ssg_engine::output_data m_last;
+};
+
+
+// ======================> ym2203
+
+class ym2203
+{
+public:
+ using fm_engine = fm_engine_base<opn_registers>;
+ static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS;
+ static constexpr uint32_t SSG_OUTPUTS = ssg_engine::OUTPUTS;
+ static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS;
+ using output_data = ymfm_output<OUTPUTS>;
+
+ // constructor
+ ym2203(ymfm_interface &intf);
+
+ // configuration
+ void ssg_override(ssg_override &intf) { m_ssg.override(intf); }
+ void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(m_fm.clock_prescale()); }
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const
+ {
+ switch (m_fidelity)
+ {
+ case OPN_FIDELITY_MIN: return input_clock / 24;
+ case OPN_FIDELITY_MED: return input_clock / 12;
+ default:
+ case OPN_FIDELITY_MAX: return input_clock / 4;
+ }
+ }
+ uint32_t ssg_effective_clock(uint32_t input_clock) const { uint32_t scale = m_fm.clock_prescale() * 2 / 3; return input_clock * 2 / scale; }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read_data();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal helpers
+ void update_prescale(uint8_t prescale);
+ void clock_fm();
+
+ // internal state
+ opn_fidelity m_fidelity; // configured fidelity
+ uint8_t m_address; // address register
+ uint8_t m_fm_samples_per_output; // how many samples to repeat
+ fm_engine::output_data m_last_fm; // last FM output
+ fm_engine m_fm; // core FM engine
+ ssg_engine m_ssg; // SSG engine
+ ssg_resampler<output_data, 1, false> m_ssg_resampler; // SSG resampler helper
+};
+
+
+
+//*********************************************************
+// OPNA IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ym2608
+
+class ym2608
+{
+ static constexpr uint8_t STATUS_ADPCM_B_EOS = 0x04;
+ static constexpr uint8_t STATUS_ADPCM_B_BRDY = 0x08;
+ static constexpr uint8_t STATUS_ADPCM_B_ZERO = 0x10;
+ static constexpr uint8_t STATUS_ADPCM_B_PLAYING = 0x20;
+
+public:
+ using fm_engine = fm_engine_base<opna_registers>;
+ static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS;
+ static constexpr uint32_t SSG_OUTPUTS = 1;
+ static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS;
+ using output_data = ymfm_output<OUTPUTS>;
+
+ // constructor
+ ym2608(ymfm_interface &intf);
+
+ // configuration
+ void ssg_override(ssg_override &intf) { m_ssg.override(intf); }
+ void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(m_fm.clock_prescale()); }
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const
+ {
+ switch (m_fidelity)
+ {
+ case OPN_FIDELITY_MIN: return input_clock / 48;
+ case OPN_FIDELITY_MED: return input_clock / 24;
+ default:
+ case OPN_FIDELITY_MAX: return input_clock / 8;
+ }
+ }
+ uint32_t ssg_effective_clock(uint32_t input_clock) const { uint32_t scale = m_fm.clock_prescale() * 2 / 3; return input_clock / scale; }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read_data();
+ uint8_t read_status_hi();
+ uint8_t read_data_hi();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write_address_hi(uint8_t data);
+ void write_data_hi(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal helpers
+ void update_prescale(uint8_t prescale);
+ void clock_fm_and_adpcm();
+
+ // internal state
+ opn_fidelity m_fidelity; // configured fidelity
+ uint16_t m_address; // address register
+ uint8_t m_fm_samples_per_output; // how many samples to repeat
+ uint8_t m_irq_enable; // IRQ enable register
+ uint8_t m_flag_control; // flag control register
+ fm_engine::output_data m_last_fm; // last FM output
+ fm_engine m_fm; // core FM engine
+ ssg_engine m_ssg; // SSG engine
+ ssg_resampler<output_data, 2, true> m_ssg_resampler; // SSG resampler helper
+ adpcm_a_engine m_adpcm_a; // ADPCM-A engine
+ adpcm_b_engine m_adpcm_b; // ADPCM-B engine
+};
+
+
+// ======================> ymf288
+
+class ymf288
+{
+public:
+ using fm_engine = fm_engine_base<opna_registers>;
+ static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS;
+ static constexpr uint32_t SSG_OUTPUTS = 1;
+ static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS;
+ using output_data = ymfm_output<OUTPUTS>;
+
+ // constructor
+ ymf288(ymfm_interface &intf);
+
+ // configuration
+ void ssg_override(ssg_override &intf) { m_ssg.override(intf); }
+ void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(); }
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const
+ {
+ switch (m_fidelity)
+ {
+ case OPN_FIDELITY_MIN: return input_clock / 144;
+ case OPN_FIDELITY_MED: return input_clock / 144;
+ default:
+ case OPN_FIDELITY_MAX: return input_clock / 16;
+ }
+ }
+ uint32_t ssg_effective_clock(uint32_t input_clock) const { return input_clock / 4; }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read_data();
+ uint8_t read_status_hi();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write_address_hi(uint8_t data);
+ void write_data_hi(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal helpers
+ bool ymf288_mode() { return ((m_fm.regs().read(0x20) & 0x02) != 0); }
+ void update_prescale();
+ void clock_fm_and_adpcm();
+
+ // internal state
+ opn_fidelity m_fidelity; // configured fidelity
+ uint16_t m_address; // address register
+ uint8_t m_fm_samples_per_output; // how many samples to repeat
+ uint8_t m_irq_enable; // IRQ enable register
+ uint8_t m_flag_control; // flag control register
+ fm_engine::output_data m_last_fm; // last FM output
+ fm_engine m_fm; // core FM engine
+ ssg_engine m_ssg; // SSG engine
+ ssg_resampler<output_data, 2, true> m_ssg_resampler; // SSG resampler helper
+ adpcm_a_engine m_adpcm_a; // ADPCM-A engine
+};
+
+
+// ======================> ym2610/ym2610b
+
+class ym2610
+{
+ static constexpr uint8_t EOS_FLAGS_MASK = 0xbf;
+
+public:
+ using fm_engine = fm_engine_base<opna_registers>;
+ static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS;
+ static constexpr uint32_t SSG_OUTPUTS = 1;
+ static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS;
+ using output_data = ymfm_output<OUTPUTS>;
+
+ // constructor
+ ym2610(ymfm_interface &intf, uint8_t channel_mask = 0x36);
+
+ // configuration
+ void ssg_override(ssg_override &intf) { m_ssg.override(intf); }
+ void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(); }
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const
+ {
+ switch (m_fidelity)
+ {
+ case OPN_FIDELITY_MIN: return input_clock / 144;
+ case OPN_FIDELITY_MED: return input_clock / 144;
+ default:
+ case OPN_FIDELITY_MAX: return input_clock / 16;
+ }
+ }
+ uint32_t ssg_effective_clock(uint32_t input_clock) const { return input_clock / 4; }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read_data();
+ uint8_t read_status_hi();
+ uint8_t read_data_hi();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write_address_hi(uint8_t data);
+ void write_data_hi(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal helpers
+ void update_prescale();
+ void clock_fm_and_adpcm();
+
+ // internal state
+ opn_fidelity m_fidelity; // configured fidelity
+ uint16_t m_address; // address register
+ uint8_t const m_fm_mask; // FM channel mask
+ uint8_t m_fm_samples_per_output; // how many samples to repeat
+ uint8_t m_eos_status; // end-of-sample signals
+ uint8_t m_flag_mask; // flag mask control
+ fm_engine::output_data m_last_fm; // last FM output
+ fm_engine m_fm; // core FM engine
+ ssg_engine m_ssg; // core FM engine
+ ssg_resampler<output_data, 2, true> m_ssg_resampler; // SSG resampler helper
+ adpcm_a_engine m_adpcm_a; // ADPCM-A engine
+ adpcm_b_engine m_adpcm_b; // ADPCM-B engine
+};
+
+class ym2610b : public ym2610
+{
+public:
+ // constructor
+ ym2610b(ymfm_interface &intf) : ym2610(intf, 0x3f) { }
+};
+
+
+// ======================> ym2612
+
+class ym2612
+{
+public:
+ using fm_engine = fm_engine_base<opna_registers>;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+ using output_data = fm_engine::output_data;
+
+ // constructor
+ ym2612(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write_address_hi(uint8_t data);
+ void write_data_hi(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // simulate the DAC discontinuity
+ constexpr int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 3) : (value + 4); }
+
+ // internal state
+ uint16_t m_address; // address register
+ uint16_t m_dac_data; // 9-bit DAC data
+ uint8_t m_dac_enable; // DAC enabled?
+ fm_engine m_fm; // core FM engine
+};
+
+
+// ======================> ym3438
+
+class ym3438 : public ym2612
+{
+public:
+ ym3438(ymfm_interface &intf) : ym2612(intf) { }
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+};
+
+
+// ======================> ymf276
+
+class ymf276 : public ym2612
+{
+public:
+ ymf276(ymfm_interface &intf) : ym2612(intf) { }
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+};
+
+}
+
+
+#endif // YMFM_OPN_H
diff --git a/3rdparty/ymfm/src/ymfm_opq.cpp b/3rdparty/ymfm/src/ymfm_opq.cpp
new file mode 100644
index 00000000000..78ae1616409
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opq.cpp
@@ -0,0 +1,480 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_opq.h"
+#include "ymfm_fm.ipp"
+
+#define TEMPORARY_DEBUG_PRINTS (0)
+
+//
+// OPQ (aka YM3806/YM3533)
+//
+// This chip is not officially documented as far as I know. What I have
+// comes from Jari Kangas' work on reverse engineering the PSR70:
+//
+// https://github.com/JKN0/PSR70-reverse
+//
+// OPQ appears be bsaically a mixture of OPM and OPN.
+//
+
+namespace ymfm
+{
+
+//*********************************************************
+// OPQ SPECIFICS
+//*********************************************************
+
+//-------------------------------------------------
+// opq_registers - constructor
+//-------------------------------------------------
+
+opq_registers::opq_registers() :
+ m_lfo_counter(0),
+ m_lfo_am(0)
+{
+ // create the waveforms
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15);
+
+ uint16_t zeroval = m_waveform[0][0];
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[1][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index];
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+void opq_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+
+ // enable output on both channels by default
+ m_regdata[0x10] = m_regdata[0x11] = m_regdata[0x12] = m_regdata[0x13] = 0xc0;
+ m_regdata[0x14] = m_regdata[0x15] = m_regdata[0x16] = m_regdata[0x17] = 0xc0;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void opq_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_lfo_counter);
+ state.save_restore(m_lfo_am);
+ state.save_restore(m_regdata);
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPM this is fixed
+//-------------------------------------------------
+
+void opq_registers::operator_map(operator_mapping &dest) const
+{
+ // seems like the operators are not swizzled like they are on OPM/OPN?
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 8, 16, 24 ), // Channel 0 operators
+ operator_list( 1, 9, 17, 25 ), // Channel 1 operators
+ operator_list( 2, 10, 18, 26 ), // Channel 2 operators
+ operator_list( 3, 11, 19, 27 ), // Channel 3 operators
+ operator_list( 4, 12, 20, 28 ), // Channel 4 operators
+ operator_list( 5, 13, 21, 29 ), // Channel 5 operators
+ operator_list( 6, 14, 22, 30 ), // Channel 6 operators
+ operator_list( 7, 15, 23, 31 ), // Channel 7 operators
+ } };
+ dest = s_fixed_map;
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array
+//-------------------------------------------------
+
+bool opq_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
+{
+ assert(index < REGISTERS);
+
+ // detune/multiple share a register based on the MSB of what is written
+ // remap the multiple values to 100-11F
+ if ((index & 0xe0) == 0x40 && bitfield(data, 7) != 0)
+ index += 0xc0;
+
+ m_regdata[index] = data;
+
+ // handle writes to the key on index
+ if (index == 0x05)
+ {
+ channel = bitfield(data, 0, 3);
+ opmask = bitfield(data, 3, 4);
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+int32_t opq_registers::clock_noise_and_lfo()
+{
+ // OPQ LFO is not well-understood, but the enable and rate values
+ // look a lot like OPN, so we'll crib from there as a starting point
+
+ // if LFO not enabled (not present on OPN), quick exit with 0s
+ if (!lfo_enable())
+ {
+ m_lfo_counter = 0;
+ m_lfo_am = 0;
+ return 0;
+ }
+
+ // this table is based on converting the frequencies in the applications
+ // manual to clock dividers, based on the assumption of a 7-bit LFO value
+ static uint8_t const lfo_max_count[8] = { 109, 78, 72, 68, 63, 45, 9, 6 };
+ uint32_t subcount = uint8_t(m_lfo_counter++);
+
+ // when we cross the divider count, add enough to zero it and cause an
+ // increment at bit 8; the 7-bit value lives from bits 8-14
+ if (subcount >= lfo_max_count[lfo_rate()])
+ m_lfo_counter += 0x101 - subcount;
+
+ // AM value is 7 bits, staring at bit 8; grab the low 6 directly
+ m_lfo_am = bitfield(m_lfo_counter, 8, 6);
+
+ // first half of the AM period (bit 6 == 0) is inverted
+ if (bitfield(m_lfo_counter, 8+6) == 0)
+ m_lfo_am ^= 0x3f;
+
+ // PM value is 5 bits, starting at bit 10; grab the low 3 directly
+ int32_t pm = bitfield(m_lfo_counter, 10, 3);
+
+ // PM is reflected based on bit 3
+ if (bitfield(m_lfo_counter, 10+3))
+ pm ^= 7;
+
+ // PM is negated based on bit 4
+ return bitfield(m_lfo_counter, 10+4) ? -pm : pm;
+}
+
+
+//-------------------------------------------------
+// lfo_am_offset - return the AM offset from LFO
+// for the given channel
+//-------------------------------------------------
+
+uint32_t opq_registers::lfo_am_offset(uint32_t choffs) const
+{
+ // OPM maps AM quite differently from OPN
+
+ // shift value for AM sensitivity is [*, 0, 1, 2],
+ // mapping to values of [0, 23.9, 47.8, and 95.6dB]
+ uint32_t am_sensitivity = ch_lfo_am_sens(choffs);
+ if (am_sensitivity == 0)
+ return 0;
+
+ // QUESTION: see OPN note below for the dB range mapping; it applies
+ // here as well
+
+ // raw LFO AM value on OPM is 0-FF, which is already a factor of 2
+ // larger than the OPN below, putting our staring point at 2x theirs;
+ // this works out since our minimum is 2x their maximum
+ return m_lfo_am << (am_sensitivity - 1);
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data
+//-------------------------------------------------
+
+void opq_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
+{
+ // set up the easy stuff
+ cache.waveform = &m_waveform[op_waveform(opoffs)][0];
+
+ // get frequency from the appropriate registers
+ uint32_t block_freq = cache.block_freq = (opoffs & 8) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs);
+
+ // compute the keycode: block_freq is:
+ //
+ // BBBFFFFFFFFFFFF
+ // ^^^^???
+ //
+ // keycode is not understood, so just guessing it is like OPN:
+ // the 5-bit keycode uses the top 4 bits plus a magic formula
+ // for the final bit
+ uint32_t keycode = bitfield(block_freq, 11, 4) << 1;
+
+ // lowest bit is determined by a mix of next lower FNUM bits
+ // according to this equation from the YM2608 manual:
+ //
+ // (F11 & (F10 | F9 | F8)) | (!F11 & F10 & F9 & F8)
+ //
+ // for speed, we just look it up in a 16-bit constant
+ keycode |= bitfield(0xfe80, bitfield(block_freq, 8, 4));
+
+ // detune adjustment: the detune values supported by the OPQ are
+ // a much larger range (6 bits vs 3 bits) compared to any other
+ // known FM chip; based on experiments, it seems that the extra
+ // bits provide a bigger detune range rather than finer control,
+ // so until we get true measurements just assemble a net detune
+ // value by summing smaller detunes
+ int32_t detune = int32_t(op_detune(opoffs)) - 0x20;
+ int32_t abs_detune = std::abs(detune);
+ int32_t adjust = (abs_detune / 3) * detune_adjustment(3, keycode) + detune_adjustment(abs_detune % 3, keycode);
+ cache.detune = (detune >= 0) ? adjust : -adjust;
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ static const uint8_t s_multiple_map[16] = { 1,2,4,6,8,10,12,14,16,18,20,24,30,32,34,36 };
+ cache.multiple = s_multiple_map[op_multiple(opoffs)];
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on
+ // block_freq, detune, and multiple, so compute it after we've done those
+ if (lfo_enable() == 0 || ch_lfo_pm_sens(choffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8
+ cache.total_level = op_total_level(opoffs) << 3;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // determine KSR adjustment for enevlope rates
+ uint32_t ksrval = keycode >> (op_ksr(opoffs) ^ 3);
+ cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval);
+ cache.eg_rate[EG_REVERB] = (ch_reverb(choffs) != 0) ? 5*4 : cache.eg_rate[EG_RELEASE];
+ cache.eg_shift = 0;
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+uint32_t opq_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm)
+{
+ // OPN phase calculation has only a single detune parameter
+ // and uses FNUMs instead of keycodes
+
+ // extract frequency number (low 12 bits of block_freq)
+ uint32_t fnum = bitfield(cache.block_freq, 0, 12);
+
+ // if there's a non-zero PM sensitivity, compute the adjustment
+ uint32_t pm_sensitivity = ch_lfo_pm_sens(choffs);
+ if (pm_sensitivity != 0)
+ {
+ // apply the phase adjustment based on the upper 7 bits
+ // of FNUM and the PM depth parameters
+ fnum += opn_lfo_pm_phase_adjustment(bitfield(cache.block_freq, 5, 7), pm_sensitivity, lfo_raw_pm);
+
+ // keep fnum to 12 bits
+ fnum &= 0xfff;
+ }
+
+ // apply block shift to compute phase step
+ uint32_t block = bitfield(cache.block_freq, 12, 3);
+ uint32_t phase_step = (fnum << block) >> 2;
+
+ // apply detune based on the keycode
+ phase_step += cache.detune;
+
+ // clamp to 17 bits in case detune overflows
+ // QUESTION: is this specific to the YM2612/3438?
+ phase_step &= 0x1ffff;
+
+ // apply frequency multiplier (which is cached as an x.1 value)
+ return (phase_step * cache.multiple) >> 1;
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
+{
+ uint32_t chnum = choffs;
+ uint32_t opnum = opoffs;
+
+ char buffer[256];
+ int end = 0;
+
+ 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,
+ ch_feedback(choffs),
+ ch_algorithm(choffs),
+ op_multiple(opoffs),
+ op_total_level(opoffs),
+ op_ksr(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_sustain_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs),
+ ch_output_0(choffs) ? 'L' : '-',
+ ch_output_1(choffs) ? 'R' : '-');
+
+ bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0);
+ if (am)
+ 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(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs));
+ if (am || pm)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate());
+ if (ch_reverb(choffs))
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " reverb");
+
+ return buffer;
+}
+
+
+
+//*********************************************************
+// YM3806
+//*********************************************************
+
+//-------------------------------------------------
+// ym3806 - constructor
+//-------------------------------------------------
+
+ym3806::ym3806(ymfm_interface &intf) :
+ m_fm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym3806::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym3806::save_restore(ymfm_saved_state &state)
+{
+ m_fm.save_restore(state);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym3806::read_status()
+{
+ uint8_t result = m_fm.status();
+ if (m_fm.intf().ymfm_is_busy())
+ result |= fm_engine::STATUS_BUSY;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym3806::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset)
+ {
+ case 0: // status port
+ result = read_status();
+ break;
+
+ default: // unknown
+ debug::log_unexpected_read_write("Unexpected read from YM3806 offset %02X\n", offset);
+ break;
+ }
+if (TEMPORARY_DEBUG_PRINTS && offset != 0) printf("Read %02X = %02X\n", offset, result);
+ return result;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym3806::write(uint32_t offset, uint8_t data)
+{
+if (TEMPORARY_DEBUG_PRINTS && (offset != 3 || data != 0x71)) printf("Write %02X = %02X\n", offset, data);
+ // write the FM register
+ m_fm.write(offset, data);
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ym3806::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; YM3806 is full 14-bit with no intermediate clipping
+ m_fm.output(output->clear(), 0, 32767, fm_engine::ALL_CHANNELS);
+
+ // YM3608 appears to go through a YM3012 DAC, which means we want to apply
+ // the FP truncation logic to the outputs
+ output->roundtrip_fp();
+ }
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_opq.h b/3rdparty/ymfm/src/ymfm_opq.h
new file mode 100644
index 00000000000..f530ac070f1
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opq.h
@@ -0,0 +1,293 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_OPQ_H
+#define YMFM_OPQ_H
+
+#pragma once
+
+#include "ymfm.h"
+#include "ymfm_fm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// REGISTER CLASSES
+//*********************************************************
+
+// ======================> opq_registers
+
+//
+// OPQ register map:
+//
+// System-wide registers:
+// 03 xxxxxxxx Timer control (unknown; 0x71 causes interrupts at ~10ms)
+// 04 ----x--- LFO disable
+// -----xxx LFO frequency (0=~4Hz, 6=~10Hz, 7=~47Hz)
+// 05 -x------ Key on/off operator 4
+// --x----- Key on/off operator 3
+// ---x---- Key on/off operator 2
+// ----x--- Key on/off operator 1
+// -----xxx Channel select
+//
+// Per-channel registers (channel in address bits 0-2)
+// 10-17 x------- Pan right
+// -x------ Pan left
+// --xxx--- Feedback level for operator 1 (0-7)
+// -----xxx Operator connection algorithm (0-7)
+// 18-1F x------- Reverb
+// -xxx---- PM sensitivity
+// ------xx AM shift
+// 20-27 -xxx---- Block (0-7), Operator 2 & 4
+// ----xxxx Frequency number upper 4 bits, Operator 2 & 4
+// 28-2F -xxx---- Block (0-7), Operator 1 & 3
+// ----xxxx Frequency number upper 4 bits, Operator 1 & 3
+// 30-37 xxxxxxxx Frequency number lower 8 bits, Operator 2 & 4
+// 38-3F xxxxxxxx Frequency number lower 8 bits, Operator 1 & 3
+//
+// Per-operator registers (channel in address bits 0-2, operator in bits 3-4)
+// 40-5F 0-xxxxxx Detune value (0-63)
+// 1---xxxx Multiple value (0-15)
+// 60-7F -xxxxxxx Total level (0-127)
+// 80-9F xx------ Key scale rate (0-3)
+// ---xxxxx Attack rate (0-31)
+// A0-BF x------- LFO AM enable, retrigger disable
+// x------ Waveform select
+// ---xxxxx Decay rate (0-31)
+// C0-DF ---xxxxx Sustain rate (0-31)
+// E0-FF xxxx---- Sustain level (0-15)
+// ----xxxx Release rate (0-15)
+//
+// Diffs from OPM:
+// - 2 frequencies/channel
+// - retrigger disable
+// - 2 waveforms
+// - uses FNUM
+// - reverb behavior
+// - larger detune range
+//
+// Questions:
+// - timer information is pretty light
+// - how does echo work?
+// -
+
+class opq_registers : public fm_registers_base
+{
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = 2;
+ static constexpr uint32_t CHANNELS = 8;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+ static constexpr uint32_t OPERATORS = CHANNELS * 4;
+ static constexpr uint32_t WAVEFORMS = 2;
+ static constexpr uint32_t REGISTERS = 0x120;
+ static constexpr uint32_t REG_MODE = 0x03;
+ static constexpr uint32_t DEFAULT_PRESCALE = 2;
+ static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
+ static constexpr bool EG_HAS_REVERB = true;
+ static constexpr bool MODULATOR_DELAY = false;
+ static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
+ static constexpr uint8_t STATUS_TIMERA = 0;
+ static constexpr uint8_t STATUS_TIMERB = 0x04;
+ static constexpr uint8_t STATUS_BUSY = 0x80;
+ static constexpr uint8_t STATUS_IRQ = 0;
+
+ // constructor
+ opq_registers();
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ return chnum;
+ }
+
+ // map operator number to register offset
+ static constexpr uint32_t operator_offset(uint32_t opnum)
+ {
+ assert(opnum < OPERATORS);
+ return opnum;
+ }
+
+ // return an array of operator indices for each channel
+ struct operator_mapping { uint32_t chan[CHANNELS]; };
+ void operator_map(operator_mapping &dest) const;
+
+ // handle writes to the register array
+ bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask);
+
+ // clock the noise and LFO, if present, returning LFO PM value
+ int32_t clock_noise_and_lfo();
+
+ // reset the LFO
+ void reset_lfo() { m_lfo_counter = 0; }
+
+ // return the AM offset from LFO for the given channel
+ uint32_t lfo_am_offset(uint32_t choffs) const;
+
+ // return the current noise state, gated by the noise clock
+ uint32_t noise_state() const { return 0; }
+
+ // caching helpers
+ void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache);
+
+ // compute the phase step, given a PM value
+ uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm);
+
+ // log a key-on event
+ std::string log_keyon(uint32_t choffs, uint32_t opoffs);
+
+ // system-wide registers
+ uint32_t timer_a_value() const { return 0; }
+ uint32_t timer_b_value() const { return byte(0x03, 2, 6) | 0xc0; } // ???
+ uint32_t csm() const { return 0; }
+ uint32_t reset_timer_b() const { return byte(0x03, 0, 1); } // ???
+ uint32_t reset_timer_a() const { return 0; }
+ uint32_t enable_timer_b() const { return byte(0x03, 0, 1); } // ???
+ uint32_t enable_timer_a() const { return 0; }
+ uint32_t load_timer_b() const { return byte(0x03, 0, 1); } // ???
+ uint32_t load_timer_a() const { return 0; }
+ uint32_t lfo_enable() const { return byte(0x04, 3, 1) ^ 1; }
+ uint32_t lfo_rate() const { return byte(0x04, 0, 3); }
+
+ // per-channel registers
+ uint32_t ch_output_any(uint32_t choffs) const { return byte(0x10, 6, 2, choffs); }
+ uint32_t ch_output_0(uint32_t choffs) const { return byte(0x10, 6, 1, choffs); }
+ uint32_t ch_output_1(uint32_t choffs) const { return byte(0x10, 7, 1, choffs); }
+ uint32_t ch_output_2(uint32_t choffs) const { return 0; }
+ uint32_t ch_output_3(uint32_t choffs) const { return 0; }
+ uint32_t ch_feedback(uint32_t choffs) const { return byte(0x10, 3, 3, choffs); }
+ uint32_t ch_algorithm(uint32_t choffs) const { return byte(0x10, 0, 3, choffs); }
+ uint32_t ch_reverb(uint32_t choffs) const { return byte(0x18, 7, 1, choffs); }
+ uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return byte(0x18, 4, 3, choffs); }
+ uint32_t ch_lfo_am_sens(uint32_t choffs) const { return byte(0x18, 0, 2, choffs); }
+ uint32_t ch_block_freq_24(uint32_t choffs) const { return word(0x20, 0, 7, 0x30, 0, 8, choffs); }
+ uint32_t ch_block_freq_13(uint32_t choffs) const { return word(0x28, 0, 7, 0x38, 0, 8, choffs); }
+
+ // per-operator registers
+ uint32_t op_detune(uint32_t opoffs) const { return byte(0x40, 0, 6, opoffs); }
+ uint32_t op_multiple(uint32_t opoffs) const { return byte(0x100, 0, 4, opoffs); }
+ uint32_t op_total_level(uint32_t opoffs) const { return byte(0x60, 0, 7, opoffs); }
+ uint32_t op_ksr(uint32_t opoffs) const { return byte(0x80, 6, 2, opoffs); }
+ uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x80, 0, 5, opoffs); }
+ uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0xa0, 7, 1, opoffs); }
+ uint32_t op_waveform(uint32_t opoffs) const { return byte(0xa0, 6, 1, opoffs); }
+ uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0xa0, 0, 5, opoffs); }
+ uint32_t op_sustain_rate(uint32_t opoffs) const { return byte(0xc0, 0, 5, opoffs); }
+ uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0xe0, 4, 4, opoffs); }
+ uint32_t op_release_rate(uint32_t opoffs) const { return byte(0xe0, 0, 4, opoffs); }
+
+protected:
+ // return a bitfield extracted from a byte
+ uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const
+ {
+ return bitfield(m_regdata[offset + extra_offset], start, count);
+ }
+
+ // return a bitfield extracted from a pair of bytes, MSBs listed first
+ uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const
+ {
+ return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset);
+ }
+
+ // internal state
+ uint32_t m_lfo_counter; // LFO counter
+ uint8_t m_lfo_am; // current LFO AM value
+ uint8_t m_regdata[REGISTERS]; // register data
+ uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms
+};
+
+
+
+//*********************************************************
+// IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ym3806
+
+class ym3806
+{
+public:
+ using fm_engine = fm_engine_base<opq_registers>;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+ using output_data = fm_engine::output_data;
+
+ // constructor
+ ym3806(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data) { /* not supported; only direct writes */ }
+ void write_data(uint8_t data) { /* not supported; only direct writes */ }
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ fm_engine m_fm; // core FM engine
+};
+
+
+// ======================> ym3533
+
+class ym3533 : public ym3806
+{
+public:
+ // constructor
+ ym3533(ymfm_interface &intf) :
+ ym3806(intf) { }
+};
+
+}
+
+
+#endif // YMFM_OPQ_H
diff --git a/3rdparty/ymfm/src/ymfm_opx.h b/3rdparty/ymfm/src/ymfm_opx.h
new file mode 100644
index 00000000000..f8ee1c34f18
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opx.h
@@ -0,0 +1,290 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_OPX_H
+#define YMFM_OPX_H
+
+#pragma once
+
+#include "ymfm.h"
+#include "ymfm_fm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// REGISTER CLASSES
+//*********************************************************
+
+// ======================> opx_registers
+
+//
+// OPX register map:
+//
+// System-wide registers:
+//
+// Per-channel registers (channel in address bits 0-2)
+//
+// Per-operator registers (4 banks):
+// 00-0F x------- Enable
+// -xxxx--- EXT out
+// -------x Key on
+// 10-1F xxxxxxxx LFO frequency
+// 20-2F xx------ AM sensitivity (0-3)
+// --xxx--- PM sensitivity (0-7)
+// ------xx LFO waveform (0=disable, 1=saw, 2=
+// 30-3F -xxx---- Detune (0-7)
+// ----xxxx Multiple (0-15)
+// 40-4F -xxxxxxx Total level (0-127)
+// 50-5F xxx----- Key scale (0-7)
+// ---xxxxx Attack rate (0-31)
+// 60-6F ---xxxxx Decay rate (0-31)
+// 70-7F ---xxxxx Sustain rate (0-31)
+// 80-8F xxxx---- Sustain level (0-15)
+// ----xxxx Release rate (0-15)
+// 90-9F xxxxxxxx Frequency number (low 8 bits)
+// A0-AF xxxx---- Block (0-15)
+// ----xxxx Frequency number (high 4 bits)
+// B0-BF x------- Acc on
+// -xxx---- Feedback level (0-7)
+// -----xxx Waveform (0-7, 7=PCM)
+// C0-CF ----xxxx Algorithm (0-15)
+// D0-DF xxxx---- CH0 level (0-15)
+// ----xxxx CH1 level (0-15)
+// E0-EF xxxx---- CH2 level (0-15)
+// ----xxxx CH3 level (0-15)
+//
+
+class opx_registers : public fm_registers_base
+{
+ // LFO waveforms are 256 entries long
+ static constexpr uint32_t LFO_WAVEFORM_LENGTH = 256;
+
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = 8;
+ static constexpr uint32_t CHANNELS = 24;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+ static constexpr uint32_t OPERATORS = CHANNELS * 2;
+ static constexpr uint32_t WAVEFORMS = 8;
+ static constexpr uint32_t REGISTERS = 0x800;
+ static constexpr uint32_t DEFAULT_PRESCALE = 8;
+ static constexpr uint32_t EG_CLOCK_DIVIDER = 2;
+ static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
+ static constexpr uint32_t REG_MODE = 0x14;
+ static constexpr uint8_t STATUS_TIMERA = 0x01;
+ static constexpr uint8_t STATUS_TIMERB = 0x02;
+ static constexpr uint8_t STATUS_BUSY = 0x80;
+ static constexpr uint8_t STATUS_IRQ = 0;
+
+ // constructor
+ opx_registers();
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ return chnum;
+ }
+
+ // map operator number to register offset
+ static constexpr uint32_t operator_offset(uint32_t opnum)
+ {
+ assert(opnum < OPERATORS);
+ return opnum;
+ }
+
+ // return an array of operator indices for each channel
+ struct operator_mapping { uint32_t chan[CHANNELS]; };
+ void operator_map(operator_mapping &dest) const;
+
+ // handle writes to the register array
+ bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask);
+
+ // clock the noise and LFO, if present, returning LFO PM value
+ int32_t clock_noise_and_lfo();
+
+ // return the AM offset from LFO for the given channel
+ uint32_t lfo_am_offset(uint32_t choffs) const;
+
+ // return the current noise state, gated by the noise clock
+ uint32_t noise_state() const { return m_noise_state; }
+
+ // caching helpers
+ void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache);
+
+ // compute the phase step, given a PM value
+ uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm);
+
+ // log a key-on event
+ std::string log_keyon(uint32_t choffs, uint32_t opoffs);
+
+ // system-wide registers
+ uint32_t noise_frequency() const { return byte(0x0f, 0, 5); }
+ uint32_t noise_enable() const { return byte(0x0f, 7, 1); }
+ uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); }
+ uint32_t timer_b_value() const { return byte(0x12, 0, 8); }
+ uint32_t csm() const { return byte(0x14, 7, 1); }
+ uint32_t reset_timer_b() const { return byte(0x14, 5, 1); }
+ uint32_t reset_timer_a() const { return byte(0x14, 4, 1); }
+ uint32_t enable_timer_b() const { return byte(0x14, 3, 1); }
+ uint32_t enable_timer_a() const { return byte(0x14, 2, 1); }
+ uint32_t load_timer_b() const { return byte(0x14, 1, 1); }
+ uint32_t load_timer_a() const { return byte(0x14, 0, 1); }
+ uint32_t lfo2_pm_depth() const { return byte(0x148, 0, 7); } // fake
+ uint32_t lfo2_rate() const { return byte(0x16, 0, 8); }
+ uint32_t lfo2_am_depth() const { return byte(0x17, 0, 7); }
+ uint32_t lfo_rate() const { return byte(0x18, 0, 8); }
+ uint32_t lfo_am_depth() const { return byte(0x19, 0, 7); }
+ uint32_t lfo_pm_depth() const { return byte(0x149, 0, 7); } // fake
+ uint32_t output_bits() const { return byte(0x1b, 6, 2); }
+ uint32_t lfo2_sync() const { return byte(0x1b, 5, 1); }
+ uint32_t lfo_sync() const { return byte(0x1b, 4, 1); }
+ uint32_t lfo2_waveform() const { return byte(0x1b, 2, 2); }
+ uint32_t lfo_waveform() const { return byte(0x1b, 0, 2); }
+
+ // per-channel registers
+ uint32_t ch_volume(uint32_t choffs) const { return byte(0x00, 0, 8, choffs); }
+ uint32_t ch_output_any(uint32_t choffs) const { return byte(0x20, 7, 1, choffs) | byte(0x30, 0, 1, choffs); }
+ uint32_t ch_output_0(uint32_t choffs) const { return byte(0x30, 0, 1, choffs); }
+ uint32_t ch_output_1(uint32_t choffs) const { return byte(0x20, 7, 1, choffs) | byte(0x30, 0, 1, choffs); }
+ uint32_t ch_output_2(uint32_t choffs) const { return 0; }
+ uint32_t ch_output_3(uint32_t choffs) const { return 0; }
+ uint32_t ch_key_on(uint32_t choffs) const { return byte(0x20, 6, 1, choffs); }
+ uint32_t ch_feedback(uint32_t choffs) const { return byte(0x20, 3, 3, choffs); }
+ uint32_t ch_algorithm(uint32_t choffs) const { return byte(0x20, 0, 3, choffs); }
+ uint32_t ch_block_freq(uint32_t choffs) const { return word(0x28, 0, 7, 0x30, 2, 6, choffs); }
+ uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return byte(0x38, 4, 3, choffs); }
+ uint32_t ch_lfo_am_sens(uint32_t choffs) const { return byte(0x38, 0, 2, choffs); }
+ uint32_t ch_lfo2_pm_sens(uint32_t choffs) const { return byte(0x140, 4, 3, choffs); } // fake
+ uint32_t ch_lfo2_am_sens(uint32_t choffs) const { return byte(0x140, 0, 2, choffs); } // fake
+
+ // per-operator registers
+ uint32_t op_detune(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); }
+ uint32_t op_multiple(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); }
+ uint32_t op_fix_range(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); }
+ uint32_t op_fix_frequency(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); }
+ uint32_t op_waveform(uint32_t opoffs) const { return byte(0x100, 4, 3, opoffs); } // fake
+ uint32_t op_fine(uint32_t opoffs) const { return byte(0x100, 0, 4, opoffs); } // fake
+ uint32_t op_total_level(uint32_t opoffs) const { return byte(0x60, 0, 7, opoffs); }
+ uint32_t op_ksr(uint32_t opoffs) const { return byte(0x80, 6, 2, opoffs); }
+ uint32_t op_fix_mode(uint32_t opoffs) const { return byte(0x80, 5, 1, opoffs); }
+ uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x80, 0, 5, opoffs); }
+ uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0xa0, 7, 1, opoffs); }
+ uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0xa0, 0, 5, opoffs); }
+ uint32_t op_detune2(uint32_t opoffs) const { return byte(0xc0, 6, 2, opoffs); }
+ uint32_t op_sustain_rate(uint32_t opoffs) const { return byte(0xc0, 0, 5, opoffs); }
+ uint32_t op_eg_shift(uint32_t opoffs) const { return byte(0x120, 6, 2, opoffs); } // fake
+ uint32_t op_reverb_rate(uint32_t opoffs) const { return byte(0x120, 0, 3, opoffs); } // fake
+ uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0xe0, 4, 4, opoffs); }
+ uint32_t op_release_rate(uint32_t opoffs) const { return byte(0xe0, 0, 4, opoffs); }
+
+protected:
+ // return a bitfield extracted from a byte
+ uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const
+ {
+ return bitfield(m_regdata[offset + extra_offset], start, count);
+ }
+
+ // return a bitfield extracted from a pair of bytes, MSBs listed first
+ uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const
+ {
+ return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset);
+ }
+
+ // internal state
+ uint32_t m_lfo_counter[2]; // LFO counter
+ uint32_t m_noise_lfsr; // noise LFSR state
+ uint8_t m_noise_counter; // noise counter
+ uint8_t m_noise_state; // latched noise state
+ uint8_t m_noise_lfo; // latched LFO noise value
+ uint8_t m_lfo_am[2]; // current LFO AM value
+ uint8_t m_regdata[REGISTERS]; // register data
+ uint16_t m_phase_substep[OPERATORS]; // phase substep for fixed frequency
+ int16_t m_lfo_waveform[4][LFO_WAVEFORM_LENGTH]; // LFO waveforms; AM in low 8, PM in upper 8
+ uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms
+};
+
+
+
+//*********************************************************
+// IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ymf271
+
+class ymf271
+{
+public:
+ using fm_engine = fm_engine_base<opx_registers>;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+ using output_data = fm_engine::output_data;
+
+ // constructor
+ ymf271(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ uint8_t m_address; // address register
+ fm_engine m_fm; // core FM engine
+};
+
+}
+
+
+#endif // YMFM_OPX_H
diff --git a/3rdparty/ymfm/src/ymfm_opz.cpp b/3rdparty/ymfm/src/ymfm_opz.cpp
new file mode 100644
index 00000000000..1178417bb8a
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opz.cpp
@@ -0,0 +1,808 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_opz.h"
+#include "ymfm_fm.ipp"
+
+#define TEMPORARY_DEBUG_PRINTS (0)
+
+//
+// OPZ (aka YM2414)
+//
+// This chip is not officially documented as far as I know. What I have
+// comes from this site:
+//
+// http://sr4.sakura.ne.jp/fmsound/opz.html
+//
+// and from reading the TX81Z operator manual, which describes how a number
+// of these new features work.
+//
+// OPZ appears be bsaically OPM with a bunch of extra features.
+//
+// For starters, there are two LFO generators. I have presumed that they
+// operate identically since identical parameters are offered for each. I
+// have also presumed the effects are additive between them. The LFOs on
+// the OPZ have an extra "sync" option which apparently causes the LFO to
+// reset whenever a key on is received.
+//
+// At the channel level, there is an additional 8-bit volume control. This
+// might work as an addition to total level, or some other way. Completely
+// unknown, and unimplemented.
+//
+// At the operator level, there are a number of extra features. First, there
+// are 8 different waveforms to choose from. These are different than the
+// waveforms introduced in the OPL2 and later chips.
+//
+// Second, there is an additional "reverb" stage added to the envelope
+// generator, which kicks in when the envelope reaches -18dB. It specifies
+// a slower decay rate to produce a sort of faux reverb effect.
+//
+// The envelope generator also supports a 2-bit shift value, which can be
+// used to reduce the effect of the envelope attenuation.
+//
+// OPZ supports a "fixed frequency" mode for each operator, with a 3-bit
+// range and 4-bit frequency value, plus a 1-bit enable. Not sure how that
+// works at all, so it's not implemented.
+//
+// There are also several mystery fields in the operators which I have no
+// clue about: "fine" (4 bits), "eg_shift" (2 bits), and "rev" (3 bits).
+// eg_shift is some kind of envelope generator effect, but how it works is
+// unknown.
+//
+// Also, according to the site above, the panning controls are changed from
+// OPM, with a "mono" bit and only one control bit for the right channel.
+// Current implementation is just a guess.
+//
+
+namespace ymfm
+{
+
+//*********************************************************
+// OPZ REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// opz_registers - constructor
+//-------------------------------------------------
+
+opz_registers::opz_registers() :
+ m_lfo_counter{ 0, 0 },
+ m_noise_lfsr(1),
+ m_noise_counter(0),
+ m_noise_state(0),
+ m_noise_lfo(0),
+ m_lfo_am{ 0, 0 }
+{
+ // create the waveforms
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15);
+
+ // we only have the diagrams to judge from, but suspecting waveform 1 (and
+ // derived waveforms) are sin^2, based on OPX description of similar wave-
+ // forms; since our sin table is logarithmic, this ends up just being
+ // 2*existing value
+ uint16_t zeroval = m_waveform[0][0];
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[1][index] = std::min<uint16_t>(2 * (m_waveform[0][index] & 0x7fff), zeroval) | (bitfield(index, 9) << 15);
+
+ // remaining waveforms are just derivations of the 2 main ones
+ for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
+ {
+ m_waveform[2][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index];
+ m_waveform[3][index] = bitfield(index, 9) ? zeroval : m_waveform[1][index];
+ m_waveform[4][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index * 2];
+ m_waveform[5][index] = bitfield(index, 9) ? zeroval : m_waveform[1][index * 2];
+ m_waveform[6][index] = bitfield(index, 9) ? zeroval : m_waveform[0][(index * 2) & 0x1ff];
+ m_waveform[7][index] = bitfield(index, 9) ? zeroval : m_waveform[1][(index * 2) & 0x1ff];
+ }
+
+ // create the LFO waveforms; AM in the low 8 bits, PM in the upper 8
+ // waveforms are adjusted to match the pictures in the application manual
+ for (uint32_t index = 0; index < LFO_WAVEFORM_LENGTH; index++)
+ {
+ // waveform 0 is a sawtooth
+ uint8_t am = index ^ 0xff;
+ 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 = 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 = bitfield(index, 6) ? am : ~am;
+ m_lfo_waveform[2][index] = am | (pm << 8);
+
+ // waveform 3 is noise; it is filled in dynamically
+ }
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+void opz_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+
+ // enable output on both channels by default
+ m_regdata[0x30] = m_regdata[0x31] = m_regdata[0x32] = m_regdata[0x33] = 0x01;
+ m_regdata[0x34] = m_regdata[0x35] = m_regdata[0x36] = m_regdata[0x37] = 0x01;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void opz_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_lfo_counter);
+ state.save_restore(m_lfo_am);
+ state.save_restore(m_noise_lfsr);
+ state.save_restore(m_noise_counter);
+ state.save_restore(m_noise_state);
+ state.save_restore(m_noise_lfo);
+ state.save_restore(m_regdata);
+ state.save_restore(m_phase_substep);
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPZ this is fixed
+//-------------------------------------------------
+
+void opz_registers::operator_map(operator_mapping &dest) const
+{
+ // Note that the channel index order is 0,2,1,3, so we bitswap the index.
+ //
+ // This is because the order in the map is:
+ // carrier 1, carrier 2, modulator 1, modulator 2
+ //
+ // But when wiring up the connections, the more natural order is:
+ // carrier 1, modulator 1, carrier 2, modulator 2
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 16, 8, 24 ), // Channel 0 operators
+ operator_list( 1, 17, 9, 25 ), // Channel 1 operators
+ operator_list( 2, 18, 10, 26 ), // Channel 2 operators
+ operator_list( 3, 19, 11, 27 ), // Channel 3 operators
+ operator_list( 4, 20, 12, 28 ), // Channel 4 operators
+ operator_list( 5, 21, 13, 29 ), // Channel 5 operators
+ operator_list( 6, 22, 14, 30 ), // Channel 6 operators
+ operator_list( 7, 23, 15, 31 ), // Channel 7 operators
+ } };
+ dest = s_fixed_map;
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array
+//-------------------------------------------------
+
+bool opz_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
+{
+ assert(index < REGISTERS);
+
+ // special mappings:
+ // 0x16 -> 0x188 if bit 7 is set
+ // 0x19 -> 0x189 if bit 7 is set
+ // 0x38..0x3F -> 0x180..0x187 if bit 7 is set
+ // 0x40..0x5F -> 0x100..0x11F if bit 7 is set
+ // 0xC0..0xDF -> 0x120..0x13F if bit 5 is set
+ if (index == 0x17 && bitfield(data, 7) != 0)
+ m_regdata[0x188] = data;
+ else if (index == 0x19 && bitfield(data, 7) != 0)
+ m_regdata[0x189] = data;
+ else if ((index & 0xf8) == 0x38 && bitfield(data, 7) != 0)
+ m_regdata[0x180 + (index & 7)] = data;
+ else if ((index & 0xe0) == 0x40 && bitfield(data, 7) != 0)
+ m_regdata[0x100 + (index & 0x1f)] = data;
+ else if ((index & 0xe0) == 0xc0 && bitfield(data, 5) != 0)
+ m_regdata[0x120 + (index & 0x1f)] = data;
+ else if (index < 0x100)
+ m_regdata[index] = data;
+
+ // preset writes restore some values from a preset memory; not sure
+ // how this really works but the TX81Z will overwrite the sustain level/
+ // release rate register and the envelope shift/reverb rate register to
+ // dampen sound, then write the preset number to register 8 to restore them
+ if (index == 0x08)
+ {
+ int chan = bitfield(data, 0, 3);
+ if (TEMPORARY_DEBUG_PRINTS)
+ printf("Loading preset %d\n", chan);
+ m_regdata[0xe0 + chan + 0] = m_regdata[0x140 + chan + 0];
+ m_regdata[0xe0 + chan + 8] = m_regdata[0x140 + chan + 8];
+ m_regdata[0xe0 + chan + 16] = m_regdata[0x140 + chan + 16];
+ m_regdata[0xe0 + chan + 24] = m_regdata[0x140 + chan + 24];
+ m_regdata[0x120 + chan + 0] = m_regdata[0x160 + chan + 0];
+ m_regdata[0x120 + chan + 8] = m_regdata[0x160 + chan + 8];
+ m_regdata[0x120 + chan + 16] = m_regdata[0x160 + chan + 16];
+ m_regdata[0x120 + chan + 24] = m_regdata[0x160 + chan + 24];
+ }
+
+ // store the presets under some unknown condition; the pattern of writes
+ // when setting a new preset is:
+ //
+ // 08 (0-7), 80-9F, A0-BF, C0-DF, C0-DF (alt), 20-27, 40-5F, 40-5F (alt),
+ // C0-DF (alt -- again?), 38-3F, 1B, 18, E0-FF
+ //
+ // So it writes 0-7 to 08 to either reset all presets or to indicate
+ // that we're going to be loading them. Immediately after all the writes
+ // above, the very next write will be temporary values to blow away the
+ // values loaded into E0-FF, so somehow it also knows that anything after
+ // that point is not part of the preset.
+ //
+ // For now, try using the 40-5F (alt) writes as flags that presets are
+ // being loaded until the E0-FF writes happen.
+ bool is_setting_preset = (bitfield(m_regdata[0x100 + (index & 0x1f)], 7) != 0);
+ if (is_setting_preset)
+ {
+ if ((index & 0xe0) == 0xe0)
+ {
+ m_regdata[0x140 + (index & 0x1f)] = data;
+ m_regdata[0x100 + (index & 0x1f)] &= 0x7f;
+ }
+ else if ((index & 0xe0) == 0xc0 && bitfield(data, 5) != 0)
+ m_regdata[0x160 + (index & 0x1f)] = data;
+ }
+
+ // handle writes to the key on index
+ if ((index & 0xf8) == 0x20 && bitfield(index, 0, 3) == bitfield(m_regdata[0x08], 0, 3))
+ {
+ channel = bitfield(index, 0, 3);
+ opmask = ch_key_on(channel) ? 0xf : 0;
+
+ // according to the TX81Z manual, the sync option causes the LFOs
+ // to reset at each note on
+ if (opmask != 0)
+ {
+ if (lfo_sync())
+ m_lfo_counter[0] = 0;
+ if (lfo2_sync())
+ m_lfo_counter[1] = 0;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+int32_t opz_registers::clock_noise_and_lfo()
+{
+ // base noise frequency is measured at 2x 1/2 FM frequency; this
+ // means each tick counts as two steps against the noise counter
+ uint32_t freq = noise_frequency();
+ for (int rep = 0; rep < 2; rep++)
+ {
+ // evidence seems to suggest the LFSR is clocked continually and just
+ // sampled at the noise frequency for output purposes; note that the
+ // low 8 bits are the most recent 8 bits of history while bits 8-24
+ // contain the 17 bit LFSR state
+ m_noise_lfsr <<= 1;
+ m_noise_lfsr |= bitfield(m_noise_lfsr, 17) ^ bitfield(m_noise_lfsr, 14) ^ 1;
+
+ // compare against the frequency and latch when we exceed it
+ if (m_noise_counter++ >= freq)
+ {
+ m_noise_counter = 0;
+ m_noise_state = bitfield(m_noise_lfsr, 17);
+ }
+ }
+
+ // treat the rate as a 4.4 floating-point step value with implied
+ // leading 1; this matches exactly the frequencies in the application
+ // manual, though it might not be implemented exactly this way on chip
+ uint32_t rate0 = lfo_rate();
+ uint32_t rate1 = lfo2_rate();
+ m_lfo_counter[0] += (0x10 | bitfield(rate0, 0, 4)) << bitfield(rate0, 4, 4);
+ m_lfo_counter[1] += (0x10 | bitfield(rate1, 0, 4)) << bitfield(rate1, 4, 4);
+ uint32_t lfo0 = bitfield(m_lfo_counter[0], 22, 8);
+ uint32_t lfo1 = bitfield(m_lfo_counter[1], 22, 8);
+
+ // fill in the noise entry 1 ahead of our current position; this
+ // ensures the current value remains stable for a full LFO clock
+ // and effectively latches the running value when the LFO advances
+ uint32_t lfo_noise = bitfield(m_noise_lfsr, 17, 8);
+ m_lfo_waveform[3][(lfo0 + 1) & 0xff] = lfo_noise | (lfo_noise << 8);
+ m_lfo_waveform[3][(lfo1 + 1) & 0xff] = lfo_noise | (lfo_noise << 8);
+
+ // fetch the AM/PM values based on the waveform; AM is unsigned and
+ // encoded in the low 8 bits, while PM signed and encoded in the upper
+ // 8 bits
+ int32_t ampm0 = m_lfo_waveform[lfo_waveform()][lfo0];
+ int32_t ampm1 = m_lfo_waveform[lfo2_waveform()][lfo1];
+
+ // apply depth to the AM values and store for later
+ m_lfo_am[0] = ((ampm0 & 0xff) * lfo_am_depth()) >> 7;
+ m_lfo_am[1] = ((ampm1 & 0xff) * lfo2_am_depth()) >> 7;
+
+ // apply depth to the PM values and return them combined into two
+ int32_t pm0 = ((ampm0 >> 8) * int32_t(lfo_pm_depth())) >> 7;
+ int32_t pm1 = ((ampm1 >> 8) * int32_t(lfo2_pm_depth())) >> 7;
+ return (pm0 & 0xff) | (pm1 << 8);
+}
+
+
+//-------------------------------------------------
+// lfo_am_offset - return the AM offset from LFO
+// for the given channel
+//-------------------------------------------------
+
+uint32_t opz_registers::lfo_am_offset(uint32_t choffs) const
+{
+ // not sure how this works for real, but just adding the two
+ // AM LFOs together
+ uint32_t result = 0;
+
+ // shift value for AM sensitivity is [*, 0, 1, 2],
+ // mapping to values of [0, 23.9, 47.8, and 95.6dB]
+ uint32_t am_sensitivity = ch_lfo_am_sens(choffs);
+ if (am_sensitivity != 0)
+ result = m_lfo_am[0] << (am_sensitivity - 1);
+
+ // QUESTION: see OPN note below for the dB range mapping; it applies
+ // here as well
+
+ // raw LFO AM value on OPZ is 0-FF, which is already a factor of 2
+ // larger than the OPN below, putting our staring point at 2x theirs;
+ // this works out since our minimum is 2x their maximum
+ uint32_t am_sensitivity2 = ch_lfo2_am_sens(choffs);
+ if (am_sensitivity2 != 0)
+ result += m_lfo_am[1] << (am_sensitivity2 - 1);
+
+ return result;
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data
+//-------------------------------------------------
+
+void opz_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
+{
+ // TODO: how does fixed frequency mode work? appears to be enabled by
+ // op_fix_mode(), and controlled by op_fix_range(), op_fix_frequency()
+
+ // TODO: what is op_rev()?
+
+ // set up the easy stuff
+ cache.waveform = &m_waveform[op_waveform(opoffs)][0];
+
+ // get frequency from the channel
+ uint32_t block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // compute the keycode: block_freq is:
+ //
+ // BBBCCCCFFFFFF
+ // ^^^^^
+ //
+ // the 5-bit keycode is just the top 5 bits (block + top 2 bits
+ // of the key code)
+ uint32_t keycode = bitfield(block_freq, 8, 5);
+
+ // detune adjustment
+ cache.detune = detune_adjustment(op_detune(opoffs), keycode);
+
+ // multiple value, as an x.4 value (0 means 0.5)
+ // the "fine" control provides the fractional bits
+ cache.multiple = op_multiple(opoffs) << 4;
+ if (cache.multiple == 0)
+ cache.multiple = 0x08;
+ cache.multiple |= op_fine(opoffs);
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on
+ // block_freq, detune, and multiple, so compute it after we've done those;
+ // note that fix frequency mode is also treated as dynamic
+ if (!op_fix_mode(opoffs) && (lfo_pm_depth() == 0 || ch_lfo_pm_sens(choffs) == 0) && (lfo2_pm_depth() == 0 || ch_lfo2_pm_sens(choffs) == 0))
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8
+ // TODO: how does ch_volume() fit into this?
+ cache.total_level = op_total_level(opoffs) << 3;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // determine KSR adjustment for enevlope rates
+ uint32_t ksrval = keycode >> (op_ksr(opoffs) ^ 3);
+ cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval);
+ cache.eg_rate[EG_REVERB] = cache.eg_rate[EG_RELEASE];
+ uint32_t reverb = op_reverb_rate(opoffs);
+ if (reverb != 0)
+ cache.eg_rate[EG_REVERB] = std::min<uint32_t>(effective_rate(reverb * 4 + 2, ksrval), cache.eg_rate[EG_REVERB]);
+
+ // set the envelope shift; TX81Z manual says operator 1 shift is fixed at "off"
+ cache.eg_shift = ((opoffs & 0x18) == 0) ? 0 : op_eg_shift(opoffs);
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+uint32_t opz_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm)
+{
+ // OPZ has a fixed frequency mode; it is unclear whether the
+ // detune and multiple parameters affect things
+
+ uint32_t phase_step;
+ if (op_fix_mode(opoffs))
+ {
+ // the baseline frequency in hz comes from the fix frequency and fine
+ // registers, which can specify values 8-255Hz in 1Hz increments; that
+ // value is then shifted up by the 3-bit range
+ uint32_t freq = op_fix_frequency(opoffs) << 4;
+ if (freq == 0)
+ freq = 8;
+ freq |= op_fine(opoffs);
+ freq <<= op_fix_range(opoffs);
+
+ // there is not enough resolution in the plain phase step to track the
+ // full range of frequencies, so we keep a per-operator sub step with an
+ // additional 12 bits of resolution; this calculation gives us, for
+ // example, a frequency of 8.0009Hz when 8Hz is requested
+ uint32_t substep = m_phase_substep[opoffs];
+ substep += 75 * freq;
+ phase_step = substep >> 12;
+ m_phase_substep[opoffs] = substep & 0xfff;
+
+ // detune/multiple occupy the same space as fix_range/fix_frequency so
+ // don't apply them in addition
+ return phase_step;
+ }
+ else
+ {
+ // start with coarse detune delta; table uses cents value from
+ // manual, converted into 1/64ths
+ static const int16_t s_detune2_delta[4] = { 0, (600*64+50)/100, (781*64+50)/100, (950*64+50)/100 };
+ int32_t delta = s_detune2_delta[op_detune2(opoffs)];
+
+ // add in the PM deltas
+ uint32_t pm_sensitivity = ch_lfo_pm_sens(choffs);
+ if (pm_sensitivity != 0)
+ {
+ // raw PM value is -127..128 which is +/- 200 cents
+ // manual gives these magnitudes in cents:
+ // 0, +/-5, +/-10, +/-20, +/-50, +/-100, +/-400, +/-700
+ // this roughly corresponds to shifting the 200-cent value:
+ // 0 >> 5, >> 4, >> 3, >> 2, >> 1, << 1, << 2
+ if (pm_sensitivity < 6)
+ delta += int8_t(lfo_raw_pm) >> (6 - pm_sensitivity);
+ else
+ delta += int8_t(lfo_raw_pm) << (pm_sensitivity - 5);
+ }
+ uint32_t pm_sensitivity2 = ch_lfo2_pm_sens(choffs);
+ if (pm_sensitivity2 != 0)
+ {
+ // raw PM value is -127..128 which is +/- 200 cents
+ // manual gives these magnitudes in cents:
+ // 0, +/-5, +/-10, +/-20, +/-50, +/-100, +/-400, +/-700
+ // this roughly corresponds to shifting the 200-cent value:
+ // 0 >> 5, >> 4, >> 3, >> 2, >> 1, << 1, << 2
+ if (pm_sensitivity2 < 6)
+ delta += int8_t(lfo_raw_pm >> 8) >> (6 - pm_sensitivity2);
+ else
+ delta += int8_t(lfo_raw_pm >> 8) << (pm_sensitivity2 - 5);
+ }
+
+ // apply delta and convert to a frequency number; this translation is
+ // the same as OPM so just re-use that helper
+ phase_step = opm_key_code_to_phase_step(cache.block_freq, delta);
+
+ // apply detune based on the keycode
+ phase_step += cache.detune;
+
+ // apply frequency multiplier (which is cached as an x.4 value)
+ return (phase_step * cache.multiple) >> 4;
+ }
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
+{
+ uint32_t chnum = choffs;
+ uint32_t opnum = opoffs;
+
+ char buffer[256];
+ int end = 0;
+
+ end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u", chnum, opnum);
+
+ if (op_fix_mode(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(&buffer[end], sizeof(buffer) - end, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs));
+
+ 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),
+ op_multiple(opoffs),
+ op_total_level(opoffs),
+ op_ksr(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_sustain_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs),
+ ch_output_0(choffs) ? 'L' : '-',
+ ch_output_1(choffs) ? 'R' : '-');
+
+ if (op_eg_shift(opoffs) != 0)
+ 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(&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(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth());
+ if (am || pm)
+ 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(&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(&buffer[end], sizeof(buffer) - end, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth());
+ if (am2 || pm2)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]);
+
+ if (op_reverb_rate(opoffs) != 0)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " rev=%u", op_reverb_rate(opoffs));
+ if (op_waveform(opoffs) != 0)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs));
+ if (noise_enable() && opoffs == 31)
+ end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1");
+
+ return buffer;
+}
+
+
+
+//*********************************************************
+// YM2414
+//*********************************************************
+
+//-------------------------------------------------
+// ym2414 - constructor
+//-------------------------------------------------
+
+ym2414::ym2414(ymfm_interface &intf) :
+ m_address(0),
+ m_fm(intf)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the system
+//-------------------------------------------------
+
+void ym2414::reset()
+{
+ // reset the engines
+ m_fm.reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ym2414::save_restore(ymfm_saved_state &state)
+{
+ m_fm.save_restore(state);
+ state.save_restore(m_address);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+uint8_t ym2414::read_status()
+{
+ uint8_t result = m_fm.status();
+ if (m_fm.intf().ymfm_is_busy())
+ result |= fm_engine::STATUS_BUSY;
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - handle a read from the device
+//-------------------------------------------------
+
+uint8_t ym2414::read(uint32_t offset)
+{
+ uint8_t result = 0xff;
+ switch (offset & 1)
+ {
+ case 0: // data port (unused)
+ debug::log_unexpected_read_write("Unexpected read from YM2414 offset %d\n", offset & 3);
+ break;
+
+ case 1: // status port, YM2203 compatible
+ result = read_status();
+ break;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_address - handle a write to the address
+// register
+//-------------------------------------------------
+
+void ym2414::write_address(uint8_t data)
+{
+ // just set the address
+ m_address = data;
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2414::write_data(uint8_t data)
+{
+ // write the FM register
+ m_fm.write(m_address, data);
+ if (TEMPORARY_DEBUG_PRINTS)
+ {
+ switch (m_address & 0xe0)
+ {
+ case 0x00:
+ printf("CTL %02X = %02X\n", m_address, data);
+ break;
+
+ case 0x20:
+ switch (m_address & 0xf8)
+ {
+ case 0x20: printf("R/FBL/ALG %d = %02X\n", m_address & 7, data); break;
+ case 0x28: printf("KC %d = %02X\n", m_address & 7, data); break;
+ case 0x30: printf("KF/M %d = %02X\n", m_address & 7, data); break;
+ case 0x38: printf("PMS/AMS %d = %02X\n", m_address & 7, data); break;
+ }
+ break;
+
+ case 0x40:
+ if (bitfield(data, 7) == 0)
+ printf("DT1/MUL %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
+ else
+ printf("OW/FINE %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
+ break;
+
+ case 0x60:
+ printf("TL %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
+ break;
+
+ case 0x80:
+ printf("KRS/FIX/AR %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
+ break;
+
+ case 0xa0:
+ printf("A/D1R %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
+ break;
+
+ case 0xc0:
+ if (bitfield(data, 5) == 0)
+ printf("DT2/D2R %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
+ else
+ printf("EGS/REV %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
+ break;
+
+ case 0xe0:
+ printf("D1L/RR %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
+ break;
+ }
+ }
+
+ // special cases
+ if (m_address == 0x1b)
+ {
+ // writes to register 0x1B send the upper 2 bits to the output lines
+ m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data >> 6);
+ }
+
+ // mark busy for a bit
+ m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the register
+// interface
+//-------------------------------------------------
+
+void ym2414::write(uint32_t offset, uint8_t data)
+{
+ switch (offset & 1)
+ {
+ case 0: // address port
+ write_address(data);
+ break;
+
+ case 1: // data port
+ write_data(data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// generate - generate one sample of sound
+//-------------------------------------------------
+
+void ym2414::generate(output_data *output, uint32_t numsamples)
+{
+ for (uint32_t samp = 0; samp < numsamples; samp++, output++)
+ {
+ // clock the system
+ m_fm.clock(fm_engine::ALL_CHANNELS);
+
+ // update the FM content; YM2414 is full 14-bit with no intermediate clipping
+ m_fm.output(output->clear(), 0, 32767, fm_engine::ALL_CHANNELS);
+
+ // unsure about YM2414 outputs; assume it is like YM2151
+ output->roundtrip_fp();
+ }
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_opz.h b/3rdparty/ymfm/src/ymfm_opz.h
new file mode 100644
index 00000000000..997ba32f9f2
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_opz.h
@@ -0,0 +1,332 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_OPZ_H
+#define YMFM_OPZ_H
+
+#pragma once
+
+#include "ymfm.h"
+#include "ymfm_fm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// REGISTER CLASSES
+//*********************************************************
+
+// ======================> opz_registers
+
+//
+// OPZ register map:
+//
+// System-wide registers:
+// 08 -----xxx Load preset (not sure how it gets saved)
+// 0F x------- Noise enable
+// ---xxxxx Noise frequency
+// 10 xxxxxxxx Timer A value (upper 8 bits)
+// 11 ------xx Timer A value (lower 2 bits)
+// 12 xxxxxxxx Timer B value
+// 14 x------- CSM mode
+// --x----- Reset timer B
+// ---x---- Reset timer A
+// ----x--- Enable timer B
+// -----x-- Enable timer A
+// ------x- Load timer B
+// -------x Load timer A
+// 16 xxxxxxxx LFO #2 frequency
+// 17 0xxxxxxx AM LFO #2 depth
+// 1xxxxxxx PM LFO #2 depth
+// 18 xxxxxxxx LFO frequency
+// 19 0xxxxxxx AM LFO depth
+// 1xxxxxxx PM LFO depth
+// 1B xx------ CT (2 output data lines)
+// --x----- LFO #2 sync
+// ---x---- LFO sync
+// ----xx-- LFO #2 waveform
+// ------xx LFO waveform
+//
+// Per-channel registers (channel in address bits 0-2)
+// 00-07 xxxxxxxx Channel volume
+// 20-27 x------- Pan right
+// -x------ Key on (0)/off(1)
+// --xxx--- Feedback level for operator 1 (0-7)
+// -----xxx Operator connection algorithm (0-7)
+// 28-2F -xxxxxxx Key code
+// 30-37 xxxxxx-- Key fraction
+// -------x Mono? mode
+// 38-3F 0xxx---- LFO PM sensitivity
+// -----0xx LFO AM shift
+// 1xxx---- LFO #2 PM sensitivity
+// -----1xx LFO #2 AM shift
+//
+// Per-operator registers (channel in address bits 0-2, operator in bits 3-4)
+// 40-5F 0xxx---- Detune value (0-7)
+// 0---xxxx Multiple value (0-15)
+// 0xxx---- Fix range (0-15)
+// 0---xxxx Fix frequency (0-15)
+// 1xxx---- Oscillator waveform (0-7)
+// 1---xxxx Fine? (0-15)
+// 60-7F -xxxxxxx Total level (0-127)
+// 80-9F xx------ Key scale rate (0-3)
+// --x----- Fix frequency mode
+// ---xxxxx Attack rate (0-31)
+// A0-BF x------- LFO AM enable
+// ---xxxxx Decay rate (0-31)
+// C0-DF xx0----- Detune 2 value (0-3)
+// --0xxxxx Sustain rate (0-31)
+// xx1----- Envelope generator shift? (0-3)
+// --1--xxx Rev? (0-7)
+// E0-FF xxxx---- Sustain level (0-15)
+// ----xxxx Release rate (0-15)
+//
+// Internal (fake) registers:
+// 100-11F -xxx---- Oscillator waveform (0-7)
+// ----xxxx Fine? (0-15)
+// 120-13F xx------ Envelope generator shift (0-3)
+// -----xxx Reverb rate (0-7)
+// 140-15F xxxx---- Preset sustain level (0-15)
+// ----xxxx Preset release rate (0-15)
+// 160-17F xx------ Envelope generator shift (0-3)
+// -----xxx Reverb rate (0-7)
+// 180-187 -xxx---- LFO #2 PM sensitivity
+// ---- xxx LFO #2 AM shift
+// 188 -xxxxxxx LFO #2 PM depth
+// 189 -xxxxxxx LFO PM depth
+//
+
+class opz_registers : public fm_registers_base
+{
+ // LFO waveforms are 256 entries long
+ static constexpr uint32_t LFO_WAVEFORM_LENGTH = 256;
+
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = 2;
+ static constexpr uint32_t CHANNELS = 8;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+ static constexpr uint32_t OPERATORS = CHANNELS * 4;
+ static constexpr uint32_t WAVEFORMS = 8;
+ static constexpr uint32_t REGISTERS = 0x190;
+ static constexpr uint32_t DEFAULT_PRESCALE = 2;
+ static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
+ static constexpr bool EG_HAS_REVERB = true;
+ static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
+ static constexpr uint32_t REG_MODE = 0x14;
+ static constexpr uint8_t STATUS_TIMERA = 0x01;
+ static constexpr uint8_t STATUS_TIMERB = 0x02;
+ static constexpr uint8_t STATUS_BUSY = 0x80;
+ static constexpr uint8_t STATUS_IRQ = 0;
+
+ // constructor
+ opz_registers();
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ return chnum;
+ }
+
+ // map operator number to register offset
+ static constexpr uint32_t operator_offset(uint32_t opnum)
+ {
+ assert(opnum < OPERATORS);
+ return opnum;
+ }
+
+ // return an array of operator indices for each channel
+ struct operator_mapping { uint32_t chan[CHANNELS]; };
+ void operator_map(operator_mapping &dest) const;
+
+ // handle writes to the register array
+ bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask);
+
+ // clock the noise and LFO, if present, returning LFO PM value
+ int32_t clock_noise_and_lfo();
+
+ // return the AM offset from LFO for the given channel
+ uint32_t lfo_am_offset(uint32_t choffs) const;
+
+ // return the current noise state, gated by the noise clock
+ uint32_t noise_state() const { return m_noise_state; }
+
+ // caching helpers
+ void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache);
+
+ // compute the phase step, given a PM value
+ uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm);
+
+ // log a key-on event
+ std::string log_keyon(uint32_t choffs, uint32_t opoffs);
+
+ // system-wide registers
+ uint32_t noise_frequency() const { return byte(0x0f, 0, 5); }
+ uint32_t noise_enable() const { return byte(0x0f, 7, 1); }
+ uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); }
+ uint32_t timer_b_value() const { return byte(0x12, 0, 8); }
+ uint32_t csm() const { return byte(0x14, 7, 1); }
+ uint32_t reset_timer_b() const { return byte(0x14, 5, 1); }
+ uint32_t reset_timer_a() const { return byte(0x14, 4, 1); }
+ uint32_t enable_timer_b() const { return byte(0x14, 3, 1); }
+ uint32_t enable_timer_a() const { return byte(0x14, 2, 1); }
+ uint32_t load_timer_b() const { return byte(0x14, 1, 1); }
+ uint32_t load_timer_a() const { return byte(0x14, 0, 1); }
+ uint32_t lfo2_pm_depth() const { return byte(0x188, 0, 7); } // fake
+ uint32_t lfo2_rate() const { return byte(0x16, 0, 8); }
+ uint32_t lfo2_am_depth() const { return byte(0x17, 0, 7); }
+ uint32_t lfo_rate() const { return byte(0x18, 0, 8); }
+ uint32_t lfo_am_depth() const { return byte(0x19, 0, 7); }
+ uint32_t lfo_pm_depth() const { return byte(0x189, 0, 7); } // fake
+ uint32_t output_bits() const { return byte(0x1b, 6, 2); }
+ uint32_t lfo2_sync() const { return byte(0x1b, 5, 1); }
+ uint32_t lfo_sync() const { return byte(0x1b, 4, 1); }
+ uint32_t lfo2_waveform() const { return byte(0x1b, 2, 2); }
+ uint32_t lfo_waveform() const { return byte(0x1b, 0, 2); }
+
+ // per-channel registers
+ uint32_t ch_volume(uint32_t choffs) const { return byte(0x00, 0, 8, choffs); }
+ uint32_t ch_output_any(uint32_t choffs) const { return byte(0x20, 7, 1, choffs) | byte(0x30, 0, 1, choffs); }
+ uint32_t ch_output_0(uint32_t choffs) const { return byte(0x30, 0, 1, choffs); }
+ uint32_t ch_output_1(uint32_t choffs) const { return byte(0x20, 7, 1, choffs) | byte(0x30, 0, 1, choffs); }
+ uint32_t ch_output_2(uint32_t choffs) const { return 0; }
+ uint32_t ch_output_3(uint32_t choffs) const { return 0; }
+ uint32_t ch_key_on(uint32_t choffs) const { return byte(0x20, 6, 1, choffs); }
+ uint32_t ch_feedback(uint32_t choffs) const { return byte(0x20, 3, 3, choffs); }
+ uint32_t ch_algorithm(uint32_t choffs) const { return byte(0x20, 0, 3, choffs); }
+ uint32_t ch_block_freq(uint32_t choffs) const { return word(0x28, 0, 7, 0x30, 2, 6, choffs); }
+ uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return byte(0x38, 4, 3, choffs); }
+ uint32_t ch_lfo_am_sens(uint32_t choffs) const { return byte(0x38, 0, 2, choffs); }
+ uint32_t ch_lfo2_pm_sens(uint32_t choffs) const { return byte(0x180, 4, 3, choffs); } // fake
+ uint32_t ch_lfo2_am_sens(uint32_t choffs) const { return byte(0x180, 0, 2, choffs); } // fake
+
+ // per-operator registers
+ uint32_t op_detune(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); }
+ uint32_t op_multiple(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); }
+ uint32_t op_fix_range(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); }
+ uint32_t op_fix_frequency(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); }
+ uint32_t op_waveform(uint32_t opoffs) const { return byte(0x100, 4, 3, opoffs); } // fake
+ uint32_t op_fine(uint32_t opoffs) const { return byte(0x100, 0, 4, opoffs); } // fake
+ uint32_t op_total_level(uint32_t opoffs) const { return byte(0x60, 0, 7, opoffs); }
+ uint32_t op_ksr(uint32_t opoffs) const { return byte(0x80, 6, 2, opoffs); }
+ uint32_t op_fix_mode(uint32_t opoffs) const { return byte(0x80, 5, 1, opoffs); }
+ uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x80, 0, 5, opoffs); }
+ uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0xa0, 7, 1, opoffs); }
+ uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0xa0, 0, 5, opoffs); }
+ uint32_t op_detune2(uint32_t opoffs) const { return byte(0xc0, 6, 2, opoffs); }
+ uint32_t op_sustain_rate(uint32_t opoffs) const { return byte(0xc0, 0, 5, opoffs); }
+ uint32_t op_eg_shift(uint32_t opoffs) const { return byte(0x120, 6, 2, opoffs); } // fake
+ uint32_t op_reverb_rate(uint32_t opoffs) const { return byte(0x120, 0, 3, opoffs); } // fake
+ uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0xe0, 4, 4, opoffs); }
+ uint32_t op_release_rate(uint32_t opoffs) const { return byte(0xe0, 0, 4, opoffs); }
+
+protected:
+ // return a bitfield extracted from a byte
+ uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const
+ {
+ return bitfield(m_regdata[offset + extra_offset], start, count);
+ }
+
+ // return a bitfield extracted from a pair of bytes, MSBs listed first
+ uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const
+ {
+ return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset);
+ }
+
+ // internal state
+ uint32_t m_lfo_counter[2]; // LFO counter
+ uint32_t m_noise_lfsr; // noise LFSR state
+ uint8_t m_noise_counter; // noise counter
+ uint8_t m_noise_state; // latched noise state
+ uint8_t m_noise_lfo; // latched LFO noise value
+ uint8_t m_lfo_am[2]; // current LFO AM value
+ uint8_t m_regdata[REGISTERS]; // register data
+ uint16_t m_phase_substep[OPERATORS]; // phase substep for fixed frequency
+ int16_t m_lfo_waveform[4][LFO_WAVEFORM_LENGTH]; // LFO waveforms; AM in low 8, PM in upper 8
+ uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms
+};
+
+
+
+//*********************************************************
+// IMPLEMENTATION CLASSES
+//*********************************************************
+
+// ======================> ym2414
+
+class ym2414
+{
+public:
+ using fm_engine = fm_engine_base<opz_registers>;
+ static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
+ using output_data = fm_engine::output_data;
+
+ // constructor
+ ym2414(ymfm_interface &intf);
+
+ // reset
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // pass-through helpers
+ uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
+ void invalidate_caches() { m_fm.invalidate_caches(); }
+
+ // read access
+ uint8_t read_status();
+ uint8_t read(uint32_t offset);
+
+ // write access
+ void write_address(uint8_t data);
+ void write_data(uint8_t data);
+ void write(uint32_t offset, uint8_t data);
+
+ // generate one sample of sound
+ void generate(output_data *output, uint32_t numsamples = 1);
+
+protected:
+ // internal state
+ uint8_t m_address; // address register
+ fm_engine m_fm; // core FM engine
+};
+
+}
+
+
+#endif // YMFM_OPZ_H
diff --git a/3rdparty/ymfm/src/ymfm_pcm.cpp b/3rdparty/ymfm/src/ymfm_pcm.cpp
new file mode 100644
index 00000000000..34417490ccd
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_pcm.cpp
@@ -0,0 +1,714 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_pcm.h"
+#include "ymfm_fm.h"
+#include "ymfm_fm.ipp"
+
+namespace ymfm
+{
+
+//*********************************************************
+// PCM REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// reset - reset the register state
+//-------------------------------------------------
+
+void pcm_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+ m_regdata[0xf8] = 0x1b;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void pcm_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_regdata);
+}
+
+
+//-------------------------------------------------
+// cache_channel_data - update the cache with
+// data from the registers
+//-------------------------------------------------
+
+void pcm_registers::cache_channel_data(uint32_t choffs, pcm_cache &cache)
+{
+ // compute step from octave and fnumber; the math here implies
+ // a .18 fraction but .16 should be perfectly fine
+ int32_t octave = int8_t(ch_octave(choffs) << 4) >> 4;
+ uint32_t fnum = ch_fnumber(choffs);
+ cache.step = ((0x400 | fnum) << (octave + 7)) >> 2;
+
+ // total level is computed as a .10 value for interpolation
+ cache.total_level = ch_total_level(choffs) << 10;
+
+ // compute panning values in terms of envelope attenuation
+ int32_t panpot = int8_t(ch_panpot(choffs) << 4) >> 4;
+ if (panpot >= 0)
+ {
+ cache.pan_left = (panpot == 7) ? 0x3ff : 0x20 * panpot;
+ cache.pan_right = 0;
+ }
+ else if (panpot >= -7)
+ {
+ cache.pan_left = 0;
+ cache.pan_right = (panpot == -7) ? 0x3ff : -0x20 * panpot;
+ }
+ else
+ cache.pan_left = cache.pan_right = 0x3ff;
+
+ // determine the LFO stepping value; this how much to add to a running
+ // x.18 value for the LFO; steps were derived from frequencies in the
+ // manual and come out very close with these values
+ static const uint8_t s_lfo_steps[8] = { 1, 12, 19, 25, 31, 35, 37, 42 };
+ cache.lfo_step = s_lfo_steps[ch_lfo_speed(choffs)];
+
+ // AM LFO depth values, derived from the manual; note each has at most
+ // 2 bits to make the "multiply" easy in hardware
+ static const uint8_t s_am_depth[8] = { 0, 0x14, 0x20, 0x28, 0x30, 0x40, 0x50, 0x80 };
+ cache.am_depth = s_am_depth[ch_am_depth(choffs)];
+
+ // PM LFO depth values; these are converted from the manual's cents values
+ // into f-numbers; the computations come out quite cleanly so pretty sure
+ // these are correct
+ static const uint8_t s_pm_depth[8] = { 0, 2, 3, 4, 6, 12, 24, 48 };
+ cache.pm_depth = s_pm_depth[ch_vibrato(choffs)];
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = ch_sustain_level(choffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // compute the key scaling correction factor; 15 means don't do any correction
+ int32_t correction = ch_rate_correction(choffs);
+ if (correction == 15)
+ correction = 0;
+ else
+ correction = (octave + correction) * 2 + bitfield(fnum, 9);
+
+ // compute the envelope generator rates
+ cache.eg_rate[EG_ATTACK] = effective_rate(ch_attack_rate(choffs), correction);
+ cache.eg_rate[EG_DECAY] = effective_rate(ch_decay_rate(choffs), correction);
+ cache.eg_rate[EG_SUSTAIN] = effective_rate(ch_sustain_rate(choffs), correction);
+ cache.eg_rate[EG_RELEASE] = effective_rate(ch_release_rate(choffs), correction);
+ cache.eg_rate[EG_REVERB] = 5;
+
+ // if damping is on, override some things; essentially decay at a hardcoded
+ // rate of 48 until -12db (0x80), then at maximum rate for the rest
+ if (ch_damp(choffs) != 0)
+ {
+ cache.eg_rate[EG_DECAY] = 48;
+ cache.eg_rate[EG_SUSTAIN] = 63;
+ cache.eg_rate[EG_RELEASE] = 63;
+ cache.eg_sustain = 0x80;
+ }
+}
+
+
+//-------------------------------------------------
+// effective_rate - return the effective rate,
+// clamping and applying corrections as needed
+//-------------------------------------------------
+
+uint32_t pcm_registers::effective_rate(uint32_t raw, uint32_t correction)
+{
+ // raw rates of 0 and 15 just pin to min/max
+ if (raw == 0)
+ return 0;
+ if (raw == 15)
+ return 63;
+
+ // otherwise add the correction and clamp to range
+ return clamp(raw * 4 + correction, 0, 63);
+}
+
+
+
+//*********************************************************
+// PCM CHANNEL
+//*********************************************************
+
+//-------------------------------------------------
+// pcm_channel - constructor
+//-------------------------------------------------
+
+pcm_channel::pcm_channel(pcm_engine &owner, uint32_t choffs) :
+ m_choffs(choffs),
+ m_baseaddr(0),
+ m_endpos(0),
+ m_looppos(0),
+ m_curpos(0),
+ m_nextpos(0),
+ m_lfo_counter(0),
+ m_eg_state(EG_RELEASE),
+ m_env_attenuation(0x3ff),
+ m_total_level(0x7f << 10),
+ m_format(0),
+ m_key_state(0),
+ m_regs(owner.regs()),
+ m_owner(owner)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the channel state
+//-------------------------------------------------
+
+void pcm_channel::reset()
+{
+ m_baseaddr = 0;
+ m_endpos = 0;
+ m_looppos = 0;
+ m_curpos = 0;
+ m_nextpos = 0;
+ m_lfo_counter = 0;
+ m_eg_state = EG_RELEASE;
+ m_env_attenuation = 0x3ff;
+ m_total_level = 0x7f << 10;
+ m_format = 0;
+ m_key_state = 0;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void pcm_channel::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_baseaddr);
+ state.save_restore(m_endpos);
+ state.save_restore(m_looppos);
+ state.save_restore(m_curpos);
+ state.save_restore(m_nextpos);
+ state.save_restore(m_lfo_counter);
+ state.save_restore(m_eg_state);
+ state.save_restore(m_env_attenuation);
+ state.save_restore(m_total_level);
+ state.save_restore(m_format);
+ state.save_restore(m_key_state);
+}
+
+
+//-------------------------------------------------
+// prepare - prepare for clocking
+//-------------------------------------------------
+
+bool pcm_channel::prepare()
+{
+ // cache the data
+ m_regs.cache_channel_data(m_choffs, m_cache);
+
+ // clock the key state
+ if ((m_key_state & KEY_PENDING) != 0)
+ {
+ uint8_t oldstate = m_key_state;
+ m_key_state = (m_key_state >> 1) & KEY_ON;
+ if (((oldstate ^ m_key_state) & KEY_ON) != 0)
+ {
+ if ((m_key_state & KEY_ON) != 0)
+ start_attack();
+ else
+ start_release();
+ }
+ }
+
+ // set the total level directly if not interpolating
+ if (m_regs.ch_level_direct(m_choffs))
+ m_total_level = m_cache.total_level;
+
+ // we're active until we're quiet after the release
+ return (m_eg_state < EG_RELEASE || m_env_attenuation < EG_QUIET);
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+void pcm_channel::clock(uint32_t env_counter)
+{
+ // clock the LFO, which is an x.18 value incremented based on the
+ // LFO speed value
+ m_lfo_counter += m_cache.lfo_step;
+
+ // clock the envelope
+ clock_envelope(env_counter);
+
+ // determine the step after applying vibrato
+ uint32_t step = m_cache.step;
+ if (m_cache.pm_depth != 0)
+ {
+ // shift the LFO by 1/4 cycle for PM so that it starts at 0
+ uint32_t lfo_shifted = m_lfo_counter + (1 << 16);
+ int32_t lfo_value = bitfield(lfo_shifted, 10, 7);
+ if (bitfield(lfo_shifted, 17) != 0)
+ lfo_value ^= 0x7f;
+ lfo_value -= 0x40;
+ step += (lfo_value * int32_t(m_cache.pm_depth)) >> 7;
+ }
+
+ // advance the sample step and loop as needed
+ m_curpos = m_nextpos;
+ m_nextpos = m_curpos + step;
+ if (m_nextpos >= m_endpos)
+ m_nextpos += m_looppos - m_endpos;
+
+ // interpolate total level if needed
+ if (m_total_level != m_cache.total_level)
+ {
+ // max->min volume takes 156.4ms, or pretty close to 19/1024 per 44.1kHz sample
+ // min->max volume is half that, so advance by 38/1024 per sample
+ if (m_total_level < m_cache.total_level)
+ m_total_level = std::min<int32_t>(m_total_level + 19, m_cache.total_level);
+ else
+ m_total_level = std::max<int32_t>(m_total_level - 38, m_cache.total_level);
+ }
+}
+
+
+//-------------------------------------------------
+// output - return the computed output value, with
+// panning applied
+//-------------------------------------------------
+
+void pcm_channel::output(output_data &output) const
+{
+ // early out if the envelope is effectively off
+ uint32_t envelope = m_env_attenuation;
+ if (envelope > EG_QUIET)
+ return;
+
+ // add in LFO AM modulation
+ if (m_cache.am_depth != 0)
+ {
+ uint32_t lfo_value = bitfield(m_lfo_counter, 10, 7);
+ if (bitfield(m_lfo_counter, 17) != 0)
+ lfo_value ^= 0x7f;
+ envelope += (lfo_value * m_cache.am_depth) >> 7;
+ }
+
+ // add in the current interpolated total level value, which is a .10
+ // value shifted left by 2
+ envelope += m_total_level >> 8;
+
+ // add in panning effect and clamp
+ uint32_t lenv = std::min<uint32_t>(envelope + m_cache.pan_left, 0x3ff);
+ uint32_t renv = std::min<uint32_t>(envelope + m_cache.pan_right, 0x3ff);
+
+ // convert to volume as a .11 fraction
+ int32_t lvol = attenuation_to_volume(lenv << 2);
+ int32_t rvol = attenuation_to_volume(renv << 2);
+
+ // fetch current sample and add
+ int16_t sample = fetch_sample();
+ uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2;
+ output.data[outnum + 0] += (lvol * sample) >> 15;
+ output.data[outnum + 1] += (rvol * sample) >> 15;
+}
+
+
+//-------------------------------------------------
+// keyonoff - signal key on/off
+//-------------------------------------------------
+
+void pcm_channel::keyonoff(bool on)
+{
+ // mark the key state as pending
+ m_key_state |= KEY_PENDING | (on ? KEY_PENDING_ON : 0);
+
+ // don't log masked channels
+ if ((m_key_state & (KEY_PENDING_ON | KEY_ON)) == KEY_PENDING_ON && ((debug::GLOBAL_PCM_CHANNEL_MASK >> m_choffs) & 1) != 0)
+ {
+ debug::log_keyon("KeyOn PCM-%02d: num=%3d oct=%2d fnum=%03X level=%02X%c ADSR=%X/%X/%X/%X SL=%X",
+ m_choffs,
+ m_regs.ch_wave_table_num(m_choffs),
+ int8_t(m_regs.ch_octave(m_choffs) << 4) >> 4,
+ m_regs.ch_fnumber(m_choffs),
+ m_regs.ch_total_level(m_choffs),
+ m_regs.ch_level_direct(m_choffs) ? '!' : '/',
+ m_regs.ch_attack_rate(m_choffs),
+ m_regs.ch_decay_rate(m_choffs),
+ m_regs.ch_sustain_rate(m_choffs),
+ m_regs.ch_release_rate(m_choffs),
+ m_regs.ch_sustain_level(m_choffs));
+
+ if (m_regs.ch_rate_correction(m_choffs) != 15)
+ debug::log_keyon(" RC=%X", m_regs.ch_rate_correction(m_choffs));
+
+ if (m_regs.ch_pseudo_reverb(m_choffs) != 0)
+ debug::log_keyon(" %s", "REV");
+ if (m_regs.ch_damp(m_choffs) != 0)
+ debug::log_keyon(" %s", "DAMP");
+
+ if (m_regs.ch_vibrato(m_choffs) != 0 || m_regs.ch_am_depth(m_choffs) != 0)
+ {
+ if (m_regs.ch_vibrato(m_choffs) != 0)
+ debug::log_keyon(" VIB=%d", m_regs.ch_vibrato(m_choffs));
+ if (m_regs.ch_am_depth(m_choffs) != 0)
+ debug::log_keyon(" AM=%d", m_regs.ch_am_depth(m_choffs));
+ debug::log_keyon(" LFO=%d", m_regs.ch_lfo_speed(m_choffs));
+ }
+ debug::log_keyon("%s", "\n");
+ }
+}
+
+
+//-------------------------------------------------
+// load_wavetable - load a wavetable by fetching
+// its data from external memory
+//-------------------------------------------------
+
+void pcm_channel::load_wavetable()
+{
+ // determine the address of the wave table header
+ uint32_t wavnum = m_regs.ch_wave_table_num(m_choffs);
+ uint32_t wavheader = 12 * wavnum;
+
+ // above 384 it may be in a different bank
+ if (wavnum >= 384)
+ {
+ uint32_t bank = m_regs.wave_table_header();
+ if (bank != 0)
+ wavheader = 512*1024 * bank + (wavnum - 384) * 12;
+ }
+
+ // fetch the 22-bit base address and 2-bit format
+ uint8_t byte = read_pcm(wavheader + 0);
+ m_format = bitfield(byte, 6, 2);
+ m_baseaddr = bitfield(byte, 0, 6) << 16;
+ m_baseaddr |= read_pcm(wavheader + 1) << 8;
+ m_baseaddr |= read_pcm(wavheader + 2) << 0;
+
+ // fetch the 16-bit loop position
+ m_looppos = read_pcm(wavheader + 3) << 8;
+ m_looppos |= read_pcm(wavheader + 4);
+ m_looppos <<= 16;
+
+ // fetch the 16-bit end position, which is stored as a negative value
+ // for some reason that is unclear
+ m_endpos = read_pcm(wavheader + 5) << 8;
+ m_endpos |= read_pcm(wavheader + 6);
+ m_endpos = -int32_t(m_endpos) << 16;
+
+ // remaining data values set registers
+ m_owner.write(0x80 + m_choffs, read_pcm(wavheader + 7));
+ m_owner.write(0x98 + m_choffs, read_pcm(wavheader + 8));
+ m_owner.write(0xb0 + m_choffs, read_pcm(wavheader + 9));
+ m_owner.write(0xc8 + m_choffs, read_pcm(wavheader + 10));
+ m_owner.write(0xe0 + m_choffs, read_pcm(wavheader + 11));
+
+ // reset the envelope so we don't continue playing mid-sample from previous key ons
+ m_env_attenuation = 0x3ff;
+}
+
+
+//-------------------------------------------------
+// read_pcm - read a byte from the external PCM
+// memory interface
+//-------------------------------------------------
+
+uint8_t pcm_channel::read_pcm(uint32_t address) const
+{
+ return m_owner.intf().ymfm_external_read(ACCESS_PCM, address);
+}
+
+
+//-------------------------------------------------
+// start_attack - start the attack phase
+//-------------------------------------------------
+
+void pcm_channel::start_attack()
+{
+ // don't change anything if already in attack state
+ if (m_eg_state == EG_ATTACK)
+ return;
+ m_eg_state = EG_ATTACK;
+
+ // reset the LFO if requested
+ if (m_regs.ch_lfo_reset(m_choffs))
+ m_lfo_counter = 0;
+
+ // if the attack rate == 63 then immediately go to max attenuation
+ if (m_cache.eg_rate[EG_ATTACK] == 63)
+ m_env_attenuation = 0;
+
+ // reset the positions
+ m_curpos = m_nextpos = 0;
+}
+
+
+//-------------------------------------------------
+// start_release - start the release phase
+//-------------------------------------------------
+
+void pcm_channel::start_release()
+{
+ // don't change anything if already in release or reverb state
+ if (m_eg_state >= EG_RELEASE)
+ return;
+ m_eg_state = EG_RELEASE;
+}
+
+
+//-------------------------------------------------
+// clock_envelope - clock the envelope generator
+//-------------------------------------------------
+
+void pcm_channel::clock_envelope(uint32_t env_counter)
+{
+ // handle attack->decay transitions
+ if (m_eg_state == EG_ATTACK && m_env_attenuation == 0)
+ m_eg_state = EG_DECAY;
+
+ // handle decay->sustain transitions
+ if (m_eg_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain)
+ m_eg_state = EG_SUSTAIN;
+
+ // fetch the appropriate 6-bit rate value from the cache
+ uint32_t rate = m_cache.eg_rate[m_eg_state];
+
+ // compute the rate shift value; this is the shift needed to
+ // apply to the env_counter such that it becomes a 5.11 fixed
+ // point number
+ uint32_t rate_shift = rate >> 2;
+ env_counter <<= rate_shift;
+
+ // see if the fractional part is 0; if not, it's not time to clock
+ if (bitfield(env_counter, 0, 11) != 0)
+ return;
+
+ // determine the increment based on the non-fractional part of env_counter
+ uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3);
+ uint32_t increment = attenuation_increment(rate, relevant_bits);
+
+ // attack is the only one that increases
+ if (m_eg_state == EG_ATTACK)
+ m_env_attenuation += (~m_env_attenuation * increment) >> 4;
+
+ // all other cases are similar
+ else
+ {
+ // apply the increment
+ m_env_attenuation += increment;
+
+ // clamp the final attenuation
+ if (m_env_attenuation >= 0x400)
+ m_env_attenuation = 0x3ff;
+
+ // transition to reverb at -18dB if enabled
+ if (m_env_attenuation >= 0xc0 && m_eg_state < EG_REVERB && m_regs.ch_pseudo_reverb(m_choffs))
+ m_eg_state = EG_REVERB;
+ }
+}
+
+
+//-------------------------------------------------
+// fetch_sample - fetch a sample at the current
+// position
+//-------------------------------------------------
+
+int16_t pcm_channel::fetch_sample() const
+{
+ uint32_t addr = m_baseaddr;
+ uint32_t pos = m_curpos >> 16;
+
+ // 8-bit PCM: shift up by 8
+ if (m_format == 0)
+ return read_pcm(addr + pos) << 8;
+
+ // 16-bit PCM: assemble from 2 halves
+ if (m_format == 2)
+ {
+ addr += pos * 2;
+ return (read_pcm(addr) << 8) | read_pcm(addr + 1);
+ }
+
+ // 12-bit PCM: assemble out of half of 3 bytes
+ addr += (pos / 2) * 3;
+ if ((pos & 1) == 0)
+ return (read_pcm(addr + 0) << 8) | ((read_pcm(addr + 1) << 4) & 0xf0);
+ else
+ return (read_pcm(addr + 2) << 8) | ((read_pcm(addr + 1) << 0) & 0xf0);
+}
+
+
+
+//*********************************************************
+// PCM ENGINE
+//*********************************************************
+
+//-------------------------------------------------
+// pcm_engine - constructor
+//-------------------------------------------------
+
+pcm_engine::pcm_engine(ymfm_interface &intf) :
+ m_intf(intf),
+ m_env_counter(0),
+ m_modified_channels(ALL_CHANNELS),
+ m_active_channels(ALL_CHANNELS)
+{
+ // create the channels
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ m_channel[chnum] = std::make_unique<pcm_channel>(*this, chnum);
+}
+
+
+//-------------------------------------------------
+// reset - reset the engine state
+//-------------------------------------------------
+
+void pcm_engine::reset()
+{
+ // reset register state
+ m_regs.reset();
+
+ // reset each channel
+ for (auto &chan : m_channel)
+ chan->reset();
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void pcm_engine::save_restore(ymfm_saved_state &state)
+{
+ // save our data
+ state.save_restore(m_env_counter);
+
+ // save channel state
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ m_channel[chnum]->save_restore(state);
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+void pcm_engine::clock(uint32_t chanmask)
+{
+ // if something was modified, prepare
+ // also prepare every 4k samples to catch ending notes
+ if (m_modified_channels != 0 || m_prepare_count++ >= 4096)
+ {
+ // call each channel to prepare
+ m_active_channels = 0;
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ if (m_channel[chnum]->prepare())
+ m_active_channels |= 1 << chnum;
+
+ // reset the modified channels and prepare count
+ m_modified_channels = m_prepare_count = 0;
+ }
+
+ // increment the envelope counter; the envelope generator
+ // only clocks every other sample in order to make the PCM
+ // envelopes line up with the FM envelopes (after taking into
+ // account the different FM sampling rate)
+ m_env_counter++;
+
+ // now update the state of all the channels and operators
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ m_channel[chnum]->clock(m_env_counter >> 1);
+}
+
+
+//-------------------------------------------------
+// update - master update function
+//-------------------------------------------------
+
+void pcm_engine::output(output_data &output, uint32_t chanmask)
+{
+ // mask out some channels for debug purposes
+ chanmask &= debug::GLOBAL_PCM_CHANNEL_MASK;
+
+ // compute the output of each channel
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ if (bitfield(chanmask, chnum))
+ m_channel[chnum]->output(output);
+}
+
+
+//-------------------------------------------------
+// read - handle reads from the PCM registers
+//-------------------------------------------------
+
+uint8_t pcm_engine::read(uint32_t regnum)
+{
+ // handle reads from the data register
+ if (regnum == 0x06 && m_regs.memory_access_mode() != 0)
+ return m_intf.ymfm_external_read(ACCESS_PCM, m_regs.memory_address_autoinc());
+
+ return m_regs.read(regnum);
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the PCM registers
+//-------------------------------------------------
+
+void pcm_engine::write(uint32_t regnum, uint8_t data)
+{
+ // handle reads to the data register
+ if (regnum == 0x06 && m_regs.memory_access_mode() != 0)
+ {
+ m_intf.ymfm_external_write(ACCESS_PCM, m_regs.memory_address_autoinc(), data);
+ return;
+ }
+
+ // for now just mark all channels as modified
+ m_modified_channels = ALL_CHANNELS;
+
+ // most writes are passive, consumed only when needed
+ m_regs.write(regnum, data);
+
+ // however, process keyons immediately
+ if (regnum >= 0x68 && regnum <= 0x7f)
+ m_channel[regnum - 0x68]->keyonoff(bitfield(data, 7));
+
+ // and also wavetable writes
+ else if (regnum >= 0x08 && regnum <= 0x1f)
+ m_channel[regnum - 0x08]->load_wavetable();
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_pcm.h b/3rdparty/ymfm/src/ymfm_pcm.h
new file mode 100644
index 00000000000..b471fa611a6
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_pcm.h
@@ -0,0 +1,347 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_PCM_H
+#define YMFM_PCM_H
+
+#pragma once
+
+#include "ymfm.h"
+
+namespace ymfm
+{
+
+/*
+Note to self: Sega "Multi-PCM" is almost identical to this
+
+28 channels
+
+Writes:
+00 = data reg, causes write
+01 = target slot = data - (data / 8)
+02 = address (clamped to 7)
+
+Slot data (registers with ADSR/KSR seem to be inaccessible):
+0: xxxx---- panpot
+1: xxxxxxxx wavetable low
+2: xxxxxx-- pitch low
+ -------x wavetable high
+3: xxxx---- octave
+ ----xxxx pitch hi
+4: x------- key on
+5: xxxxxxx- total level
+ -------x level direct (0=interpolate)
+6: --xxx--- LFO frequency
+ -----xxx PM sensitivity
+7: -----xxx AM sensitivity
+
+Sample data:
++00: start hi
++01: start mid
++02: start low
++03: loop hi
++04: loop low
++05: -end hi
++06: -end low
++07: vibrato (reg 6)
++08: attack/decay
++09: sustain level/rate
++0A: ksr/release
++0B: LFO amplitude (reg 7)
+
+*/
+
+//*********************************************************
+// INTERFACE CLASSES
+//*********************************************************
+
+class pcm_engine;
+
+
+// ======================> pcm_cache
+
+// this class holds data that is computed once at the start of clocking
+// and remains static during subsequent sound generation
+struct pcm_cache
+{
+ uint32_t step; // sample position step, as a .16 value
+ uint32_t total_level; // target total level, as a .10 value
+ uint32_t pan_left; // left panning attenuation
+ uint32_t pan_right; // right panning attenuation
+ uint32_t eg_sustain; // sustain level, shifted up to envelope values
+ uint8_t eg_rate[EG_STATES]; // envelope rate, including KSR
+ uint8_t lfo_step; // stepping value for LFO
+ uint8_t am_depth; // scale value for AM LFO
+ uint8_t pm_depth; // scale value for PM LFO
+};
+
+
+// ======================> pcm_registers
+
+//
+// PCM register map:
+//
+// System-wide registers:
+// 00-01 xxxxxxxx LSI Test
+// 02 -------x Memory access mode (0=sound gen, 1=read/write)
+// ------x- Memory type (0=ROM, 1=ROM+SRAM)
+// ---xxx-- Wave table header
+// xxx----- Device ID (=1 for YMF278B)
+// 03 --xxxxxx Memory address high
+// 04 xxxxxxxx Memory address mid
+// 05 xxxxxxxx Memory address low
+// 06 xxxxxxxx Memory data
+// F8 --xxx--- Mix control (FM_R)
+// -----xxx Mix control (FM_L)
+// F9 --xxx--- Mix control (PCM_R)
+// -----xxx Mix control (PCM_L)
+//
+// Channel-specific registers:
+// 08-1F xxxxxxxx Wave table number low
+// 20-37 -------x Wave table number high
+// xxxxxxx- F-number low
+// 38-4F -----xxx F-number high
+// ----x--- Pseudo-reverb
+// xxxx---- Octave
+// 50-67 xxxxxxx- Total level
+// -------x Level direct
+// 68-7F x------- Key on
+// -x------ Damp
+// --x----- LFO reset
+// ---x---- Output channel
+// ----xxxx Panpot
+// 80-97 --xxx--- LFO speed
+// -----xxx Vibrato
+// 98-AF xxxx---- Attack rate
+// ----xxxx Decay rate
+// B0-C7 xxxx---- Sustain level
+// ----xxxx Sustain rate
+// C8-DF xxxx---- Rate correction
+// ----xxxx Release rate
+// E0-F7 -----xxx AM depth
+
+class pcm_registers
+{
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = 4;
+ static constexpr uint32_t CHANNELS = 24;
+ static constexpr uint32_t REGISTERS = 0x100;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+
+ // constructor
+ pcm_registers() { }
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // reset to initial state
+ void reset();
+
+ // update cache information
+ void cache_channel_data(uint32_t choffs, pcm_cache &cache);
+
+ // direct read/write access
+ uint8_t read(uint32_t index ) { return m_regdata[index]; }
+ void write(uint32_t index, uint8_t data) { m_regdata[index] = data; }
+
+ // system-wide registers
+ uint32_t memory_access_mode() const { return bitfield(m_regdata[0x02], 0); }
+ uint32_t memory_type() const { return bitfield(m_regdata[0x02], 1); }
+ uint32_t wave_table_header() const { return bitfield(m_regdata[0x02], 2, 3); }
+ uint32_t device_id() const { return bitfield(m_regdata[0x02], 5, 3); }
+ uint32_t memory_address() const { return (bitfield(m_regdata[0x03], 0, 6) << 16) | (m_regdata[0x04] << 8) | m_regdata[0x05]; }
+ uint32_t memory_data() const { return m_regdata[0x06]; }
+ uint32_t mix_fm_r() const { return bitfield(m_regdata[0xf8], 3, 3); }
+ uint32_t mix_fm_l() const { return bitfield(m_regdata[0xf8], 0, 3); }
+ uint32_t mix_pcm_r() const { return bitfield(m_regdata[0xf9], 3, 3); }
+ uint32_t mix_pcm_l() const { return bitfield(m_regdata[0xf9], 0, 3); }
+
+ // per-channel registers
+ uint32_t ch_wave_table_num(uint32_t choffs) const { return m_regdata[choffs + 0x08] | (bitfield(m_regdata[choffs + 0x20], 0) << 8); }
+ uint32_t ch_fnumber(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x20], 1, 7) | (bitfield(m_regdata[choffs + 0x38], 0, 3) << 7); }
+ uint32_t ch_pseudo_reverb(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 3); }
+ uint32_t ch_octave(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 4, 4); }
+ uint32_t ch_total_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 1, 7); }
+ uint32_t ch_level_direct(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 0); }
+ uint32_t ch_keyon(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 7); }
+ uint32_t ch_damp(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 6); }
+ uint32_t ch_lfo_reset(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 5); }
+ uint32_t ch_output_channel(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 4); }
+ uint32_t ch_panpot(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 0, 4); }
+ uint32_t ch_lfo_speed(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 3, 3); }
+ uint32_t ch_vibrato(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 0, 3); }
+ uint32_t ch_attack_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 4, 4); }
+ uint32_t ch_decay_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 0, 4); }
+ uint32_t ch_sustain_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 4, 4); }
+ uint32_t ch_sustain_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 0, 4); }
+ uint32_t ch_rate_correction(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 4, 4); }
+ uint32_t ch_release_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 0, 4); }
+ uint32_t ch_am_depth(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xe0], 0, 3); }
+
+ // return the memory address and increment it
+ uint32_t memory_address_autoinc()
+ {
+ uint32_t result = memory_address();
+ uint32_t newval = result + 1;
+ m_regdata[0x05] = newval >> 0;
+ m_regdata[0x04] = newval >> 8;
+ m_regdata[0x03] = (newval >> 16) & 0x3f;
+ return result;
+ }
+
+private:
+ // internal helpers
+ uint32_t effective_rate(uint32_t raw, uint32_t correction);
+
+ // internal state
+ uint8_t m_regdata[REGISTERS]; // register data
+};
+
+
+// ======================> pcm_channel
+
+class pcm_channel
+{
+ static constexpr uint8_t KEY_ON = 0x01;
+ static constexpr uint8_t KEY_PENDING_ON = 0x02;
+ static constexpr uint8_t KEY_PENDING = 0x04;
+
+ // "quiet" value, used to optimize when we can skip doing working
+ static constexpr uint32_t EG_QUIET = 0x200;
+
+public:
+ using output_data = ymfm_output<pcm_registers::OUTPUTS>;
+
+ // constructor
+ pcm_channel(pcm_engine &owner, uint32_t choffs);
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // reset the channel state
+ void reset();
+
+ // return the channel offset
+ uint32_t choffs() const { return m_choffs; }
+
+ // prepare prior to clocking
+ bool prepare();
+
+ // master clocking function
+ void clock(uint32_t env_counter);
+
+ // return the computed output value, with panning applied
+ void output(output_data &output) const;
+
+ // signal key on/off
+ void keyonoff(bool on);
+
+ // load a new wavetable entry
+ void load_wavetable();
+
+private:
+ // internal helpers
+ void start_attack();
+ void start_release();
+ void clock_envelope(uint32_t env_counter);
+ int16_t fetch_sample() const;
+ uint8_t read_pcm(uint32_t address) const;
+
+ // internal state
+ uint32_t const m_choffs; // channel offset
+ uint32_t m_baseaddr; // base address
+ uint32_t m_endpos; // ending position
+ uint32_t m_looppos; // loop position
+ uint32_t m_curpos; // current position
+ uint32_t m_nextpos; // next position
+ uint32_t m_lfo_counter; // LFO counter
+ envelope_state m_eg_state; // envelope state
+ uint16_t m_env_attenuation; // computed envelope attenuation
+ uint32_t m_total_level; // total level with as 7.10 for interp
+ uint8_t m_format; // sample format
+ uint8_t m_key_state; // current key state
+ pcm_cache m_cache; // cached data
+ pcm_registers &m_regs; // reference to registers
+ pcm_engine &m_owner; // reference to our owner
+};
+
+
+// ======================> pcm_engine
+
+class pcm_engine
+{
+public:
+ static constexpr int OUTPUTS = pcm_registers::OUTPUTS;
+ static constexpr int CHANNELS = pcm_registers::CHANNELS;
+ static constexpr uint32_t ALL_CHANNELS = pcm_registers::ALL_CHANNELS;
+ using output_data = pcm_channel::output_data;
+
+ // constructor
+ pcm_engine(ymfm_interface &intf);
+
+ // reset our status
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // master clocking function
+ void clock(uint32_t chanmask);
+
+ // compute sum of channel outputs
+ void output(output_data &output, uint32_t chanmask);
+
+ // read from the PCM registers
+ uint8_t read(uint32_t regnum);
+
+ // write to the PCM registers
+ void write(uint32_t regnum, uint8_t data);
+
+ // return a reference to our interface
+ ymfm_interface &intf() { return m_intf; }
+
+ // return a reference to our registers
+ pcm_registers &regs() { return m_regs; }
+
+private:
+ // internal state
+ ymfm_interface &m_intf; // reference to the interface
+ uint32_t m_env_counter; // envelope counter
+ uint32_t m_modified_channels; // bitmask of modified channels
+ uint32_t m_active_channels; // bitmask of active channels
+ uint32_t m_prepare_count; // counter to do periodic prepare sweeps
+ std::unique_ptr<pcm_channel> m_channel[CHANNELS]; // array of channels
+ pcm_registers m_regs; // registers
+};
+
+}
+
+#endif // YMFM_PCM_H
diff --git a/3rdparty/ymfm/src/ymfm_ssg.cpp b/3rdparty/ymfm/src/ymfm_ssg.cpp
new file mode 100644
index 00000000000..1c477d0de0c
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_ssg.cpp
@@ -0,0 +1,279 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "ymfm_ssg.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// SSG REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// reset - reset the register state
+//-------------------------------------------------
+
+void ssg_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ssg_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_regdata);
+}
+
+
+
+//*********************************************************
+// SSG ENGINE
+//*********************************************************
+
+//-------------------------------------------------
+// ssg_engine - constructor
+//-------------------------------------------------
+
+ssg_engine::ssg_engine(ymfm_interface &intf) :
+ m_intf(intf),
+ m_tone_count{ 0,0,0 },
+ m_tone_state{ 0,0,0 },
+ m_envelope_count(0),
+ m_envelope_state(0),
+ m_noise_count(0),
+ m_noise_state(1),
+ m_override(nullptr)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the engine state
+//-------------------------------------------------
+
+void ssg_engine::reset()
+{
+ // defer to the override if present
+ if (m_override != nullptr)
+ return m_override->ssg_reset();
+
+ // reset register state
+ m_regs.reset();
+
+ // reset engine state
+ for (int chan = 0; chan < 3; chan++)
+ {
+ m_tone_count[chan] = 0;
+ m_tone_state[chan] = 0;
+ }
+ m_envelope_count = 0;
+ m_envelope_state = 0;
+ m_noise_count = 0;
+ m_noise_state = 1;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ssg_engine::save_restore(ymfm_saved_state &state)
+{
+ // save register state
+ m_regs.save_restore(state);
+
+ // save engine state
+ state.save_restore(m_tone_count);
+ state.save_restore(m_tone_state);
+ state.save_restore(m_envelope_count);
+ state.save_restore(m_envelope_state);
+ state.save_restore(m_noise_count);
+ state.save_restore(m_noise_state);
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+void ssg_engine::clock()
+{
+ // clock tones; tone period units are clock/16 but since we run at clock/8
+ // that works out for us to toggle the state (50% duty cycle) at twice the
+ // programmed period
+ for (int chan = 0; chan < 3; chan++)
+ {
+ m_tone_count[chan]++;
+ if (m_tone_count[chan] >= m_regs.ch_tone_period(chan))
+ {
+ m_tone_state[chan] ^= 1;
+ m_tone_count[chan] = 0;
+ }
+ }
+
+ // clock noise; noise period units are clock/16 but since we run at clock/8,
+ // our counter needs a right shift prior to compare; note that a period of 0
+ // should produce an indentical result to a period of 1, so add a special
+ // check against that case
+ m_noise_count++;
+ if ((m_noise_count >> 1) >= m_regs.noise_period() && m_noise_count != 1)
+ {
+ m_noise_state ^= (bitfield(m_noise_state, 0) ^ bitfield(m_noise_state, 3)) << 17;
+ m_noise_state >>= 1;
+ m_noise_count = 0;
+ }
+
+ // clock envelope; envelope period units are clock/8 (manual says clock/256
+ // but that's for all 32 steps)
+ m_envelope_count++;
+ if (m_envelope_count >= m_regs.envelope_period())
+ {
+ m_envelope_state++;
+ m_envelope_count = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// output - output the current state
+//-------------------------------------------------
+
+void ssg_engine::output(output_data &output)
+{
+ // volume to amplitude table, taken from MAME's implementation but biased
+ // so that 0 == 0
+ static int16_t const s_amplitudes[32] =
+ {
+ 0, 32, 78, 141, 178, 222, 262, 306,
+ 369, 441, 509, 585, 701, 836, 965, 1112,
+ 1334, 1595, 1853, 2146, 2576, 3081, 3576, 4135,
+ 5000, 6006, 7023, 8155, 9963,11976,14132,16382
+ };
+
+ // compute the envelope volume
+ uint32_t envelope_volume;
+ if ((m_regs.envelope_hold() | (m_regs.envelope_continue() ^ 1)) && m_envelope_state >= 32)
+ {
+ m_envelope_state = 32;
+ envelope_volume = ((m_regs.envelope_attack() ^ m_regs.envelope_alternate()) & m_regs.envelope_continue()) ? 31 : 0;
+ }
+ else
+ {
+ uint32_t attack = m_regs.envelope_attack();
+ if (m_regs.envelope_alternate())
+ attack ^= bitfield(m_envelope_state, 5);
+ envelope_volume = (m_envelope_state & 31) ^ (attack ? 0 : 31);
+ }
+
+ // iterate over channels
+ for (int chan = 0; chan < 3; chan++)
+ {
+ // noise depends on the noise state, which is the LSB of m_noise_state
+ uint32_t noise_on = m_regs.ch_noise_enable_n(chan) | m_noise_state;
+
+ // tone depends on the current tone state
+ uint32_t tone_on = m_regs.ch_tone_enable_n(chan) | m_tone_state[chan];
+
+ // if neither tone nor noise enabled, return 0
+ uint32_t volume;
+ if ((noise_on & tone_on) == 0)
+ volume = 0;
+
+ // if the envelope is enabled, use its amplitude
+ else if (m_regs.ch_envelope_enable(chan))
+ volume = envelope_volume;
+
+ // otherwise, scale the tone amplitude up to match envelope values
+ // according to the datasheet, amplitude 15 maps to envelope 31
+ else
+ {
+ volume = m_regs.ch_amplitude(chan) * 2;
+ if (volume != 0)
+ volume |= 1;
+ }
+
+ // convert to amplitude
+ output.data[chan] = s_amplitudes[volume];
+ }
+}
+
+
+//-------------------------------------------------
+// read - handle reads from the SSG registers
+//-------------------------------------------------
+
+uint8_t ssg_engine::read(uint32_t regnum)
+{
+ // defer to the override if present
+ if (m_override != nullptr)
+ return m_override->ssg_read(regnum);
+
+ // read from the I/O ports call the handlers if they are configured for input
+ if (regnum == 0x0e && !m_regs.io_a_out())
+ return m_intf.ymfm_external_read(ACCESS_IO, 0);
+ else if (regnum == 0x0f && !m_regs.io_b_out())
+ return m_intf.ymfm_external_read(ACCESS_IO, 1);
+
+ // otherwise just return the register value
+ return m_regs.read(regnum);
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the SSG registers
+//-------------------------------------------------
+
+void ssg_engine::write(uint32_t regnum, uint8_t data)
+{
+ // defer to the override if present
+ if (m_override != nullptr)
+ return m_override->ssg_write(regnum, data);
+
+ // store the raw value to the register array;
+ // most writes are passive, consumed only when needed
+ m_regs.write(regnum, data);
+
+ // writes to the envelope shape register reset the state
+ if (regnum == 0x0d)
+ m_envelope_state = 0;
+
+ // writes to the I/O ports call the handlers if they are configured for output
+ else if (regnum == 0x0e && m_regs.io_a_out())
+ m_intf.ymfm_external_write(ACCESS_IO, 0, data);
+ else if (regnum == 0x0f && m_regs.io_b_out())
+ m_intf.ymfm_external_write(ACCESS_IO, 1, data);
+}
+
+}
diff --git a/3rdparty/ymfm/src/ymfm_ssg.h b/3rdparty/ymfm/src/ymfm_ssg.h
new file mode 100644
index 00000000000..cb7ec9e7c41
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_ssg.h
@@ -0,0 +1,207 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_SSG_H
+#define YMFM_SSG_H
+
+#pragma once
+
+#include "ymfm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// OVERRIDE INTERFACE
+//*********************************************************
+
+// ======================> ssg_override
+
+// this class describes a simple interface to allow the internal SSG to be
+// overridden with another implementation
+class ssg_override
+{
+public:
+ virtual ~ssg_override() = default;
+
+ // reset our status
+ virtual void ssg_reset() = 0;
+
+ // read/write to the SSG registers
+ virtual uint8_t ssg_read(uint32_t regnum) = 0;
+ virtual void ssg_write(uint32_t regnum, uint8_t data) = 0;
+
+ // notification when the prescale has changed
+ virtual void ssg_prescale_changed() = 0;
+};
+
+
+//*********************************************************
+// REGISTER CLASS
+//*********************************************************
+
+// ======================> ssg_registers
+
+//
+// SSG register map:
+//
+// System-wide registers:
+// 06 ---xxxxx Noise period
+// 07 x------- I/O B in(0) or out(1)
+// -x------ I/O A in(0) or out(1)
+// --x----- Noise enable(0) or disable(1) for channel C
+// ---x---- Noise enable(0) or disable(1) for channel B
+// ----x--- Noise enable(0) or disable(1) for channel A
+// -----x-- Tone enable(0) or disable(1) for channel C
+// ------x- Tone enable(0) or disable(1) for channel B
+// -------x Tone enable(0) or disable(1) for channel A
+// 0B xxxxxxxx Envelope period fine
+// 0C xxxxxxxx Envelope period coarse
+// 0D ----x--- Envelope shape: continue
+// -----x-- Envelope shape: attack/decay
+// ------x- Envelope shape: alternate
+// -------x Envelope shape: hold
+// 0E xxxxxxxx 8-bit parallel I/O port A
+// 0F xxxxxxxx 8-bit parallel I/O port B
+//
+// Per-channel registers:
+// 00,02,04 xxxxxxxx Tone period (fine) for channel A,B,C
+// 01,03,05 ----xxxx Tone period (coarse) for channel A,B,C
+// 08,09,0A ---x---- Mode: fixed(0) or variable(1) for channel A,B,C
+// ----xxxx Amplitude for channel A,B,C
+//
+class ssg_registers
+{
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = 3;
+ static constexpr uint32_t CHANNELS = 3;
+ static constexpr uint32_t REGISTERS = 0x10;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+
+ // constructor
+ ssg_registers() { }
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // direct read/write access
+ uint8_t read(uint32_t index) { return m_regdata[index]; }
+ void write(uint32_t index, uint8_t data) { m_regdata[index] = data; }
+
+ // system-wide registers
+ uint32_t noise_period() const { return bitfield(m_regdata[0x06], 0, 5); }
+ uint32_t io_b_out() const { return bitfield(m_regdata[0x07], 7); }
+ uint32_t io_a_out() const { return bitfield(m_regdata[0x07], 6); }
+ uint32_t envelope_period() const { return m_regdata[0x0b] | (m_regdata[0x0c] << 8); }
+ uint32_t envelope_continue() const { return bitfield(m_regdata[0x0d], 3); }
+ uint32_t envelope_attack() const { return bitfield(m_regdata[0x0d], 2); }
+ uint32_t envelope_alternate() const { return bitfield(m_regdata[0x0d], 1); }
+ uint32_t envelope_hold() const { return bitfield(m_regdata[0x0d], 0); }
+ uint32_t io_a_data() const { return m_regdata[0x0e]; }
+ uint32_t io_b_data() const { return m_regdata[0x0f]; }
+
+ // per-channel registers
+ uint32_t ch_noise_enable_n(uint32_t choffs) const { return bitfield(m_regdata[0x07], 3 + choffs); }
+ uint32_t ch_tone_enable_n(uint32_t choffs) const { return bitfield(m_regdata[0x07], 0 + choffs); }
+ uint32_t ch_tone_period(uint32_t choffs) const { return m_regdata[0x00 + 2 * choffs] | (bitfield(m_regdata[0x01 + 2 * choffs], 0, 4) << 8); }
+ uint32_t ch_envelope_enable(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 4); }
+ uint32_t ch_amplitude(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 0, 4); }
+
+private:
+ // internal state
+ uint8_t m_regdata[REGISTERS]; // register data
+};
+
+
+// ======================> ssg_engine
+
+class ssg_engine
+{
+public:
+ static constexpr int OUTPUTS = ssg_registers::OUTPUTS;
+ static constexpr int CHANNELS = ssg_registers::CHANNELS;
+ static constexpr int CLOCK_DIVIDER = 8;
+
+ using output_data = ymfm_output<OUTPUTS>;
+
+ // constructor
+ ssg_engine(ymfm_interface &intf);
+
+ // configure an override
+ void override(ssg_override &override) { m_override = &override; }
+
+ // reset our status
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // master clocking function
+ void clock();
+
+ // compute sum of channel outputs
+ void output(output_data &output);
+
+ // read/write to the SSG registers
+ uint8_t read(uint32_t regnum);
+ void write(uint32_t regnum, uint8_t data);
+
+ // return a reference to our interface
+ ymfm_interface &intf() { return m_intf; }
+
+ // return a reference to our registers
+ ssg_registers &regs() { return m_regs; }
+
+ // true if we are overridden
+ bool overridden() const { return (m_override != nullptr); }
+
+ // indicate the prescale has changed
+ void prescale_changed() { if (m_override != nullptr) m_override->ssg_prescale_changed(); }
+
+private:
+ // internal state
+ ymfm_interface &m_intf; // reference to the interface
+ uint32_t m_tone_count[3]; // current tone counter
+ uint32_t m_tone_state[3]; // current tone state
+ uint32_t m_envelope_count; // envelope counter
+ uint32_t m_envelope_state; // envelope state
+ uint32_t m_noise_count; // current noise counter
+ uint32_t m_noise_state; // current noise state
+ ssg_registers m_regs; // registers
+ ssg_override *m_override; // override interface
+};
+
+}
+
+#endif // YMFM_SSG_H