From 52f0acb25ca8a88a8170d0c9399c682d9469fac4 Mon Sep 17 00:00:00 2001 From: Aaron Giles Date: Fri, 2 Apr 2021 10:58:04 -0700 Subject: New BSD-licensed OPL/OPLL (YM3526, YM3812, YM2413, Y8950, YMF262, etc) cores (#7869) * OPL prep. Define FAMILY constant in register classes, and use that instead of template specialization for family-specific behaviors. Expand channel masks to 32 bits. Add is_keyon() helper. * Made FM channel and operator mapping more flexible. Operators are now owned by the engine and can be dynamically assigned to channels. Register classes now provide a mapping between a linear set of operators and channels. The register data array is now a regular array instead of a vector. * Minor change for consistency. * Introduce OPL registers and create a ymopl_engine. Add support for sustain-less notes and the OPL envelope clocks (which does not divide by 3). * Moved keycode calculations into register class. Removed unnecessary recalc in phase generator. Lined up OPL frequency, feedback, algorithm, and total level. * Implement key scale level and OPL-specific LFO and phase handling. * Create new YM3526 device based on new OPL. Fix keyon and sustain behaviors. * Fix weird OPL multiple values. Clean up and further document OPL LFO. * No busy flag on OPL, so no need to do the work. Add a right shift of 1 to the output stage to line up volume with old implementation. * More compact way of representing key scale level values. * Move the KSL bitswap into the registers since it's apparently fixed in OPLL and perhaps others. * Add support for ryhthm generation in OPL. Change compute_volume to take a phase value directly, and expose operator phase. * Fix OPL timers. * Start LFSR with a non-zero value to ensure it actually starts generating for OPL. * Fix silly bug in sustain logic for OPL. Fixes a lot of previously missing sounds. * Create OPL2 engine. Add waveform support for OPL2/3 waveforms. * Wire up YM3812 to the new OPL2 engine. * Reset OPL timers when the mask is written as well. * Manage rhythm key ons separately. Fixes Wardner awfulness. * Explicitly make channel logic handle 0, 2, or 4 operators rather than checking each one for null. Also simplify the combining logic for the 2 operator case. * Reverse bit order of LFSRs to make things a little simpler. Fix OPL LFSR so that it has its full 23-bit period. * Change outputs along the path to arrays rather than stereo items. This allows for four channel output. Also add a constant for the number of outputs to the register class. * Move status register bit definitions to the registers class. Generically support the IRQ bit. * Create shared helpers for FP encode/decode/roundtrip and use them throughout. Also update TMNT to use the FP decoder. * No need to clamp when using the roundtrip. * Clear the EOS flag when execute is turned off on ADPCM-B. Fix combine_status in YM2608 to ignore previously set flags. * Add missing note_select in base class. Don't add 1 to the OPL release rate. * Move Y8950 over to new OPL engine. * Remove old y8950, along with fmopl and ymdeltat * Add updates prior to status reads for ADPCM systems. * Add status_mask and irq_reset logic into the core. Clean up documentation on family-specific registers. Includes some temporary gross debugging stuff. * Made debugging less gross by giving operators and channels a reference back to their owner. * Fix status port address in OPL chips. Reduce ADPCM volume to match previous implementation. * Fix Y8950 ADPCM start. Return masked status properly. * Initial cut at OPLL mapping. * Add YM2413 support based on ymfm; renamed vrc7snd to ds1001; added YM2423 amd YMF281 variants as well. Instrument data is now loaded via external ROMs. Added 'depress' envelope support to the core engine. Fixed a number of issues in the ymopll_engine. Documented hard-coded values. Moved register clear into register-specific reset. * Add missing identifier. * Y8950 is OPL not OPL2. * Some documentation cleanup. Consistency fixes in the register classes. * Consolidate large comments. Add support for delayed modulators for OPL. Broke out 2-op and 4-op cases to help simplify logic. Fixed overflow handling in fp encoding. * Fix silly bug. * Changed operator assignment mechanism to be more readable. Added prepare method to be called at the start of sound update. Added ALL_CHANNELS constant to register files. Updated all consumers to call prepare and use constants where applicable. * Move YMF262 and YMF278B to use new FM engine for OPL3/4. Fix several issues in OPL3 logic, which now seems to work ok. * Minor fixes. More documentation. * Fix MSVC build. * Add caching of data to prepare methods to improve performance. Moved non-register decoding logic out of the .h file and into .cpp file. Move phase_step calculation into register class. * More notes. Removed keycode from cache. Split 2/4-operator outputs into separate functions. Changed OPN/OPL to use templates for variants. Added channel/operator_offset helpers. * Fairly substantial overhaul of register interface. Register interface is now stateless and contains family-specific state. Channel and operator accessors are prefixed by ch_/op_ now and require an offset to the specific channel or operator. Moved LFO/noise generation into register class, along with keyon logging. * Add noise back to OPL/OPLL * Added early-out for low envelope. Moved waveform logic out into family-specific code. General clean up of ordering. Reduced family base class to minimal needed. * More aggressively track active channels to help performance. * Use only summing outputs for consideration of active channels. Centralize the logic of determined 4-op vs 2-op. * More conservative channel deactivation. * Add helper to compute the sample rate and use it in all implementations. Remove unneeded chnum/opnum members. * Fix error in YM2612 that caused crashes. * Switching parameters and locals to 32-bit values gives a noticeable performance boost. Checkpoint 1. * More moving to 32-bit values. Checkpoint 2. * Last of the 32-bit promotions. * Ensure SSG inverted flag is only tested on systems with SSG support in the innermost loops. * Make most constants 32-bit as well. Expose some constants via the engine class. * Expand waveforms ahead of time. Optimize the attenuation lookup a bit. * If volume is low, don't erase output, just leave it alone. Fixes missing sound in raimais. * Replace a couple of magic numbers with constants. * Normalization of FM engine usage across consumers. * Removed explicit external prepare() call; this logic is now automatically done after writes and periodically. Changed OPL/OPLL to use native formats for block/fnum. Fixed waveform generation. Fixed PM and AM depth on OPL/OPLL. * Cache multiple value. Clean up output handling a bit. * Move multiple caching ahead of phase step caching. * Fully split OPLL from OPL. Remove many hacks now that OPLL registers can have state. Created shared helpers between OPL/OPLL. Removed more aggressive channel disables since it was not rhythm friendly and didn't really buy much. * Remove old comment * Remove bad write in OPL mode case. Fixes fsoccer intro. Only call set_reset_status() once per mode call. * Remove FM output boost in YM2608/2610. Not sure why I did that. Better matches previous volume now. * Make AM/PM logging less confusing. * Let's actually set DYNAMIC_OPS properly, eh? * Improved logging. * Comment cleanups. Add constant for dynamic phase. Pre-shift sustain level. Srcclean. * Fix memory regions on YM2608 games. * Clean up ymadpcm to line up with recent ymfm changes. * y8950: Reshuffle read/write handlers. Rename them to less confusing names. * ym2413: Reshuffle read/write handlers. Rename them to less confusing names. * ym3526/ym3812: Reshuffle read/write handlers. Rename them to less confusing names. * ymf262: Match read/write details to datasheet and previous tests. * Use a constexpr function instead of macro for packing operator numbers. Pre-compute OPM LFO waveforms. * Generate OPL4 engine to support the proper clock divider and new flags. Update YMF278B to use FM timers and status rather than replicating the logic. * Fix 4-operator enable on new OPL4 instance. * Fix FM downsampling and adjust balance in YM278B. --- scripts/src/sound.lua | 49 +- src/devices/bus/bbc/1mhzbus/beebopl.h | 2 +- src/devices/bus/c64/sfx_sound_expander.h | 2 +- src/devices/bus/isa/adlib.cpp | 6 +- src/devices/bus/isa/adlib.h | 2 +- src/devices/bus/isa/sb16.h | 2 +- src/devices/bus/isa/sblaster.cpp | 8 +- src/devices/bus/isa/sblaster.h | 2 +- src/devices/bus/isa/stereo_fx.h | 2 +- src/devices/bus/msx_cart/msx_audio.cpp | 2 +- src/devices/bus/msx_cart/msx_audio.h | 2 +- src/devices/bus/nes/konami.cpp | 6 +- src/devices/bus/nes/konami.h | 2 +- src/devices/sound/262intf.cpp | 145 -- src/devices/sound/262intf.h | 50 - src/devices/sound/3526intf.cpp | 160 -- src/devices/sound/3526intf.h | 57 - src/devices/sound/3812intf.cpp | 161 -- src/devices/sound/3812intf.h | 62 - src/devices/sound/8950intf.cpp | 173 -- src/devices/sound/8950intf.h | 81 - src/devices/sound/fmopl.cpp | 2554 ------------------------ src/devices/sound/fmopl.h | 112 -- src/devices/sound/y8950.cpp | 309 +++ src/devices/sound/y8950.h | 81 + src/devices/sound/ym2151.cpp | 91 +- src/devices/sound/ym2151.h | 7 +- src/devices/sound/ym2203.cpp | 98 +- src/devices/sound/ym2203.h | 7 +- src/devices/sound/ym2413.cpp | 1839 ++---------------- src/devices/sound/ym2413.h | 237 +-- src/devices/sound/ym2608.cpp | 95 +- src/devices/sound/ym2608.h | 11 +- src/devices/sound/ym2610.cpp | 59 +- src/devices/sound/ym2610.h | 11 +- src/devices/sound/ym2612.cpp | 106 +- src/devices/sound/ym2612.h | 7 +- src/devices/sound/ym3526.cpp | 153 ++ src/devices/sound/ym3526.h | 53 + src/devices/sound/ym3812.cpp | 151 ++ src/devices/sound/ym3812.h | 53 + src/devices/sound/ymadpcm.cpp | 199 +- src/devices/sound/ymadpcm.h | 238 ++- src/devices/sound/ymdeltat.cpp | 628 ------ src/devices/sound/ymdeltat.h | 88 - src/devices/sound/ymf262.cpp | 2827 +-------------------------- src/devices/sound/ymf262.h | 75 +- src/devices/sound/ymf278b.cpp | 296 +-- src/devices/sound/ymf278b.h | 34 +- src/devices/sound/ymfm.cpp | 3125 ++++++++++++++++++++++-------- src/devices/sound/ymfm.h | 1495 +++++++++----- src/mame/audio/nichisnd.h | 2 +- src/mame/drivers/actfancr.cpp | 2 +- src/mame/drivers/aerofgt.cpp | 10 +- src/mame/drivers/alpha68k_i.cpp | 8 +- src/mame/drivers/amaticmg.cpp | 2 +- src/mame/drivers/armedf.cpp | 4 +- src/mame/drivers/battlane.cpp | 2 +- src/mame/drivers/battlnts.cpp | 2 +- src/mame/drivers/bebox.cpp | 2 +- src/mame/drivers/bigkarnk_ms.cpp | 2 +- src/mame/drivers/brkthru.cpp | 2 +- src/mame/drivers/bublbobl.cpp | 2 +- src/mame/drivers/cop01.cpp | 2 +- src/mame/drivers/crospang.cpp | 2 +- src/mame/drivers/cybertnk.cpp | 2 +- src/mame/drivers/dcon.cpp | 2 +- src/mame/drivers/dec0.cpp | 2 +- src/mame/drivers/dec8.cpp | 4 +- src/mame/drivers/deniam.cpp | 2 +- src/mame/drivers/discoboy.cpp | 2 +- src/mame/drivers/dunhuang.cpp | 4 +- src/mame/drivers/dynax.cpp | 2 +- src/mame/drivers/dynduke.cpp | 2 +- src/mame/drivers/esd16.cpp | 2 +- src/mame/drivers/exprraid.cpp | 2 +- src/mame/drivers/fantland.cpp | 2 +- src/mame/drivers/firetrap.cpp | 2 +- src/mame/drivers/fresh.cpp | 4 +- src/mame/drivers/fuukifg2.cpp | 2 +- src/mame/drivers/gaelco.cpp | 2 +- src/mame/drivers/galivan.cpp | 2 +- src/mame/drivers/galspnbl.cpp | 2 +- src/mame/drivers/ginganin.cpp | 2 +- src/mame/drivers/goodejan.cpp | 2 +- src/mame/drivers/hcastle.cpp | 2 +- src/mame/drivers/igs011.cpp | 4 +- src/mame/drivers/itech8.cpp | 2 +- src/mame/drivers/jackpot.cpp | 2 +- src/mame/drivers/karnov.cpp | 4 +- src/mame/drivers/legionna.cpp | 2 +- src/mame/drivers/lordgun.cpp | 2 +- src/mame/drivers/matmania.cpp | 2 +- src/mame/drivers/mephistp.cpp | 2 +- src/mame/drivers/metlclsh.cpp | 2 +- src/mame/drivers/missb2.cpp | 2 +- src/mame/drivers/mitchell.cpp | 10 +- src/mame/drivers/nbmj8688.cpp | 2 +- src/mame/drivers/nbmj8891.cpp | 2 +- src/mame/drivers/nbmj8900.cpp | 2 +- src/mame/drivers/nbmj8991.cpp | 2 +- src/mame/drivers/nbmj9195.cpp | 2 +- src/mame/drivers/nemesis.cpp | 2 +- src/mame/drivers/nmg5.cpp | 2 +- src/mame/drivers/nmk16.cpp | 2 +- src/mame/drivers/oneshot.cpp | 2 +- src/mame/drivers/onetwo.cpp | 6 +- src/mame/drivers/pcktgal.cpp | 2 +- src/mame/drivers/pokechmp.cpp | 2 +- src/mame/drivers/prehisle.cpp | 6 +- src/mame/drivers/r2dx_v33.cpp | 3 +- src/mame/drivers/raiden.cpp | 2 +- src/mame/drivers/raiden2.cpp | 2 +- src/mame/drivers/renegade.cpp | 2 +- src/mame/drivers/rollerg.cpp | 2 +- src/mame/drivers/sauro.cpp | 2 +- src/mame/drivers/sengokmj.cpp | 2 +- src/mame/drivers/seta.cpp | 2 +- src/mame/drivers/sidepckt.cpp | 2 +- src/mame/drivers/sigmab31.cpp | 2 +- src/mame/drivers/sigmab52.cpp | 2 +- src/mame/drivers/snk.cpp | 44 +- src/mame/drivers/snk68.cpp | 10 +- src/mame/drivers/snowbros.cpp | 2 +- src/mame/drivers/spbactn.cpp | 2 +- src/mame/drivers/spdodgeb.cpp | 2 +- src/mame/drivers/speedbal.cpp | 2 +- src/mame/drivers/splash.cpp | 2 +- src/mame/drivers/splash_ms.cpp | 2 +- src/mame/drivers/spy.cpp | 2 +- src/mame/drivers/stadhero.cpp | 2 +- src/mame/drivers/subsino.cpp | 2 +- src/mame/drivers/subsino2.cpp | 2 +- src/mame/drivers/suna16.cpp | 2 +- src/mame/drivers/suna8.cpp | 4 +- src/mame/drivers/tbowl.cpp | 2 +- src/mame/drivers/tecmo.cpp | 4 +- src/mame/drivers/tecmosys.cpp | 2 +- src/mame/drivers/terracre.cpp | 2 +- src/mame/drivers/tmnt.cpp | 21 +- src/mame/drivers/toaplan1.cpp | 7 +- src/mame/drivers/toaplan2.cpp | 6 +- src/mame/drivers/toki.cpp | 2 +- src/mame/drivers/tumbleb.cpp | 2 +- src/mame/drivers/twincobr.cpp | 2 +- src/mame/drivers/umipoker.cpp | 2 +- src/mame/drivers/unico.cpp | 10 +- src/mame/drivers/vgmplay.cpp | 8 +- src/mame/drivers/vis.cpp | 2 +- src/mame/drivers/wardner.cpp | 2 +- src/mame/drivers/yunsun16.cpp | 2 +- src/mame/drivers/yunsung8.cpp | 2 +- src/mame/includes/alpha68k.h | 2 +- src/mame/includes/bloodbro.h | 2 +- src/mame/includes/bublbobl.h | 2 +- src/mame/includes/toaplan1.h | 2 +- src/mame/machine/toaplan1.cpp | 2 +- 157 files changed, 5302 insertions(+), 11064 deletions(-) delete mode 100644 src/devices/sound/262intf.cpp delete mode 100644 src/devices/sound/262intf.h delete mode 100644 src/devices/sound/3526intf.cpp delete mode 100644 src/devices/sound/3526intf.h delete mode 100644 src/devices/sound/3812intf.cpp delete mode 100644 src/devices/sound/3812intf.h delete mode 100644 src/devices/sound/8950intf.cpp delete mode 100644 src/devices/sound/8950intf.h delete mode 100644 src/devices/sound/fmopl.cpp delete mode 100644 src/devices/sound/fmopl.h create mode 100644 src/devices/sound/y8950.cpp create mode 100644 src/devices/sound/y8950.h create mode 100644 src/devices/sound/ym3526.cpp create mode 100644 src/devices/sound/ym3526.h create mode 100644 src/devices/sound/ym3812.cpp create mode 100644 src/devices/sound/ym3812.h delete mode 100644 src/devices/sound/ymdeltat.cpp delete mode 100644 src/devices/sound/ymdeltat.h diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index bda7e45bc0c..1d643268548 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -1175,13 +1175,12 @@ end --@src/devices/sound/ym2608.h,SOUNDS["YM2608"] = true --@src/devices/sound/ym2610.h,SOUNDS["YM2610"] = true --@src/devices/sound/ym2612.h,SOUNDS["YM2612"] = true ---@src/devices/sound/3812intf.h,SOUNDS["YM3812"] = true ---@src/devices/sound/3526intf.h,SOUNDS["YM3526"] = true ---@src/devices/sound/8950intf.h,SOUNDS["Y8950"] = true +--@src/devices/sound/ym3526.h,SOUNDS["YM3526"] = true +--@src/devices/sound/ym3812.h,SOUNDS["YM3812"] = true --@src/devices/sound/ymf262.h,SOUNDS["YMF262"] = true --@src/devices/sound/ymf271.h,SOUNDS["YMF271"] = true --@src/devices/sound/ymf278b.h,SOUNDS["YMF278B"] = true ---@src/devices/sound/262intf.h,SOUNDS["YMF262"] = true +--@src/devices/sound/y8950.h,SOUNDS["Y8950"] = true --------------------------------------------------- if (SOUNDS["YM2151"]~=null) then @@ -1193,10 +1192,12 @@ if (SOUNDS["YM2151"]~=null) then } end -if (SOUNDS["YM2413"]~=null) then +if (SOUNDS["YM2413"]~=null or SOUNDS["YM2423"]~=null or SOUNDS["YMF281"]~=null or SOUNDS["DS1001"]~=null) then files { MAME_DIR .. "src/devices/sound/ym2413.cpp", MAME_DIR .. "src/devices/sound/ym2413.h", + MAME_DIR .. "src/devices/sound/ymfm.cpp", + MAME_DIR .. "src/devices/sound/ymfm.h", } end @@ -1252,34 +1253,30 @@ end if (SOUNDS["YM3812"]~=null or SOUNDS["YM3526"]~=null or SOUNDS["Y8950"]~=null) then --if (SOUNDS["YM3812"]~=null) then files { - MAME_DIR .. "src/devices/sound/3812intf.cpp", - MAME_DIR .. "src/devices/sound/3812intf.h", - MAME_DIR .. "src/devices/sound/fmopl.cpp", - MAME_DIR .. "src/devices/sound/fmopl.h", - MAME_DIR .. "src/devices/sound/ymdeltat.cpp", - MAME_DIR .. "src/devices/sound/ymdeltat.h", + MAME_DIR .. "src/devices/sound/ym3812.cpp", + MAME_DIR .. "src/devices/sound/ym3812.h", + MAME_DIR .. "src/devices/sound/ymfm.cpp", + MAME_DIR .. "src/devices/sound/ymfm.h", } --end --if (SOUNDS["YM3526"]~=null) then files { - MAME_DIR .. "src/devices/sound/3526intf.cpp", - MAME_DIR .. "src/devices/sound/3526intf.h", - MAME_DIR .. "src/devices/sound/fmopl.cpp", - MAME_DIR .. "src/devices/sound/fmopl.h", - MAME_DIR .. "src/devices/sound/ymdeltat.cpp", - MAME_DIR .. "src/devices/sound/ymdeltat.h", + MAME_DIR .. "src/devices/sound/ym3526.cpp", + MAME_DIR .. "src/devices/sound/ym3526.h", + MAME_DIR .. "src/devices/sound/ymfm.cpp", + MAME_DIR .. "src/devices/sound/ymfm.h", } --end --if (SOUNDS["Y8950"]~=null) then files { - MAME_DIR .. "src/devices/sound/8950intf.cpp", - MAME_DIR .. "src/devices/sound/8950intf.h", - MAME_DIR .. "src/devices/sound/fmopl.cpp", - MAME_DIR .. "src/devices/sound/fmopl.h", - MAME_DIR .. "src/devices/sound/ymdeltat.cpp", - MAME_DIR .. "src/devices/sound/ymdeltat.h", + MAME_DIR .. "src/devices/sound/y8950.cpp", + MAME_DIR .. "src/devices/sound/y8950.h", + MAME_DIR .. "src/devices/sound/ymfm.cpp", + MAME_DIR .. "src/devices/sound/ymfm.h", + MAME_DIR .. "src/devices/sound/ymadpcm.cpp", + MAME_DIR .. "src/devices/sound/ymadpcm.h", } --end end @@ -1288,8 +1285,8 @@ if (SOUNDS["YMF262"]~=null) then files { MAME_DIR .. "src/devices/sound/ymf262.cpp", MAME_DIR .. "src/devices/sound/ymf262.h", - MAME_DIR .. "src/devices/sound/262intf.cpp", - MAME_DIR .. "src/devices/sound/262intf.h", + MAME_DIR .. "src/devices/sound/ymfm.cpp", + MAME_DIR .. "src/devices/sound/ymfm.h", } end @@ -1304,6 +1301,8 @@ if (SOUNDS["YMF278B"]~=null) then files { MAME_DIR .. "src/devices/sound/ymf278b.cpp", MAME_DIR .. "src/devices/sound/ymf278b.h", + MAME_DIR .. "src/devices/sound/ymfm.cpp", + MAME_DIR .. "src/devices/sound/ymfm.h", } end diff --git a/src/devices/bus/bbc/1mhzbus/beebopl.h b/src/devices/bus/bbc/1mhzbus/beebopl.h index b3f9fb19fb2..47769068154 100644 --- a/src/devices/bus/bbc/1mhzbus/beebopl.h +++ b/src/devices/bus/bbc/1mhzbus/beebopl.h @@ -10,7 +10,7 @@ #define MAME_BUS_BBC_1MHZBUS_BEEBOPL_H #include "1mhzbus.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" //************************************************************************** diff --git a/src/devices/bus/c64/sfx_sound_expander.h b/src/devices/bus/c64/sfx_sound_expander.h index 9380e055c18..4edb65656af 100644 --- a/src/devices/bus/c64/sfx_sound_expander.h +++ b/src/devices/bus/c64/sfx_sound_expander.h @@ -12,7 +12,7 @@ #pragma once #include "exp.h" -#include "sound/3526intf.h" +#include "sound/ym3526.h" diff --git a/src/devices/bus/isa/adlib.cpp b/src/devices/bus/isa/adlib.cpp index 0fbd0f6ef81..0f859a0ebbd 100644 --- a/src/devices/bus/isa/adlib.cpp +++ b/src/devices/bus/isa/adlib.cpp @@ -20,7 +20,7 @@ uint8_t isa8_adlib_device::ym3812_16_r(offs_t offset) uint8_t retVal = 0xff; switch(offset) { - case 0 : retVal = m_ym3812->status_port_r(); break; + case 0 : retVal = m_ym3812->status_r(); break; } return retVal; } @@ -29,8 +29,8 @@ void isa8_adlib_device::ym3812_16_w(offs_t offset, uint8_t data) { switch(offset) { - case 0 : m_ym3812->control_port_w(data); break; - case 1 : m_ym3812->write_port_w(data); break; + case 0 : m_ym3812->address_w(data); break; + case 1 : m_ym3812->data_w(data); break; } } diff --git a/src/devices/bus/isa/adlib.h b/src/devices/bus/isa/adlib.h index 1e99a6120f4..180eee3c141 100644 --- a/src/devices/bus/isa/adlib.h +++ b/src/devices/bus/isa/adlib.h @@ -6,7 +6,7 @@ #pragma once #include "isa.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" //************************************************************************** // TYPE DEFINITIONS diff --git a/src/devices/bus/isa/sb16.h b/src/devices/bus/isa/sb16.h index 10587131679..b64f96b4979 100644 --- a/src/devices/bus/isa/sb16.h +++ b/src/devices/bus/isa/sb16.h @@ -7,8 +7,8 @@ #include "isa.h" #include "bus/pc_joy/pc_joy.h" #include "cpu/mcs51/mcs51.h" -#include "sound/262intf.h" #include "sound/dac.h" +#include "sound/ymf262.h" //********************************************************************* // TYPE DEFINITIONS diff --git a/src/devices/bus/isa/sblaster.cpp b/src/devices/bus/isa/sblaster.cpp index af7bb302511..3242483a1b7 100644 --- a/src/devices/bus/isa/sblaster.cpp +++ b/src/devices/bus/isa/sblaster.cpp @@ -14,8 +14,8 @@ #include "sblaster.h" #include "machine/pic8259.h" -#include "sound/262intf.h" #include "sound/spkrdev.h" +#include "sound/ymf262.h" #include "speaker.h" @@ -81,7 +81,7 @@ uint8_t sb8_device::ym3812_16_r(offs_t offset) uint8_t retVal = 0xff; switch(offset) { - case 0 : retVal = m_ym3812->status_port_r(); break; + case 0 : retVal = m_ym3812->status_r(); break; } return retVal; } @@ -90,8 +90,8 @@ void sb8_device::ym3812_16_w(offs_t offset, uint8_t data) { switch(offset) { - case 0 : m_ym3812->control_port_w(data); break; - case 1 : m_ym3812->write_port_w(data); break; + case 0 : m_ym3812->address_w(data); break; + case 1 : m_ym3812->data_w(data); break; } } diff --git a/src/devices/bus/isa/sblaster.h b/src/devices/bus/isa/sblaster.h index 9bc4d5e625b..a31aa1ef33b 100644 --- a/src/devices/bus/isa/sblaster.h +++ b/src/devices/bus/isa/sblaster.h @@ -8,9 +8,9 @@ #include "isa.h" #include "bus/midi/midi.h" #include "bus/pc_joy/pc_joy.h" -#include "sound/3812intf.h" #include "sound/dac.h" #include "sound/saa1099.h" +#include "sound/ym3812.h" #include "diserial.h" //************************************************************************** diff --git a/src/devices/bus/isa/stereo_fx.h b/src/devices/bus/isa/stereo_fx.h index 0e711d9c484..bd6a208dc9e 100644 --- a/src/devices/bus/isa/stereo_fx.h +++ b/src/devices/bus/isa/stereo_fx.h @@ -8,7 +8,7 @@ #include "isa.h" #include "bus/pc_joy/pc_joy.h" #include "cpu/mcs51/mcs51.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" //********************************************************************* // TYPE DEFINITIONS diff --git a/src/devices/bus/msx_cart/msx_audio.cpp b/src/devices/bus/msx_cart/msx_audio.cpp index 924ae60e40a..3f8fd1fd772 100644 --- a/src/devices/bus/msx_cart/msx_audio.cpp +++ b/src/devices/bus/msx_cart/msx_audio.cpp @@ -154,7 +154,7 @@ void msx_cart_msx_audio_nms1205_device::device_add_mconfig(machine_config &confi m_y8950->add_route(ALL_OUTPUTS, "mono", 0.40); m_y8950->keyboard_write().set("kbdc", FUNC(msx_audio_kbdc_port_device::write)); m_y8950->keyboard_read().set("kbdc", FUNC(msx_audio_kbdc_port_device::read)); - m_y8950->irq().set(FUNC(msx_cart_msx_audio_nms1205_device::irq_write)); + m_y8950->irq_handler().set(FUNC(msx_cart_msx_audio_nms1205_device::irq_write)); MSX_AUDIO_KBDC_PORT(config, "kbdc", msx_audio_keyboards, nullptr); diff --git a/src/devices/bus/msx_cart/msx_audio.h b/src/devices/bus/msx_cart/msx_audio.h index 3aeee7cddf1..135dfa60519 100644 --- a/src/devices/bus/msx_cart/msx_audio.h +++ b/src/devices/bus/msx_cart/msx_audio.h @@ -6,7 +6,7 @@ #pragma once #include "bus/msx_cart/cartridge.h" -#include "sound/8950intf.h" +#include "sound/y8950.h" #include "machine/6850acia.h" #include "bus/midi/midi.h" diff --git a/src/devices/bus/nes/konami.cpp b/src/devices/bus/nes/konami.cpp index b936b06b48a..2f77b855205 100644 --- a/src/devices/bus/nes/konami.cpp +++ b/src/devices/bus/nes/konami.cpp @@ -679,11 +679,11 @@ void nes_konami_vrc7_device::write_h(offs_t offset, uint8_t data) case 0x1010: case 0x1018: - m_vrc7snd->register_port_w(data); + m_vrc7snd->address_w(data); break; case 0x1030: case 0x1038: - m_vrc7snd->data_port_w(data); + m_vrc7snd->data_w(data); break; case 0x2000: @@ -763,5 +763,5 @@ void nes_konami_vrc7_device::device_add_mconfig(machine_config &config) // TODO: this is not how VRC7 clock signaling works! // The board uses the CLK pin in reality, not hardcoded NTSC values! - VRC7(config, m_vrc7snd, XTAL(21'477'272)/6).add_route(0, "addon", 1.0).add_route(1, "addon", 0.0); + DS1001(config, m_vrc7snd, XTAL(21'477'272)/6).add_route(0, "addon", 1.0).add_route(1, "addon", 0.0); } diff --git a/src/devices/bus/nes/konami.h b/src/devices/bus/nes/konami.h index 86afa1832e1..1635b4707f7 100644 --- a/src/devices/bus/nes/konami.h +++ b/src/devices/bus/nes/konami.h @@ -157,7 +157,7 @@ protected: virtual void device_add_mconfig(machine_config &config) override; private: - required_device m_vrc7snd; + required_device m_vrc7snd; }; diff --git a/src/devices/sound/262intf.cpp b/src/devices/sound/262intf.cpp deleted file mode 100644 index 616cefc6377..00000000000 --- a/src/devices/sound/262intf.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ernesto Corvi -/*************************************************************************** - - 262intf.c - - MAME interface for YMF262 (OPL3) emulator - -***************************************************************************/ -#include "emu.h" -#include "262intf.h" -#include "ymf262.h" - - -/* IRQ Handler */ -void ymf262_device::irq_handler(int irq) -{ - if (!m_irq_handler.isnull()) - m_irq_handler(irq); -} - -/* Timer overflow callback from timer.c */ -void ymf262_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - switch(id) - { - case 0: - ymf262_timer_over(m_chip,0); - break; - - case 1: - ymf262_timer_over(m_chip,1); - break; - } -} - - -void ymf262_device::timer_handler(int c, const attotime &period) -{ - if( period == attotime::zero ) - { /* Reset FM Timer */ - m_timer[c]->enable(false); - } - else - { /* Start FM Timer */ - m_timer[c]->adjust(period); - } -} - - - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void ymf262_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) -{ - ymf262_update_one(m_chip, outputs); -} - -//------------------------------------------------- -// device_post_load - device-specific post load -//------------------------------------------------- -void ymf262_device::device_post_load() -{ - ymf262_post_load(m_chip); -} - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void ymf262_device::device_start() -{ - int rate = clock()/288; - - m_irq_handler.resolve(); - - /* stream system initialize */ - m_chip = ymf262_init(this,clock(),rate); - if (!m_chip) - throw emu_fatalerror("ymf262_device(%s): Error creating YMF262 chip", tag()); - - m_stream = stream_alloc(0,4,rate); - - /* YMF262 setup */ - ymf262_set_timer_handler (m_chip, &ymf262_device::static_timer_handler, this); - ymf262_set_irq_handler (m_chip, &ymf262_device::static_irq_handler, this); - ymf262_set_update_handler(m_chip, &ymf262_device::static_update_request, this); - - m_timer[0] = timer_alloc(0); - m_timer[1] = timer_alloc(1); -} - -//------------------------------------------------- -// device_stop - device-specific stop -//------------------------------------------------- - -void ymf262_device::device_stop() -{ - ymf262_shutdown(m_chip); -} - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void ymf262_device::device_reset() -{ - ymf262_reset_chip(m_chip); -} - -//------------------------------------------------- -// device_clock_changed - called if the clock -// changes -//------------------------------------------------- - -void ymf262_device::device_clock_changed() -{ - int rate = clock()/288; - ymf262_clock_changed(m_chip,clock(),rate); - m_stream->set_sample_rate(rate); -} - -u8 ymf262_device::read(offs_t offset) -{ - return ymf262_read(m_chip, offset & 3); -} - -void ymf262_device::write(offs_t offset, u8 data) -{ - ymf262_write(m_chip, offset & 3, data); -} - -DEFINE_DEVICE_TYPE(YMF262, ymf262_device, "ymf262", "YMF262 OPL3") - -ymf262_device::ymf262_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, YMF262, tag, owner, clock) - , device_sound_interface(mconfig, *this) - , m_stream(nullptr) - , m_timer{ nullptr, nullptr } - , m_chip(nullptr) - , m_irq_handler(*this) -{ -} diff --git a/src/devices/sound/262intf.h b/src/devices/sound/262intf.h deleted file mode 100644 index 6c2376fc960..00000000000 --- a/src/devices/sound/262intf.h +++ /dev/null @@ -1,50 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ernesto Corvi -#ifndef MAME_SOUND_262INTF_H -#define MAME_SOUND_262INTF_H - -#pragma once - -class ymf262_device : public device_t, public device_sound_interface -{ -public: - ymf262_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // configuration helpers - auto irq_handler() { return m_irq_handler.bind(); } - - u8 read(offs_t offset); - void write(offs_t offset, u8 data); - -protected: - // device-level overrides - virtual void device_post_load() override; - virtual void device_start() override; - virtual void device_stop() override; - virtual void device_reset() override; - virtual void device_clock_changed() override; - - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; - -private: - void irq_handler(int irq); - void timer_handler(int c, const attotime &period); - void update_request() { m_stream->update(); } - - static void static_irq_handler(device_t *param, int irq) { downcast(param)->irq_handler(irq); } - static void static_timer_handler(device_t *param, int c, const attotime &period) { downcast(param)->timer_handler(c, period); } - static void static_update_request(device_t *param, int interval) { downcast(param)->update_request(); } - - // internal state - sound_stream * m_stream; - emu_timer * m_timer[2]; - void * m_chip; - devcb_write_line m_irq_handler; -}; - -DECLARE_DEVICE_TYPE(YMF262, ymf262_device) - -#endif // MAME_SOUND_262INTF_H diff --git a/src/devices/sound/3526intf.cpp b/src/devices/sound/3526intf.cpp deleted file mode 100644 index 906c3c3fd38..00000000000 --- a/src/devices/sound/3526intf.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ernesto Corvi -/****************************************************************************** -* FILE -* Yamaha 3812 emulator interface - MAME VERSION -* -* CREATED BY -* Ernesto Corvi -* -* UPDATE LOG -* JB 28-04-2002 Fixed simultaneous usage of all three different chip types. -* Used real sample rate when resample filter is active. -* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. -* CHS 1999-01-09 Fixes new ym3812 emulation interface. -* CHS 1998-10-23 Mame streaming sound chip update -* EC 1998 Created Interface -* -* NOTES -* -******************************************************************************/ -#include "emu.h" -#include "3526intf.h" -#include "fmopl.h" - - -/* IRQ Handler */ -void ym3526_device::irq_handler(int irq) -{ - if (!m_irq_handler.isnull()) - m_irq_handler(irq); -} - -/* Timer overflow callback from timer.c */ -void ym3526_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - switch(id) - { - case 0: - ym3526_timer_over(m_chip,0); - break; - - case 1: - ym3526_timer_over(m_chip,1); - break; - } -} - -void ym3526_device::timer_handler(int c,const attotime &period) -{ - if( period == attotime::zero ) - { /* Reset FM Timer */ - m_timer[c]->enable(false); - } - else - { /* Start FM Timer */ - m_timer[c]->adjust(period); - } -} - - - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void ym3526_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) -{ - ym3526_update_one(m_chip, outputs[0]); -} - - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void ym3526_device::device_start() -{ - int rate = clock() / 72; - - // resolve callbacks - m_irq_handler.resolve(); - - /* stream system initialize */ - m_chip = ym3526_init(this, clock(), rate); - if (!m_chip) - throw emu_fatalerror("ym3526_device(%s): Error creating YM3526 chip", tag()); - - calculate_rates(); - - /* YM3526 setup */ - ym3526_set_timer_handler (m_chip, &ym3526_device::static_timer_handler, this); - ym3526_set_irq_handler (m_chip, &ym3526_device::static_irq_handler, this); - ym3526_set_update_handler(m_chip, &ym3526_device::static_update_request, this); - - m_timer[0] = timer_alloc(0); - m_timer[1] = timer_alloc(1); -} - -void ym3526_device::device_clock_changed() -{ - calculate_rates(); - ym3526_clock_changed(m_chip, clock(), clock() / 72); -} - -void ym3526_device::calculate_rates() -{ - int rate = clock()/72; /* ??? */ - - if (m_stream != nullptr) - m_stream->set_sample_rate(rate); - else - m_stream = stream_alloc(0,1,rate); -} - -//------------------------------------------------- -// device_stop - device-specific stop -//------------------------------------------------- - -void ym3526_device::device_stop() -{ - ym3526_shutdown(m_chip); -} - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void ym3526_device::device_reset() -{ - ym3526_reset_chip(m_chip); -} - - -u8 ym3526_device::read(offs_t offset) -{ - return ym3526_read(m_chip, offset & 1); -} - -void ym3526_device::write(offs_t offset, u8 data) -{ - ym3526_write(m_chip, offset & 1, data); -} - -u8 ym3526_device::status_port_r() { return read(0); } -u8 ym3526_device::read_port_r() { return read(1); } -void ym3526_device::control_port_w(u8 data) { write(0, data); } -void ym3526_device::write_port_w(u8 data) { write(1, data); } - - -DEFINE_DEVICE_TYPE(YM3526, ym3526_device, "ym3526", "YM3526 OPL") - -ym3526_device::ym3526_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, YM3526, tag, owner, clock) - , device_sound_interface(mconfig, *this) - , m_stream(nullptr) - , m_timer{ nullptr, nullptr } - , m_chip(nullptr) - , m_irq_handler(*this) -{ -} diff --git a/src/devices/sound/3526intf.h b/src/devices/sound/3526intf.h deleted file mode 100644 index a45825b57b5..00000000000 --- a/src/devices/sound/3526intf.h +++ /dev/null @@ -1,57 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ernesto Corvi -#ifndef MAME_SOUND_3526INTF_H -#define MAME_SOUND_3526INTF_H - -#pragma once - - -class ym3526_device : public device_t, public device_sound_interface -{ -public: - ym3526_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // configuration helpers - auto irq_handler() { return m_irq_handler.bind(); } - - u8 read(offs_t offset); - void write(offs_t offset, u8 data); - - u8 status_port_r(); - u8 read_port_r(); - void control_port_w(u8 data); - void write_port_w(u8 data); - -protected: - // device-level overrides - virtual void device_start() override; - virtual void device_stop() override; - virtual void device_reset() override; - virtual void device_clock_changed() override; - - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; - -private: - void irq_handler(int irq); - void timer_handler(int c, const attotime &period); - void update_request() { m_stream->update(); } - - void calculate_rates(); - - static void static_irq_handler(device_t *param, int irq) { downcast(param)->irq_handler(irq); } - static void static_timer_handler(device_t *param, int c, const attotime &period) { downcast(param)->timer_handler(c, period); } - static void static_update_request(device_t *param, int interval) { downcast(param)->update_request(); } - - // internal state - sound_stream * m_stream; - emu_timer * m_timer[2]; - void * m_chip; - devcb_write_line m_irq_handler; -}; - -DECLARE_DEVICE_TYPE(YM3526, ym3526_device) - -#endif // MAME_SOUND_3526INTF_H diff --git a/src/devices/sound/3812intf.cpp b/src/devices/sound/3812intf.cpp deleted file mode 100644 index c74c75e27b0..00000000000 --- a/src/devices/sound/3812intf.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ernesto Corvi -/****************************************************************************** -* FILE -* Yamaha 3812 emulator interface - MAME VERSION -* -* CREATED BY -* Ernesto Corvi -* -* UPDATE LOG -* JB 28-04-2002 Fixed simultaneous usage of all three different chip types. -* Used real sample rate when resample filter is active. -* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. -* CHS 1999-01-09 Fixes new ym3812 emulation interface. -* CHS 1998-10-23 Mame streaming sound chip update -* EC 1998 Created Interface -* -* NOTES -* -******************************************************************************/ -#include "emu.h" -#include "3812intf.h" -#include "sound/fmopl.h" - - -void ym3812_device::irq_handler(int irq) -{ - m_timer[2]->adjust(attotime::zero, irq); -} - -/* Timer overflow callback from timer.c */ -void ym3812_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - switch(id) - { - case TIMER_A: - ym3812_timer_over(m_chip,0); - break; - - case TIMER_B: - ym3812_timer_over(m_chip,1); - break; - - case TIMER_IRQ_SYNC: - if (!m_irq_handler.isnull()) - m_irq_handler(param); - break; - } -} - -void ym3812_device::timer_handler(int c, const attotime &period) -{ - if( period == attotime::zero ) - { /* Reset FM Timer */ - m_timer[c]->enable(false); - } - else - { /* Start FM Timer */ - m_timer[c]->adjust(period); - } -} - - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void ym3812_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) -{ - ym3812_update_one(m_chip, outputs[0]); -} - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void ym3812_device::device_start() -{ - int rate = clock() / 72; - - m_irq_handler.resolve(); - - /* stream system initialize */ - m_chip = ym3812_init(this, clock(), rate); - if (!m_chip) - throw emu_fatalerror("ym3812_device(%s): Error creating YM3812 chip", tag()); - - calculate_rates(); - - /* YM3812 setup */ - ym3812_set_timer_handler (m_chip, ym3812_device::static_timer_handler, this); - ym3812_set_irq_handler (m_chip, ym3812_device::static_irq_handler, this); - ym3812_set_update_handler(m_chip, ym3812_device::static_update_request, this); - - m_timer[0] = timer_alloc(TIMER_A); - m_timer[1] = timer_alloc(TIMER_B); - m_timer[2] = timer_alloc(TIMER_IRQ_SYNC); -} - -void ym3812_device::device_clock_changed() -{ - calculate_rates(); - ym3812_clock_changed(m_chip, clock(), clock() / 72); -} - -void ym3812_device::calculate_rates() -{ - int rate = clock() / 72; - - if (m_stream != nullptr) - m_stream->set_sample_rate(rate); - else - m_stream = stream_alloc(0, 1, rate); -} - -//------------------------------------------------- -// device_stop - device-specific stop -//------------------------------------------------- - -void ym3812_device::device_stop() -{ - ym3812_shutdown(m_chip); -} - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void ym3812_device::device_reset() -{ - ym3812_reset_chip(m_chip); -} - - -u8 ym3812_device::read(offs_t offset) -{ - return ym3812_read(m_chip, offset & 1); -} - -void ym3812_device::write(offs_t offset, u8 data) -{ - ym3812_write(m_chip, offset & 1, data); -} - -u8 ym3812_device::status_port_r() { return read(0); } -u8 ym3812_device::read_port_r() { return read(1); } -void ym3812_device::control_port_w(u8 data) { write(0, data); } -void ym3812_device::write_port_w(u8 data) { write(1, data); } - - -DEFINE_DEVICE_TYPE(YM3812, ym3812_device, "ym3812", "YM3812 OPL2") - -ym3812_device::ym3812_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, YM3812, tag, owner, clock) - , device_sound_interface(mconfig, *this) - , m_stream(nullptr) - , m_timer{ nullptr, nullptr } - , m_chip(nullptr) - , m_irq_handler(*this) -{ -} diff --git a/src/devices/sound/3812intf.h b/src/devices/sound/3812intf.h deleted file mode 100644 index 5b2b4f9991a..00000000000 --- a/src/devices/sound/3812intf.h +++ /dev/null @@ -1,62 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ernesto Corvi -#ifndef MAME_SOUND_3812INTF_H -#define MAME_SOUND_3812INTF_H - - -class ym3812_device : public device_t, public device_sound_interface -{ -public: - ym3812_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // configuration helpers - auto irq_handler() { return m_irq_handler.bind(); } - - u8 read(offs_t offset); - void write(offs_t offset, u8 data); - - u8 status_port_r(); - u8 read_port_r(); - void control_port_w(u8 data); - void write_port_w(u8 data); - -protected: - // device-level overrides - virtual void device_start() override; - virtual void device_stop() override; - virtual void device_reset() override; - virtual void device_clock_changed() override; - - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; - -private: - enum - { - TIMER_A, - TIMER_B, - TIMER_IRQ_SYNC - }; - - void irq_handler(int irq); - void timer_handler(int c, const attotime &period); - void update_request() { m_stream->update(); } - - void calculate_rates(); - - static void static_irq_handler(device_t *param, int irq) { downcast(param)->irq_handler(irq); } - static void static_timer_handler(device_t *param, int c, const attotime &period) { downcast(param)->timer_handler(c, period); } - static void static_update_request(device_t *param, int interval) { downcast(param)->update_request(); } - - sound_stream * m_stream; - emu_timer * m_timer[3]; - void * m_chip; - devcb_write_line m_irq_handler; -}; - -DECLARE_DEVICE_TYPE(YM3812, ym3812_device) - - -#endif // MAME_SOUND_3812INTF_H diff --git a/src/devices/sound/8950intf.cpp b/src/devices/sound/8950intf.cpp deleted file mode 100644 index 5eed79dd138..00000000000 --- a/src/devices/sound/8950intf.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ernesto Corvi -/****************************************************************************** -* FILE -* Yamaha 3812 emulator interface - MAME VERSION -* -* CREATED BY -* Ernesto Corvi -* -* UPDATE LOG -* JB 28-04-2002 Fixed simultaneous usage of all three different chip types. -* Used real sample rate when resample filter is active. -* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. -* CHS 1999-01-09 Fixes new ym3812 emulation interface. -* CHS 1998-10-23 Mame streaming sound chip update -* EC 1998 Created Interface -* -* NOTES -* -******************************************************************************/ -#include "emu.h" -#include "8950intf.h" -#include "fmopl.h" - - -void y8950_device::irq_handler(int irq) -{ - m_irq_handler(irq); -} - -void y8950_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - switch(id) - { - case 0: - y8950_timer_over(m_chip,0); - break; - - case 1: - y8950_timer_over(m_chip,1); - break; - } -} - -void y8950_device::timer_handler(int c, const attotime &period) -{ - if( period == attotime::zero ) - { /* Reset FM Timer */ - m_timer[c]->enable(false); - } - else - { /* Start FM Timer */ - m_timer[c]->adjust(period); - } -} - - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void y8950_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) -{ - y8950_update_one(m_chip, outputs[0]); -} - - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void y8950_device::device_start() -{ - int rate = clock()/72; - - m_irq_handler.resolve_safe(); - m_keyboard_read_handler.resolve_safe(0); - m_keyboard_write_handler.resolve_safe(); - m_io_read_handler.resolve_safe(0); - m_io_write_handler.resolve_safe(); - - /* stream system initialize */ - m_chip = y8950_init(this,clock(),rate); - if (!m_chip) - throw emu_fatalerror("y8950_device(%s): Error creating Y8950 chip", tag()); - - /* ADPCM ROM data */ - y8950_set_delta_t_memory(m_chip, &y8950_device::static_read_byte, &y8950_device::static_write_byte); - - m_stream = stream_alloc(0,1,rate); - /* port and keyboard handler */ - y8950_set_port_handler(m_chip, &y8950_device::static_port_handler_w, &y8950_device::static_port_handler_r, this); - y8950_set_keyboard_handler(m_chip, &y8950_device::static_keyboard_handler_w, &y8950_device::static_keyboard_handler_r, this); - - /* Y8950 setup */ - y8950_set_timer_handler (m_chip, &y8950_device::static_timer_handler, this); - y8950_set_irq_handler (m_chip, &y8950_device::static_irq_handler, this); - y8950_set_update_handler(m_chip, &y8950_device::static_update_request, this); - - m_timer[0] = timer_alloc(0); - m_timer[1] = timer_alloc(1); -} - -//------------------------------------------------- -// device_clock_changed -//------------------------------------------------- - -void y8950_device::device_clock_changed() -{ - m_stream->set_sample_rate(clock() / 72); - y8950_clock_changed(m_chip, clock(), clock() / 72); -} - -//------------------------------------------------- -// device_stop - device-specific stop -//------------------------------------------------- - -void y8950_device::device_stop() -{ - y8950_shutdown(m_chip); -} - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void y8950_device::device_reset() -{ - y8950_reset_chip(m_chip); -} - -//------------------------------------------------- -// rom_bank_updated -//------------------------------------------------- - -void y8950_device::rom_bank_updated() -{ - m_stream->update(); -} - - -u8 y8950_device::read(offs_t offset) -{ - return y8950_read(m_chip, offset & 1); -} - -void y8950_device::write(offs_t offset, u8 data) -{ - y8950_write(m_chip, offset & 1, data); -} - -u8 y8950_device::status_port_r() { return read(0); } -u8 y8950_device::read_port_r() { return read(1); } -void y8950_device::control_port_w(u8 data) { write(0, data); } -void y8950_device::write_port_w(u8 data) { write(1, data); } - - -DEFINE_DEVICE_TYPE(Y8950, y8950_device, "y8950", "Y8950 MSX-Audio") - -y8950_device::y8950_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, Y8950, tag, owner, clock) - , device_sound_interface(mconfig, *this) - , device_rom_interface(mconfig, *this) - , m_stream(nullptr) - , m_timer{ nullptr, nullptr } - , m_chip(nullptr) - , m_irq_handler(*this) - , m_keyboard_read_handler(*this) - , m_keyboard_write_handler(*this) - , m_io_read_handler(*this) - , m_io_write_handler(*this) -{ -} diff --git a/src/devices/sound/8950intf.h b/src/devices/sound/8950intf.h deleted file mode 100644 index 9ed70e7cd92..00000000000 --- a/src/devices/sound/8950intf.h +++ /dev/null @@ -1,81 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Ernesto Corvi -#ifndef MAME_SOUND_8950INTF_H -#define MAME_SOUND_8950INTF_H - -#pragma once - -#include "dirom.h" - -class y8950_device : public device_t, - public device_sound_interface, - public device_rom_interface<21> -{ -public: - y8950_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // configuration helpers - auto irq() { return m_irq_handler.bind(); } - auto keyboard_read() { return m_keyboard_read_handler.bind(); } - auto keyboard_write() { return m_keyboard_write_handler.bind(); } - auto io_read() { return m_io_read_handler.bind(); } - auto io_write() { return m_io_write_handler.bind(); } - - u8 read(offs_t offset); - void write(offs_t offset, u8 data); - - u8 status_port_r(); - u8 read_port_r(); - void control_port_w(u8 data); - void write_port_w(u8 data); - -protected: - // device-level overrides - virtual void device_start() override; - virtual void device_clock_changed() override; - virtual void device_stop() override; - virtual void device_reset() override; - - virtual void rom_bank_updated() override; - - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; - -private: - void irq_handler(int irq); - void timer_handler(int c, const attotime &period); - void update_request() { m_stream->update(); } - - unsigned char port_handler_r() { return m_io_read_handler(0); } - void port_handler_w(unsigned char data) { m_io_write_handler(offs_t(0), data); } - unsigned char keyboard_handler_r() { return m_keyboard_read_handler(0); } - void keyboard_handler_w(unsigned char data) { m_keyboard_write_handler(offs_t(0), data); } - - static uint8_t static_read_byte(device_t *param, offs_t offset) { return downcast(param)->read_byte(offset); } - static void static_write_byte(device_t *param, offs_t offset, uint8_t data) { return downcast(param)->space().write_byte(offset, data); } - - static void static_irq_handler(device_t *param, int irq) { downcast(param)->irq_handler(irq); } - static void static_timer_handler(device_t *param, int c, const attotime &period) { downcast(param)->timer_handler(c, period); } - static void static_update_request(device_t *param, int interval) { downcast(param)->update_request(); } - - static unsigned char static_port_handler_r(device_t *param) { return downcast(param)->port_handler_r(); } - static void static_port_handler_w(device_t *param, unsigned char data) { downcast(param)->port_handler_w(data); } - static unsigned char static_keyboard_handler_r(device_t *param) { return downcast(param)->keyboard_handler_r(); } - static void static_keyboard_handler_w(device_t *param, unsigned char data) { downcast(param)->keyboard_handler_w(data); } - - // internal state - sound_stream * m_stream; - emu_timer * m_timer[2]; - void * m_chip; - devcb_write_line m_irq_handler; - devcb_read8 m_keyboard_read_handler; - devcb_write8 m_keyboard_write_handler; - devcb_read8 m_io_read_handler; - devcb_write8 m_io_write_handler; -}; - -DECLARE_DEVICE_TYPE(Y8950, y8950_device) - -#endif // MAME_SOUND_8950INTF_H diff --git a/src/devices/sound/fmopl.cpp b/src/devices/sound/fmopl.cpp deleted file mode 100644 index cb4d5ae686e..00000000000 --- a/src/devices/sound/fmopl.cpp +++ /dev/null @@ -1,2554 +0,0 @@ -// license:GPL-2.0+ -// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh -/* -** -** File: fmopl.c - software implementation of FM sound generator -** types OPL and OPL2 -** -** Copyright Jarek Burczynski (bujar at mame dot net) -** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development -** -** Version 0.72 -** - -Revision History: - -04-08-2003 Jarek Burczynski: - - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip - handles memory read/write or during the adpcm synthesis when the chip - requests another byte of ADPCM data. - -24-07-2003 Jarek Burczynski: - - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after - some (unknown) delay). Right now it's always set. - -14-06-2003 Jarek Burczynski: - - implemented all of the status register flags in Y8950 emulation - - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since - they can be either RAM or ROM - -08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) - - corrected ym3526_read() to always set bit 2 and bit 1 - to HIGH state - identical to ym3812_read (verified on real YM3526) - -04-28-2002 Jarek Burczynski: - - binary exact Envelope Generator (verified on real YM3812); - compared to YM2151: the EG clock is equal to internal_clock, - rates are 2 times slower and volume resolution is one bit less - - modified interface functions (they no longer return pointer - - that's internal to the emulator now): - - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init() - - corrected 'off by one' error in feedback calculations (when feedback is off) - - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) - - speeded up noise generator calculations (Nicola Salmoria) - -03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) - Complete rewrite (all verified on real YM3812): - - corrected sin_tab and tl_tab data - - corrected operator output calculations - - corrected waveform_select_enable register; - simply: ignore all writes to waveform_select register when - waveform_select_enable == 0 and do not change the waveform previously selected. - - corrected KSR handling - - corrected Envelope Generator: attack shape, Sustain mode and - Percussive/Non-percussive modes handling - - Envelope Generator rates are two times slower now - - LFO amplitude (tremolo) and phase modulation (vibrato) - - rhythm sounds phase generation - - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) - - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) - - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) - -12-28-2001 Acho A. Tang - - reflected Delta-T EOS status on Y8950 status port. - - fixed subscription range of attack/decay tables - - - To do: - add delay before key off in CSM mode (see CSMKeyControll) - verify volume of the FM part on the Y8950 -*/ - -#include "emu.h" -#include "fmopl.h" -#include "ymdeltat.h" - - - -/* output final shift */ -#if (OPL_SAMPLE_BITS==16) - #define FINAL_SH (0) - #define MAXOUT (+32767) - #define MINOUT (-32768) -#else - #define FINAL_SH (8) - #define MAXOUT (+127) - #define MINOUT (-128) -#endif - - -#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ -#define EG_SH 16 /* 16.16 fixed point (EG timing) */ -#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ -#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ - -#define FREQ_MASK ((1<=0) - { - if (value < 0x0200) - return (value & ~0); - if (value < 0x0400) - return (value & ~1); - if (value < 0x0800) - return (value & ~3); - if (value < 0x1000) - return (value & ~7); - if (value < 0x2000) - return (value & ~15); - if (value < 0x4000) - return (value & ~31); - return (value & ~63); - } - /*else value < 0*/ - if (value > -0x0200) - return (~abs(value) & ~0); - if (value > -0x0400) - return (~abs(value) & ~1); - if (value > -0x0800) - return (~abs(value) & ~3); - if (value > -0x1000) - return (~abs(value) & ~7); - if (value > -0x2000) - return (~abs(value) & ~15); - if (value > -0x4000) - return (~abs(value) & ~31); - return (~abs(value) & ~63); -} - - -static FILE *sample[1]; - #if 1 /*save to MONO file */ - #define SAVE_ALL_CHANNELS \ - { signed int pom = acc_calc(lt); \ - fputc((unsigned short)pom&0xff,sample[0]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ - } - #else /*save to STEREO file */ - #define SAVE_ALL_CHANNELS \ - { signed int pom = lt; \ - fputc((unsigned short)pom&0xff,sample[0]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ - pom = rt; \ - fputc((unsigned short)pom&0xff,sample[0]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ - } - #endif -#endif - -#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ -#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ -#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ -#define OPL_TYPE_IO 0x08 /* I/O port */ - -/* ---------- Generic interface section ---------- */ -#define OPL_TYPE_YM3526 (0) -#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) -#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) - - -namespace { - -// TODO: make these static members - -#define RATE_STEPS (8) -extern const unsigned char eg_rate_shift[16+64+16]; -extern const unsigned char eg_rate_select[16+64+16]; - - -struct OPL_SLOT -{ - uint32_t ar; /* attack rate: AR<<2 */ - uint32_t dr; /* decay rate: DR<<2 */ - uint32_t rr; /* release rate:RR<<2 */ - uint8_t KSR; /* key scale rate */ - uint8_t ksl; /* keyscale level */ - uint8_t ksr; /* key scale rate: kcode>>KSR */ - uint8_t mul; /* multiple: mul_tab[ML] */ - - /* Phase Generator */ - uint32_t Cnt; /* frequency counter */ - uint32_t Incr; /* frequency counter step */ - uint8_t FB; /* feedback shift value */ - int32_t *connect1; /* slot1 output pointer */ - int32_t op1_out[2]; /* slot1 output for feedback */ - uint8_t CON; /* connection (algorithm) type */ - - /* Envelope Generator */ - uint8_t eg_type; /* percussive/non-percussive mode */ - uint8_t state; /* phase type */ - uint32_t TL; /* total level: TL << 2 */ - int32_t TLL; /* adjusted now TL */ - int32_t volume; /* envelope counter */ - uint32_t sl; /* sustain level: sl_tab[SL] */ - uint8_t eg_sh_ar; /* (attack state) */ - uint8_t eg_sel_ar; /* (attack state) */ - uint8_t eg_sh_dr; /* (decay state) */ - uint8_t eg_sel_dr; /* (decay state) */ - uint8_t eg_sh_rr; /* (release state) */ - uint8_t eg_sel_rr; /* (release state) */ - uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */ - - /* LFO */ - uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ - uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/ - - /* waveform select */ - uint16_t wavetable; - - void KEYON(uint32_t key_set) - { - if( !key ) - { - /* restart Phase Generator */ - Cnt = 0; - /* phase -> Attack */ - state = EG_ATT; - } - key |= key_set; - } - - void KEYOFF(uint32_t key_clr) - { - if( key ) - { - key &= key_clr; - - if( !key ) - { - /* phase -> Release */ - if (state>EG_REL) - state = EG_REL; - } - } - } -}; - -struct OPL_CH -{ - OPL_SLOT SLOT[2]; - /* phase generator state */ - uint32_t block_fnum; /* block+fnum */ - uint32_t fc; /* Freq. Increment base */ - uint32_t ksl_base; /* KeyScaleLevel Base step */ - uint8_t kcode; /* key code (for key scaling) */ - - - /* update phase increment counter of operator (also update the EG rates if necessary) */ - void CALC_FCSLOT(OPL_SLOT &SLOT) - { - /* (frequency) phase increment counter */ - SLOT.Incr = fc * SLOT.mul; - int const ksr = kcode >> SLOT.KSR; - - if( SLOT.ksr != ksr ) - { - SLOT.ksr = ksr; - - /* calculate envelope generator rates */ - if ((SLOT.ar + SLOT.ksr) < 16+62) - { - SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; - SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; - } - else - { - SLOT.eg_sh_ar = 0; - SLOT.eg_sel_ar = 13*RATE_STEPS; - } - SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; - SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; - SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; - SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; - } - } - - /* CSM Key Control */ - void CSMKeyControll() - { - SLOT[SLOT1].KEYON(4); - SLOT[SLOT2].KEYON(4); - - /* The key off should happen exactly one sample later - not implemented correctly yet */ - - SLOT[SLOT1].KEYOFF(~4); - SLOT[SLOT2].KEYOFF(~4); - } -}; - -/* OPL state */ -class FM_OPL -{ -protected: - FM_OPL() -#if BUILD_Y8950 - : deltat(nullptr, [] (YM_DELTAT *p) { p->~YM_DELTAT(); }) -#endif - { - } - -public: - ~FM_OPL() - { - UnLockTable(); - } - - /* FM channel slots */ - OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ - - uint32_t eg_cnt; /* global envelope generator counter */ - uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ - uint32_t eg_timer_add; /* step of eg_timer */ - uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */ - - uint8_t rhythm; /* Rhythm mode */ - - uint32_t fn_tab[1024]; /* fnumber->increment counter */ - - /* LFO */ - uint32_t LFO_AM; - int32_t LFO_PM; - - uint8_t lfo_am_depth; - uint8_t lfo_pm_depth_range; - uint32_t lfo_am_cnt; - uint32_t lfo_am_inc; - uint32_t lfo_pm_cnt; - uint32_t lfo_pm_inc; - - uint32_t noise_rng; /* 23 bit noise shift register */ - uint32_t noise_p; /* current noise 'phase' */ - uint32_t noise_f; /* current noise period */ - - uint8_t wavesel; /* waveform select enable flag */ - - uint32_t T[2]; /* timer counters */ - uint8_t st[2]; /* timer enable */ - -#if BUILD_Y8950 - /* Delta-T ADPCM unit (Y8950) */ - - std::unique_ptr deltat; - - /* Keyboard and I/O ports interface */ - uint8_t portDirection; - uint8_t portLatch; - OPL_PORTHANDLER_R porthandler_r; - OPL_PORTHANDLER_W porthandler_w; - device_t * port_param; - OPL_PORTHANDLER_R keyboardhandler_r; - OPL_PORTHANDLER_W keyboardhandler_w; - device_t * keyboard_param; -#endif - - /* external event callback handlers */ - OPL_TIMERHANDLER timer_handler; /* TIMER handler */ - device_t *TimerParam; /* TIMER parameter */ - OPL_IRQHANDLER IRQHandler; /* IRQ handler */ - device_t *IRQParam; /* IRQ parameter */ - OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */ - device_t *UpdateParam; /* stream update parameter */ - - uint8_t type; /* chip type */ - uint8_t address; /* address register */ - uint8_t status; /* status flag */ - uint8_t statusmask; /* status mask */ - uint8_t mode; /* Reg.08 : CSM,notesel,etc. */ - - uint32_t clock; /* master clock (Hz) */ - uint32_t rate; /* sampling rate (Hz) */ - double freqbase; /* frequency base */ - attotime TimerBase; /* Timer base time (==sampling time)*/ - device_t *device; - - signed int phase_modulation; /* phase modulation input (SLOT 2) */ - signed int output[1]; -#if BUILD_Y8950 - int32_t output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */ -#endif - - - /* status set and IRQ handling */ - void STATUS_SET(int flag) - { - /* set status flag */ - status |= flag; - if(!(status & 0x80)) - { - if(status & statusmask) - { /* IRQ on */ - status |= 0x80; - /* callback user interrupt handler (IRQ is OFF to ON) */ - if(IRQHandler) (IRQHandler)(IRQParam,1); - } - } - } - - /* status reset and IRQ handling */ - void STATUS_RESET(int flag) - { - /* reset status flag */ - status &=~flag; - if(status & 0x80) - { - if (!(status & statusmask) ) - { - status &= 0x7f; - /* callback user interrupt handler (IRQ is ON to OFF) */ - if(IRQHandler) (IRQHandler)(IRQParam,0); - } - } - } - - /* IRQ mask set */ - void STATUSMASK_SET(int flag) - { - statusmask = flag; - /* IRQ handling check */ - STATUS_SET(0); - STATUS_RESET(0); - } - - - /* advance LFO to next sample */ - void advance_lfo() - { - /* LFO */ - lfo_am_cnt += lfo_am_inc; - if (lfo_am_cnt >= (uint32_t(LFO_AM_TAB_ELEMENTS) << LFO_SH)) /* lfo_am_table is 210 elements long */ - lfo_am_cnt -= (uint32_t(LFO_AM_TAB_ELEMENTS) << LFO_SH); - - uint8_t const tmp = lfo_am_table[ lfo_am_cnt >> LFO_SH ]; - - LFO_AM = lfo_am_depth ? tmp : tmp >> 2; - - lfo_pm_cnt += lfo_pm_inc; - LFO_PM = (lfo_pm_cnt>>LFO_SH & 7) | lfo_pm_depth_range; - } - - /* advance to next sample */ - void advance() - { - eg_timer += eg_timer_add; - - while (eg_timer >= eg_timer_overflow) - { - eg_timer -= eg_timer_overflow; - - eg_cnt++; - - for (int i=0; i<9*2; i++) - { - OPL_CH &CH = P_CH[i/2]; - OPL_SLOT &op = CH.SLOT[i&1]; - - /* Envelope Generator */ - switch(op.state) - { - case EG_ATT: /* attack phase */ - if ( !(eg_cnt & ((1<>op.eg_sh_ar)&7)]) - ) >>3; - - if (op.volume <= MIN_ATT_INDEX) - { - op.volume = MIN_ATT_INDEX; - op.state = EG_DEC; - } - - } - break; - - case EG_DEC: /* decay phase */ - if ( !(eg_cnt & ((1<>op.eg_sh_dr)&7)]; - - if ( op.volume >= op.sl ) - op.state = EG_SUS; - - } - break; - - case EG_SUS: /* sustain phase */ - - /* this is important behaviour: - one can change percusive/non-percussive modes on the fly and - the chip will remain in sustain phase - verified on real YM3812 */ - - if(op.eg_type) /* non-percussive mode */ - { - /* do nothing */ - } - else /* percussive mode */ - { - /* during sustain phase chip adds Release Rate (in percussive mode) */ - if ( !(eg_cnt & ((1<>op.eg_sh_rr)&7)]; - - if ( op.volume >= MAX_ATT_INDEX ) - op.volume = MAX_ATT_INDEX; - } - /* else do nothing in sustain phase */ - } - break; - - case EG_REL: /* release phase */ - if ( !(eg_cnt & ((1<>op.eg_sh_rr)&7)]; - - if ( op.volume >= MAX_ATT_INDEX ) - { - op.volume = MAX_ATT_INDEX; - op.state = EG_OFF; - } - - } - break; - - default: - break; - } - } - } - - for (int i=0; i<9*2; i++) - { - OPL_CH &CH = P_CH[i/2]; - OPL_SLOT &op = CH.SLOT[i&1]; - - /* Phase Generator */ - if(op.vib) - { - unsigned int block_fnum = CH.block_fnum; - unsigned int const fnum_lfo = (block_fnum&0x0380) >> 7; - - signed int const lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16*fnum_lfo ]; - - if (lfo_fn_table_index_offset) /* LFO phase modulation active */ - { - block_fnum += lfo_fn_table_index_offset; - uint8_t const block = (block_fnum&0x1c00) >> 10; - op.Cnt += (fn_tab[block_fnum&0x03ff] >> (7-block)) * op.mul; - } - else /* LFO phase modulation = zero */ - { - op.Cnt += op.Incr; - } - } - else /* LFO phase modulation disabled for this operator */ - { - op.Cnt += op.Incr; - } - } - - /* The Noise Generator of the YM3812 is 23-bit shift register. - * Period is equal to 2^23-2 samples. - * Register works at sampling frequency of the chip, so output - * can change on every sample. - * - * Output of the register and input to the bit 22 is: - * bit0 XOR bit14 XOR bit15 XOR bit22 - * - * Simply use bit 22 as the noise output. - */ - - noise_p += noise_f; - int i = noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ - noise_p &= FREQ_MASK; - while (i) - { - /* - uint32_t j; - j = ( (noise_rng) ^ (noise_rng>>14) ^ (noise_rng>>15) ^ (noise_rng>>22) ) & 1; - noise_rng = (j<<22) | (noise_rng>>1); - */ - - /* - Instead of doing all the logic operations above, we - use a trick here (and use bit 0 as the noise output). - The difference is only that the noise bit changes one - step ahead. This doesn't matter since we don't know - what is real state of the noise_rng after the reset. - */ - - if (noise_rng & 1) noise_rng ^= 0x800302; - noise_rng >>= 1; - - i--; - } - } - - /* calculate output */ - void CALC_CH(OPL_CH &CH) - { - OPL_SLOT *SLOT; - unsigned int env; - signed int out; - - phase_modulation = 0; - - /* SLOT 1 */ - SLOT = &CH.SLOT[SLOT1]; - env = volume_calc(*SLOT); - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - SLOT->op1_out[0] = SLOT->op1_out[1]; - *SLOT->connect1 += SLOT->op1_out[0]; - SLOT->op1_out[1] = 0; - if( env < ENV_QUIET ) - { - if (!SLOT->FB) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); - } - - /* SLOT 2 */ - SLOT++; - env = volume_calc(*SLOT); - if( env < ENV_QUIET ) - output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable); - } - - /* - operators used in the rhythm sounds generation process: - - Envelope Generator: - - channel operator register number Bass High Snare Tom Top - / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal - 6 / 0 12 50 70 90 f0 + - 6 / 1 15 53 73 93 f3 + - 7 / 0 13 51 71 91 f1 + - 7 / 1 16 54 74 94 f4 + - 8 / 0 14 52 72 92 f2 + - 8 / 1 17 55 75 95 f5 + - - Phase Generator: - - channel operator register number Bass High Snare Tom Top - / slot number MULTIPLE Drum Hat Drum Tom Cymbal - 6 / 0 12 30 + - 6 / 1 15 33 + - 7 / 0 13 31 + + + - 7 / 1 16 34 ----- n o t u s e d ----- - 8 / 0 14 32 + - 8 / 1 17 35 + + - - channel operator register number Bass High Snare Tom Top - number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal - 6 12,15 B6 A6 + - - 7 13,16 B7 A7 + + + - - 8 14,17 B8 A8 + + + - - */ - - /* calculate rhythm */ - - void CALC_RH() - { - unsigned int const noise = BIT(noise_rng, 0); - - OPL_SLOT *SLOT; - signed int out; - unsigned int env; - - - /* Bass Drum (verified on real YM3812): - - depends on the channel 6 'connect' register: - when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) - when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored - - output sample always is multiplied by 2 - */ - - phase_modulation = 0; - /* SLOT 1 */ - SLOT = &P_CH[6].SLOT[SLOT1]; - env = volume_calc(*SLOT); - - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - SLOT->op1_out[0] = SLOT->op1_out[1]; - - if (!SLOT->CON) - phase_modulation = SLOT->op1_out[0]; - /* else ignore output of operator 1 */ - - SLOT->op1_out[1] = 0; - if( env < ENV_QUIET ) - { - if (!SLOT->FB) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); - } - - /* SLOT 2 */ - SLOT++; - env = volume_calc(*SLOT); - if( env < ENV_QUIET ) - output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2; - - - /* Phase generation is based on: */ - /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ - /* SD (16) channel 7->slot 1 */ - /* TOM (14) channel 8->slot 1 */ - /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ - - /* Envelope generation based on: */ - /* HH channel 7->slot1 */ - /* SD channel 7->slot2 */ - /* TOM channel 8->slot1 */ - /* TOP channel 8->slot2 */ - - - /* The following formulas can be well optimized. - I leave them in direct form for now (in case I've missed something). - */ - - /* High Hat (verified on real YM3812) */ - OPL_SLOT const &SLOT7_1 = P_CH[7].SLOT[SLOT1]; - OPL_SLOT const &SLOT8_2 = P_CH[8].SLOT[SLOT2]; - env = volume_calc(SLOT7_1); - if( env < ENV_QUIET ) - { - /* high hat phase generation: - phase = d0 or 234 (based on frequency only) - phase = 34 or 2d0 (based on noise) - */ - - /* base frequency derived from operator 1 in channel 7 */ - unsigned char const bit7 = BIT(SLOT7_1.Cnt >> FREQ_SH, 7); - unsigned char const bit3 = BIT(SLOT7_1.Cnt >> FREQ_SH, 3); - unsigned char const bit2 = BIT(SLOT7_1.Cnt >> FREQ_SH, 2); - - unsigned char const res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0xd0; */ - /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ - uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; - - /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char const bit5e= BIT(SLOT8_2.Cnt >> FREQ_SH, 5); - unsigned char const bit3e= BIT(SLOT8_2.Cnt >> FREQ_SH, 3); - - unsigned char const res2 = bit3e ^ bit5e; - - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ - if (res2) - phase = (0x200|(0xd0>>2)); - - - /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ - /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ - if (phase&0x200) - { - if (noise) - phase = 0x200|0xd0; - } - else - /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ - /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ - { - if (noise) - phase = 0xd0>>2; - } - - output[0] += op_calc(phase<> FREQ_SH, 8); - - /* when bit8 = 0 phase = 0x100; */ - /* when bit8 = 1 phase = 0x200; */ - uint32_t phase = bit8 ? 0x200 : 0x100; - - /* Noise bit XOR'es phase by 0x100 */ - /* when noisebit = 0 pass the phase from calculation above */ - /* when noisebit = 1 phase ^= 0x100; */ - /* in other words: phase ^= (noisebit<<8); */ - if (noise) - phase ^= 0x100; - - output[0] += op_calc(phase<> FREQ_SH, 7); - unsigned char const bit3 = BIT(SLOT7_1.Cnt >> FREQ_SH, 3); - unsigned char const bit2 = BIT(SLOT7_1.Cnt >> FREQ_SH, 2); - - unsigned char const res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0x100; */ - /* when res1 = 1 phase = 0x200 | 0x100; */ - uint32_t phase = res1 ? 0x300 : 0x100; - - /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char const bit5e= BIT(SLOT8_2.Cnt >> FREQ_SH, 5); - unsigned char const bit3e= BIT(SLOT8_2.Cnt >> FREQ_SH, 3); - - unsigned char const res2 = bit3e ^ bit5e; - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | 0x100; */ - if (res2) - phase = 0x300; - - output[0] += op_calc(phase<> 6]; - SLOT.TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ - - SLOT.TLL = SLOT.TL + (CH.ksl_base >> SLOT.ksl); - } - - /* set attack rate & decay rate */ - void set_ar_dr(int slot, int v) - { - OPL_CH &CH = P_CH[slot/2]; - OPL_SLOT &SLOT = CH.SLOT[slot&1]; - - SLOT.ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; - - if ((SLOT.ar + SLOT.ksr) < 16+62) - { - SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; - SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; - } - else - { - SLOT.eg_sh_ar = 0; - SLOT.eg_sel_ar = 13*RATE_STEPS; - } - - SLOT.dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; - SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; - } - - /* set sustain level & release rate */ - void set_sl_rr(int slot, int v) - { - OPL_CH &CH = P_CH[slot/2]; - OPL_SLOT &SLOT = CH.SLOT[slot&1]; - - SLOT.sl = sl_tab[ v>>4 ]; - - SLOT.rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; - SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; - } - - - void ResetChip(); - void postload(); - - void clock_changed(uint32_t c, uint32_t r) - { - clock = c; - rate = r; - - /* init global tables */ - initialize(); - } - - int Write(int a, int v) - { - if( !(a&1) ) - { /* address port */ - address = v & 0xff; - } - else - { /* data port */ - if (UpdateHandler) UpdateHandler(UpdateParam, 0); - WriteReg(address, v); - } - return status>>7; - } - - unsigned char Read(int a) - { - if( !(a&1) ) - { - /* status port */ - - #if BUILD_Y8950 - - if(type&OPL_TYPE_ADPCM) /* Y8950 */ - { - return (status & (statusmask|0x80)) | (deltat->PCM_BSY&1); - } - - #endif - - /* OPL and OPL2 */ - return status & (statusmask|0x80); - } - -#if BUILD_Y8950 - /* data port */ - switch(address) - { - case 0x05: /* KeyBoard IN */ - if(type&OPL_TYPE_KEYBOARD) - { - if(keyboardhandler_r) - return keyboardhandler_r(keyboard_param); - else - device->logerror("Y8950: read unmapped KEYBOARD port\n"); - } - return 0; - - case 0x0f: /* ADPCM-DATA */ - if(type&OPL_TYPE_ADPCM) - { - uint8_t val; - - val = deltat->ADPCM_Read(); - /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/ - return val; - } - return 0; - - case 0x19: /* I/O DATA */ - if(type&OPL_TYPE_IO) - { - if(porthandler_r) - return porthandler_r(port_param); - else - device->logerror("Y8950:read unmapped I/O port\n"); - } - return 0; - case 0x1a: /* PCM-DATA */ - if(type&OPL_TYPE_ADPCM) - { - device->logerror("Y8950 A/D conversion is accessed but not implemented !\n"); - return 0x80; /* 2's complement PCM data - result from A/D conversion */ - } - return 0; - } -#endif - - return 0xff; - } - - - int TimerOver(int c) - { - if( c ) - { /* Timer B */ - STATUS_SET(0x20); - } - else - { /* Timer A */ - STATUS_SET(0x40); - /* CSM mode key,TL controll */ - if( mode & 0x80 ) - { /* CSM mode total level latch and auto key on */ - int ch; - if(UpdateHandler) UpdateHandler(UpdateParam,0); - for(ch=0; ch<9; ch++) - P_CH[ch].CSMKeyControll(); - } - } - /* reload timer */ - if (timer_handler) (timer_handler)(TimerParam,c,TimerBase * T[c]); - return status>>7; - } - - - /* Create one of virtual YM3812/YM3526/Y8950 */ - /* 'clock' is chip clock in Hz */ - /* 'rate' is sampling rate */ - static FM_OPL *Create(device_t *device, uint32_t clock, uint32_t rate, int type) - { - if (LockTable(device) == -1) - return nullptr; - - /* calculate OPL state size */ - size_t state_size = sizeof(FM_OPL); -#if BUILD_Y8950 - if (type & OPL_TYPE_ADPCM) - state_size+= sizeof(YM_DELTAT); -#endif - - /* allocate memory block */ - char *ptr = reinterpret_cast(::operator new(state_size)); - std::fill_n(ptr, state_size, 0); - - FM_OPL *const OPL = new(ptr) FM_OPL; - - ptr += sizeof(FM_OPL); - -#if BUILD_Y8950 - if (type & OPL_TYPE_ADPCM) - { - OPL->deltat.reset(reinterpret_cast(ptr)); - ptr += sizeof(YM_DELTAT); - } -#endif - - OPL->device = device; - OPL->type = type; - OPL->clock_changed(clock, rate); - - return OPL; - } - - - /* Optional handlers */ - - void SetTimerHandler(OPL_TIMERHANDLER handler, device_t *device) - { - timer_handler = handler; - TimerParam = device; - } - void SetIRQHandler(OPL_IRQHANDLER handler, device_t *device) - { - IRQHandler = handler; - IRQParam = device; - } - void SetUpdateHandler(OPL_UPDATEHANDLER handler, device_t *device) - { - UpdateHandler = handler; - UpdateParam = device; - } - -private: - void WriteReg(int r, int v); - - uint32_t volume_calc(OPL_SLOT const &OP) const - { - return OP.TLL + uint32_t(OP.volume) + (LFO_AM & OP.AMmask); - } - - static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) - { - uint32_t const p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; - - return (p >= TL_TAB_LEN) ? 0 : tl_tab[p]; - } - - static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) - { - uint32_t const p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; - - return (p >= TL_TAB_LEN) ? 0 : tl_tab[p]; - } - - - /* lock/unlock for common table */ - static int LockTable(device_t *device) - { - num_lock++; - if(num_lock>1) return 0; - - /* first time */ - - /* allocate total level table (128kb space) */ - if( !init_tables() ) - { - num_lock--; - return -1; - } - - return 0; - } - - static void UnLockTable() - { - if(num_lock) num_lock--; - if(num_lock) return; - - /* last time */ - CloseTable(); - } - - static int init_tables(); - - static void CloseTable() - { -#ifdef SAVE_SAMPLE - fclose(sample[0]); -#endif - } - - - static constexpr uint32_t SC(uint32_t db) { return uint32_t(db * (2.0 / ENV_STEP)); } - - - static constexpr double DV = 0.1875 / 2.0; - - - /* TL_TAB_LEN is calculated as: - * 12 - sinus amplitude bits (Y axis) - * 2 - sinus sign bit (Y axis) - * TL_RES_LEN - sinus resolution (X axis) - */ - static constexpr unsigned TL_TAB_LEN = 12 * 2 * TL_RES_LEN; - static constexpr unsigned ENV_QUIET = TL_TAB_LEN >> 4; - - static constexpr unsigned LFO_AM_TAB_ELEMENTS = 210; - - static const double ksl_tab[8*16]; - static const uint32_t ksl_shift[4]; - static const uint32_t sl_tab[16]; - static const unsigned char eg_inc[15 * RATE_STEPS]; - - static const uint8_t mul_tab[16]; - static signed int tl_tab[TL_TAB_LEN]; - static unsigned int sin_tab[SIN_LEN * 4]; - - static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS]; - static const int8_t lfo_pm_table[8 * 8 * 2]; - - static int num_lock; -}; - - - -/* mapping of register number (offset) to slot number used by the emulator */ -static const int slot_array[32]= -{ - 0, 2, 4, 1, 3, 5,-1,-1, - 6, 8,10, 7, 9,11,-1,-1, - 12,14,16,13,15,17,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1 -}; - -/* key scale level */ -/* table is 3dB/octave , DV converts this into 6dB/octave */ -/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ -const double FM_OPL::ksl_tab[8*16]= -{ - /* OCT 0 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - /* OCT 1 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, - 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, - /* OCT 2 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, - 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, - 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, - /* OCT 3 */ - 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, - 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, - 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, - 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, - /* OCT 4 */ - 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, - 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, - 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, - 10.875/DV,11.250/DV,11.625/DV,12.000/DV, - /* OCT 5 */ - 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, - 9.000/DV,10.125/DV,10.875/DV,11.625/DV, - 12.000/DV,12.750/DV,13.125/DV,13.500/DV, - 13.875/DV,14.250/DV,14.625/DV,15.000/DV, - /* OCT 6 */ - 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, - 12.000/DV,13.125/DV,13.875/DV,14.625/DV, - 15.000/DV,15.750/DV,16.125/DV,16.500/DV, - 16.875/DV,17.250/DV,17.625/DV,18.000/DV, - /* OCT 7 */ - 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, - 15.000/DV,16.125/DV,16.875/DV,17.625/DV, - 18.000/DV,18.750/DV,19.125/DV,19.500/DV, - 19.875/DV,20.250/DV,20.625/DV,21.000/DV -}; - -/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ -const uint32_t FM_OPL::ksl_shift[4] = { 31, 1, 2, 0 }; - - -/* sustain level table (3dB per step) */ -/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ -const uint32_t FM_OPL::sl_tab[16]={ - SC( 0),SC( 1),SC( 2),SC( 3),SC( 4),SC( 5),SC( 6),SC( 7), - SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) -}; - - -const unsigned char FM_OPL::eg_inc[15*RATE_STEPS]={ -/*cycle:0 1 2 3 4 5 6 7*/ - -/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ -/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ -/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ -/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ - -/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ -/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ -/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ -/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ - -/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ -/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ -/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ -/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ - -/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ -/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ -/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ -}; - - -#define O(a) (a*RATE_STEPS) - -/*note that there is no O(13) in this table - it's directly in the code */ -const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ -/* 16 infinite time rates */ -O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), -O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), - -/* rates 00-12 */ -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), - -/* rate 13 */ -O( 4),O( 5),O( 6),O( 7), - -/* rate 14 */ -O( 8),O( 9),O(10),O(11), - -/* rate 15 */ -O(12),O(12),O(12),O(12), - -/* 16 dummy rates (same as 15 3) */ -O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), -O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), - -}; -#undef O - -/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ -/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ -/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ - -#define O(a) (a*1) -const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ -/* 16 infinite time rates */ -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), - -/* rates 00-12 */ -O(12),O(12),O(12),O(12), -O(11),O(11),O(11),O(11), -O(10),O(10),O(10),O(10), -O( 9),O( 9),O( 9),O( 9), -O( 8),O( 8),O( 8),O( 8), -O( 7),O( 7),O( 7),O( 7), -O( 6),O( 6),O( 6),O( 6), -O( 5),O( 5),O( 5),O( 5), -O( 4),O( 4),O( 4),O( 4), -O( 3),O( 3),O( 3),O( 3), -O( 2),O( 2),O( 2),O( 2), -O( 1),O( 1),O( 1),O( 1), -O( 0),O( 0),O( 0),O( 0), - -/* rate 13 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 14 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 15 */ -O( 0),O( 0),O( 0),O( 0), - -/* 16 dummy rates (same as 15 3) */ -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), - -}; -#undef O - - -/* multiple table */ -#define ML 2 -const uint8_t FM_OPL::mul_tab[16]= { -/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ - ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML, - 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML -}; -#undef ML - -signed int FM_OPL::tl_tab[TL_TAB_LEN]; - -/* sin waveform table in 'decibel' scale */ -/* four waveforms on OPL2 type chips */ -unsigned int FM_OPL::sin_tab[SIN_LEN * 4]; - - -/* LFO Amplitude Modulation table (verified on real YM3812) - 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples - - Length: 210 elements. - - Each of the elements has to be repeated - exactly 64 times (on 64 consecutive samples). - The whole table takes: 64 * 210 = 13440 samples. - - When AM = 1 data is used directly - When AM = 0 data is divided by 4 before being used (losing precision is important) -*/ - -const uint8_t FM_OPL::lfo_am_table[LFO_AM_TAB_ELEMENTS] = { -0,0,0,0,0,0,0, -1,1,1,1, -2,2,2,2, -3,3,3,3, -4,4,4,4, -5,5,5,5, -6,6,6,6, -7,7,7,7, -8,8,8,8, -9,9,9,9, -10,10,10,10, -11,11,11,11, -12,12,12,12, -13,13,13,13, -14,14,14,14, -15,15,15,15, -16,16,16,16, -17,17,17,17, -18,18,18,18, -19,19,19,19, -20,20,20,20, -21,21,21,21, -22,22,22,22, -23,23,23,23, -24,24,24,24, -25,25,25,25, -26,26,26, -25,25,25,25, -24,24,24,24, -23,23,23,23, -22,22,22,22, -21,21,21,21, -20,20,20,20, -19,19,19,19, -18,18,18,18, -17,17,17,17, -16,16,16,16, -15,15,15,15, -14,14,14,14, -13,13,13,13, -12,12,12,12, -11,11,11,11, -10,10,10,10, -9,9,9,9, -8,8,8,8, -7,7,7,7, -6,6,6,6, -5,5,5,5, -4,4,4,4, -3,3,3,3, -2,2,2,2, -1,1,1,1 -}; - -/* LFO Phase Modulation table (verified on real YM3812) */ -const int8_t FM_OPL::lfo_pm_table[8*8*2] = { -/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ -4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ -5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ -6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ -7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ -}; - - -/* lock level of common table */ -int FM_OPL::num_lock = 0; - - -#if 0 -static inline int limit( int val, int max, int min ) { - if ( val > max ) - val = max; - else if ( val < min ) - val = min; - - return val; -} -#endif - -/* generic table initialize */ -int FM_OPL::init_tables() -{ - signed int i,x; - signed int n; - double o,m; - - - for (x=0; x>= 4; /* 12 bits here */ - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - /* 11 bits here (rounded) */ - n <<= 1; /* 12 bits here (as in real chip) */ - tl_tab[ x*2 + 0 ] = n; - tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; - - for (i=1; i<12; i++) - { - tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; - tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; - } - #if 0 - logerror("tl %04i", x*2); - for (i=0; i<12; i++) - logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); - logerror("\n"); - #endif - } - /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ - - - for (i=0; i0.0) - o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ - else - o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ - - o = o / (ENV_STEP/4); - - n = (int)(2.0*o); - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - - sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); - - /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ - } - - for (i=0; i>1) ]; - - /* waveform 3: _ _ _ _ */ - /* / |_/ |_/ |_/ |_*/ - /* abs(output only first quarter of the sinus waveform) */ - - if (i & (1<<(SIN_BITS-2)) ) - sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; - else - sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; - - /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); - logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); - logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/ - } - /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ - - -#ifdef SAVE_SAMPLE - sample[0]=fopen("sampsum.pcm","wb"); -#endif - - return 1; -} - - -void FM_OPL::initialize() -{ - int i; - - /* frequency base */ - freqbase = (rate) ? ((double)clock / 72.0) / rate : 0; -#if 0 - rate = (double)clock / 72.0; - freqbase = 1.0; -#endif - - /*logerror("freqbase=%f\n", freqbase);*/ - - /* Timer base time */ - TimerBase = clock ? attotime::from_hz(clock) * 72 : attotime::zero; - - /* make fnumber -> increment counter table */ - for( i=0 ; i < 1024 ; i++ ) - { - /* opn phase increment counter = 20bit */ - fn_tab[i] = (uint32_t)( (double)i * 64 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ -#if 0 - logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n", - i, fn_tab[i]>>6, fn_tab[i]>>6 ); -#endif - } - -#if 0 - for( i=0 ; i < 16 ; i++ ) - { - logerror("FMOPL.C: sl_tab[%i] = %08x\n", - i, sl_tab[i] ); - } - for( i=0 ; i < 8 ; i++ ) - { - int j; - logerror("FMOPL.C: ksl_tab[oct=%2i] =",i); - for (j=0; j<16; j++) - { - logerror("%08x ", static_cast(ksl_tab[i*16+j]) ); - } - logerror("\n"); - } -#endif - - - /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ - /* One entry from LFO_AM_TABLE lasts for 64 samples */ - lfo_am_inc = (1.0 / 64.0 ) * (1<>1)&1; - - /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ - STATUS_RESET(v & (0x78-0x08)); - STATUSMASK_SET((~v) & 0x78); - - /* timer 2 */ - if(st[1] != st2) - { - attotime period = st2 ? (TimerBase * T[1]) : attotime::zero; - st[1] = st2; - if (timer_handler) (timer_handler)(TimerParam,1,period); - } - /* timer 1 */ - if(st[0] != st1) - { - attotime period = st1 ? (TimerBase * T[0]) : attotime::zero; - st[0] = st1; - if (timer_handler) (timer_handler)(TimerParam,0,period); - } - } - break; -#if BUILD_Y8950 - case 0x06: /* Key Board OUT */ - if(type&OPL_TYPE_KEYBOARD) - { - if(keyboardhandler_w) - keyboardhandler_w(keyboard_param,v); - else - device->logerror("Y8950: write unmapped KEYBOARD port\n"); - } - break; - case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ - if(type&OPL_TYPE_ADPCM) - deltat->ADPCM_Write(r-0x07,v); - break; -#endif - case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ - mode = v; -#if BUILD_Y8950 - if(type&OPL_TYPE_ADPCM) - deltat->ADPCM_Write(r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */ -#endif - break; - -#if BUILD_Y8950 - case 0x09: /* START ADD */ - case 0x0a: - case 0x0b: /* STOP ADD */ - case 0x0c: - case 0x0d: /* PRESCALE */ - case 0x0e: - case 0x0f: /* ADPCM data write */ - case 0x10: /* DELTA-N */ - case 0x11: /* DELTA-N */ - case 0x12: /* ADPCM volume */ - if(type&OPL_TYPE_ADPCM) - deltat->ADPCM_Write(r-0x07,v); - break; - - case 0x15: /* DAC data high 8 bits (F7,F6...F2) */ - case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */ - case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */ - device->logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v); - break; - - case 0x18: /* I/O CTRL (Direction) */ - if(type&OPL_TYPE_IO) - portDirection = v&0x0f; - break; - case 0x19: /* I/O DATA */ - if(type&OPL_TYPE_IO) - { - portLatch = v; - if(porthandler_w) - porthandler_w(port_param,v&portDirection); - } - break; -#endif - default: - device->logerror("FMOPL.C: write to unknown register: %02x\n",r); - break; - } - break; - case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_mul(slot,v); - break; - case 0x40: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_ksl_tl(slot,v); - break; - case 0x60: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_ar_dr(slot,v); - break; - case 0x80: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_sl_rr(slot,v); - break; - case 0xa0: - if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ - { - lfo_am_depth = v & 0x80; - lfo_pm_depth_range = (v&0x40) ? 8 : 0; - - rhythm = v&0x3f; - - if(rhythm&0x20) - { - /* BD key on/off */ - if(v&0x10) - { - P_CH[6].SLOT[SLOT1].KEYON(2); - P_CH[6].SLOT[SLOT2].KEYON(2); - } - else - { - P_CH[6].SLOT[SLOT1].KEYOFF(~2); - P_CH[6].SLOT[SLOT2].KEYOFF(~2); - } - /* HH key on/off */ - if(v&0x01) P_CH[7].SLOT[SLOT1].KEYON ( 2); - else P_CH[7].SLOT[SLOT1].KEYOFF(~2); - /* SD key on/off */ - if(v&0x08) P_CH[7].SLOT[SLOT2].KEYON ( 2); - else P_CH[7].SLOT[SLOT2].KEYOFF(~2); - /* TOM key on/off */ - if(v&0x04) P_CH[8].SLOT[SLOT1].KEYON ( 2); - else P_CH[8].SLOT[SLOT1].KEYOFF(~2); - /* TOP-CY key on/off */ - if(v&0x02) P_CH[8].SLOT[SLOT2].KEYON ( 2); - else P_CH[8].SLOT[SLOT2].KEYOFF(~2); - } - else - { - /* BD key off */ - P_CH[6].SLOT[SLOT1].KEYOFF(~2); - P_CH[6].SLOT[SLOT2].KEYOFF(~2); - /* HH key off */ - P_CH[7].SLOT[SLOT1].KEYOFF(~2); - /* SD key off */ - P_CH[7].SLOT[SLOT2].KEYOFF(~2); - /* TOM key off */ - P_CH[8].SLOT[SLOT1].KEYOFF(~2); - /* TOP-CY off */ - P_CH[8].SLOT[SLOT2].KEYOFF(~2); - } - return; - } - /* keyon,block,fnum */ - if( (r&0x0f) > 8) return; - CH = &P_CH[r&0x0f]; - if(!(r&0x10)) - { /* a0-a8 */ - block_fnum = (CH->block_fnum&0x1f00) | v; - } - else - { /* b0-b8 */ - block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); - - if(v&0x20) - { - CH->SLOT[SLOT1].KEYON ( 1); - CH->SLOT[SLOT2].KEYON ( 1); - } - else - { - CH->SLOT[SLOT1].KEYOFF(~1); - CH->SLOT[SLOT2].KEYOFF(~1); - } - } - /* update */ - if(CH->block_fnum != block_fnum) - { - uint8_t block = block_fnum >> 10; - - CH->block_fnum = block_fnum; - - CH->ksl_base = static_cast(ksl_tab[block_fnum>>6]); - CH->fc = fn_tab[block_fnum&0x03ff] >> (7-block); - - /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ - CH->kcode = (CH->block_fnum&0x1c00)>>9; - - /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ - /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ - /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ - if (mode&0x40) - CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ - else - CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ - - /* refresh Total Level in both SLOTs of this channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - - /* refresh frequency counter in both SLOTs of this channel */ - CH->CALC_FCSLOT(CH->SLOT[SLOT1]); - CH->CALC_FCSLOT(CH->SLOT[SLOT2]); - } - break; - case 0xc0: - /* FB,C */ - if( (r&0x0f) > 8) return; - CH = &P_CH[r&0x0f]; - CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; - CH->SLOT[SLOT1].CON = v&1; - CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &output[0] : &phase_modulation; - break; - case 0xe0: /* waveform select */ - /* simply ignore write to the waveform select register if selecting not enabled in test register */ - if(wavesel) - { - slot = slot_array[r&0x1f]; - if(slot < 0) return; - CH = &P_CH[slot/2]; - - CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; - } - break; - } -} - - -void FM_OPL::ResetChip() -{ - eg_timer = 0; - eg_cnt = 0; - - noise_rng = 1; /* noise shift register */ - mode = 0; /* normal mode */ - STATUS_RESET(0x7f); - - /* reset with register write */ - WriteReg(0x01,0); /* wavesel disable */ - WriteReg(0x02,0); /* Timer1 */ - WriteReg(0x03,0); /* Timer2 */ - WriteReg(0x04,0); /* IRQ mask clear */ - for(int i = 0xff ; i >= 0x20 ; i-- ) WriteReg(i,0); - - /* reset operator parameters */ - for(OPL_CH &CH : P_CH) - { - for(OPL_SLOT &SLOT : CH.SLOT) - { - /* wave table */ - SLOT.wavetable = 0; - SLOT.state = EG_OFF; - SLOT.volume = MAX_ATT_INDEX; - } - } -#if BUILD_Y8950 - if(type&OPL_TYPE_ADPCM) - { - deltat->freqbase = freqbase; - deltat->output_pointer = &output_deltat[0]; - deltat->portshift = 5; - deltat->output_range = 1<<23; - deltat->ADPCM_Reset(0,YM_DELTAT::EMULATION_MODE_NORMAL,device); - } -#endif -} - - -void FM_OPL::postload() -{ - for(OPL_CH &CH : P_CH) - { - /* Look up key scale level */ - uint32_t const block_fnum = CH.block_fnum; - CH.ksl_base = static_cast(ksl_tab[block_fnum >> 6]); - CH.fc = fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10)); - - for(OPL_SLOT &SLOT : CH.SLOT) - { - /* Calculate key scale rate */ - SLOT.ksr = CH.kcode >> SLOT.KSR; - - /* Calculate attack, decay and release rates */ - if ((SLOT.ar + SLOT.ksr) < 16+62) - { - SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; - SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; - } - else - { - SLOT.eg_sh_ar = 0; - SLOT.eg_sel_ar = 13*RATE_STEPS; - } - SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; - SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; - SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; - SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; - - /* Calculate phase increment */ - SLOT.Incr = CH.fc * SLOT.mul; - - /* Total level */ - SLOT.TLL = SLOT.TL + (CH.ksl_base >> SLOT.ksl); - - /* Connect output */ - SLOT.connect1 = SLOT.CON ? &output[0] : &phase_modulation; - } - } -#if BUILD_Y8950 - if ( (type & OPL_TYPE_ADPCM) && (deltat) ) - { - // We really should call the postlod function for the YM_DELTAT, but it's hard without registers - // (see the way the YM2610 does it) - //deltat->postload(REGS); - } -#endif -} - -} // anonymous namespace - - -static void OPLsave_state_channel(device_t *device, OPL_CH *CH) -{ - int slot, ch; - - for( ch=0 ; ch < 9 ; ch++, CH++ ) - { - /* channel */ - device->save_item(NAME(CH->block_fnum), ch); - device->save_item(NAME(CH->kcode), ch); - /* slots */ - for( slot=0 ; slot < 2 ; slot++ ) - { - OPL_SLOT *SLOT = &CH->SLOT[slot]; - - device->save_item(NAME(SLOT->ar), ch * 2 + slot); - device->save_item(NAME(SLOT->dr), ch * 2 + slot); - device->save_item(NAME(SLOT->rr), ch * 2 + slot); - device->save_item(NAME(SLOT->KSR), ch * 2 + slot); - device->save_item(NAME(SLOT->ksl), ch * 2 + slot); - device->save_item(NAME(SLOT->mul), ch * 2 + slot); - - device->save_item(NAME(SLOT->Cnt), ch * 2 + slot); - device->save_item(NAME(SLOT->FB), ch * 2 + slot); - device->save_item(NAME(SLOT->op1_out), ch * 2 + slot); - device->save_item(NAME(SLOT->CON), ch * 2 + slot); - - device->save_item(NAME(SLOT->eg_type), ch * 2 + slot); - device->save_item(NAME(SLOT->state), ch * 2 + slot); - device->save_item(NAME(SLOT->TL), ch * 2 + slot); - device->save_item(NAME(SLOT->volume), ch * 2 + slot); - device->save_item(NAME(SLOT->sl), ch * 2 + slot); - device->save_item(NAME(SLOT->key), ch * 2 + slot); - - device->save_item(NAME(SLOT->AMmask), ch * 2 + slot); - device->save_item(NAME(SLOT->vib), ch * 2 + slot); - - device->save_item(NAME(SLOT->wavetable), ch * 2 + slot); - } - } -} - - -/* Register savestate for a virtual YM3812/YM3526Y8950 */ - -static void OPL_save_state(FM_OPL *OPL, device_t *device) -{ - OPLsave_state_channel(device, OPL->P_CH); - - device->save_item(NAME(OPL->eg_cnt)); - device->save_item(NAME(OPL->eg_timer)); - - device->save_item(NAME(OPL->rhythm)); - - device->save_item(NAME(OPL->lfo_am_depth)); - device->save_item(NAME(OPL->lfo_pm_depth_range)); - device->save_item(NAME(OPL->lfo_am_cnt)); - device->save_item(NAME(OPL->lfo_pm_cnt)); - - device->save_item(NAME(OPL->noise_rng)); - device->save_item(NAME(OPL->noise_p)); - - if( OPL->type & OPL_TYPE_WAVESEL ) - { - device->save_item(NAME(OPL->wavesel)); - } - - device->save_item(NAME(OPL->T)); - device->save_item(NAME(OPL->st)); - -#if BUILD_Y8950 - if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) - { - OPL->deltat->savestate(device); - } - - if ( OPL->type & OPL_TYPE_IO ) - { - device->save_item(NAME(OPL->portDirection)); - device->save_item(NAME(OPL->portLatch)); - } -#endif - - device->save_item(NAME(OPL->address)); - device->save_item(NAME(OPL->status)); - device->save_item(NAME(OPL->statusmask)); - device->save_item(NAME(OPL->mode)); - - device->machine().save().register_postload(save_prepost_delegate(FUNC(FM_OPL::postload), OPL)); -} - - -#define MAX_OPL_CHIPS 2 - - -#if (BUILD_YM3812) - -void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate) -{ - reinterpret_cast(chip)->clock_changed(clock, rate); -} - -void * ym3812_init(device_t *device, uint32_t clock, uint32_t rate) -{ - /* emulator create */ - FM_OPL *YM3812 = FM_OPL::Create(device,clock,rate,OPL_TYPE_YM3812); - if (YM3812) - { - OPL_save_state(YM3812, device); - ym3812_reset_chip(YM3812); - } - return YM3812; -} - -void ym3812_shutdown(void *chip) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - - /* emulator shutdown */ - delete YM3812; -} -void ym3812_reset_chip(void *chip) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - YM3812->ResetChip(); -} - -int ym3812_write(void *chip, int a, int v) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - return YM3812->Write(a, v); -} - -unsigned char ym3812_read(void *chip, int a) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - /* YM3812 always returns bit2 and bit1 in HIGH state */ - return YM3812->Read(a) | 0x06 ; -} -int ym3812_timer_over(void *chip, int c) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - return YM3812->TimerOver(c); -} - -void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) -{ - reinterpret_cast(chip)->SetTimerHandler(timer_handler, device); -} -void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) -{ - reinterpret_cast(chip)->SetIRQHandler(IRQHandler, device); -} -void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) -{ - reinterpret_cast(chip)->SetUpdateHandler(UpdateHandler, device); -} - - -/* -** Generate samples for one of the YM3812's -** -** 'which' is the virtual YM3812 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void ym3812_update_one(void *chip, write_stream_view &buf) -{ - FM_OPL *OPL = (FM_OPL *)chip; - uint8_t rhythm = OPL->rhythm&0x20; - int i; - - for( i=0; i < buf.samples(); i++ ) - { - int lt; - - OPL->output[0] = 0; - - OPL->advance_lfo(); - - /* FM part */ - OPL->CALC_CH(OPL->P_CH[0]); - OPL->CALC_CH(OPL->P_CH[1]); - OPL->CALC_CH(OPL->P_CH[2]); - OPL->CALC_CH(OPL->P_CH[3]); - OPL->CALC_CH(OPL->P_CH[4]); - OPL->CALC_CH(OPL->P_CH[5]); - - if(!rhythm) - { - OPL->CALC_CH(OPL->P_CH[6]); - OPL->CALC_CH(OPL->P_CH[7]); - OPL->CALC_CH(OPL->P_CH[8]); - } - else /* Rhythm part */ - { - OPL->CALC_RH(); - } - - lt = OPL->output[0]; - - #ifdef SAVE_SAMPLE - if (which==0) - { - SAVE_ALL_CHANNELS - } - #endif - - /* store to sound buffer */ - buf.put_int_clamp(i, lt, 32768 << FINAL_SH); - - OPL->advance(); - } - -} -#endif /* BUILD_YM3812 */ - - - -#if (BUILD_YM3526) - -void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate) -{ - reinterpret_cast(chip)->clock_changed(clock, rate); -} - -void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate) -{ - /* emulator create */ - FM_OPL *YM3526 = FM_OPL::Create(device,clock,rate,OPL_TYPE_YM3526); - if (YM3526) - { - OPL_save_state(YM3526, device); - ym3526_reset_chip(YM3526); - } - return YM3526; -} - -void ym3526_shutdown(void *chip) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - /* emulator shutdown */ - delete YM3526; -} -void ym3526_reset_chip(void *chip) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - YM3526->ResetChip(); -} - -int ym3526_write(void *chip, int a, int v) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - return YM3526->Write(a, v); -} - -unsigned char ym3526_read(void *chip, int a) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - /* YM3526 always returns bit2 and bit1 in HIGH state */ - return YM3526->Read(a) | 0x06 ; -} -int ym3526_timer_over(void *chip, int c) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - return YM3526->TimerOver(c); -} - -void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) -{ - reinterpret_cast(chip)->SetTimerHandler(timer_handler, device); -} -void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) -{ - reinterpret_cast(chip)->SetIRQHandler(IRQHandler, device); -} -void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) -{ - reinterpret_cast(chip)->SetUpdateHandler(UpdateHandler, device); -} - - -/* -** Generate samples for one of the YM3526's -** -** 'which' is the virtual YM3526 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void ym3526_update_one(void *chip, write_stream_view &buf) -{ - FM_OPL *OPL = (FM_OPL *)chip; - uint8_t rhythm = OPL->rhythm&0x20; - int i; - - for( i=0; i < buf.samples() ; i++ ) - { - int lt; - - OPL->output[0] = 0; - - OPL->advance_lfo(); - - /* FM part */ - OPL->CALC_CH(OPL->P_CH[0]); - OPL->CALC_CH(OPL->P_CH[1]); - OPL->CALC_CH(OPL->P_CH[2]); - OPL->CALC_CH(OPL->P_CH[3]); - OPL->CALC_CH(OPL->P_CH[4]); - OPL->CALC_CH(OPL->P_CH[5]); - - if(!rhythm) - { - OPL->CALC_CH(OPL->P_CH[6]); - OPL->CALC_CH(OPL->P_CH[7]); - OPL->CALC_CH(OPL->P_CH[8]); - } - else /* Rhythm part */ - { - OPL->CALC_RH(); - } - - lt = OPL->output[0]; - - #ifdef SAVE_SAMPLE - if (which==0) - { - SAVE_ALL_CHANNELS - } - #endif - - /* store to sound buffer */ - buf.put_int_clamp(i, lt, 32768 << FINAL_SH); - - OPL->advance(); - } - -} -#endif /* BUILD_YM3526 */ - - - - -#if BUILD_Y8950 - -static void Y8950_deltat_status_set(void *chip, uint8_t changebits) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - Y8950->STATUS_SET(changebits); -} -static void Y8950_deltat_status_reset(void *chip, uint8_t changebits) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - Y8950->STATUS_RESET(changebits); -} - -void y8950_clock_changed(void *chip, uint32_t clock, uint32_t rate) -{ - reinterpret_cast(chip)->clock_changed(clock, rate); -} - -void *y8950_init(device_t *device, uint32_t clock, uint32_t rate) -{ - /* emulator create */ - FM_OPL *Y8950 = FM_OPL::Create(device,clock,rate,OPL_TYPE_Y8950); - if (Y8950) - { - Y8950->deltat->status_set_handler = Y8950_deltat_status_set; - Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset; - Y8950->deltat->status_change_which_chip = Y8950; - Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */ - Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */ - - /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */ - /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */ - /* reset */ - OPL_save_state(Y8950, device); - y8950_reset_chip(Y8950); - } - - return Y8950; -} - -void y8950_shutdown(void *chip) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - /* emulator shutdown */ - delete Y8950; -} -void y8950_reset_chip(void *chip) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - Y8950->ResetChip(); -} - -int y8950_write(void *chip, int a, int v) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - return Y8950->Write(a, v); -} - -unsigned char y8950_read(void *chip, int a) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - return Y8950->Read(a); -} -int y8950_timer_over(void *chip, int c) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - return Y8950->TimerOver(c); -} - -void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) -{ - reinterpret_cast(chip)->SetTimerHandler(timer_handler, device); -} -void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) -{ - reinterpret_cast(chip)->SetIRQHandler(IRQHandler, device); -} -void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) -{ - reinterpret_cast(chip)->SetUpdateHandler(UpdateHandler, device); -} - -void y8950_set_delta_t_memory(void *chip, FM_READBYTE read_byte, FM_WRITEBYTE write_byte) -{ - FM_OPL *OPL = (FM_OPL *)chip; - OPL->deltat->read_byte = read_byte; - OPL->deltat->write_byte = write_byte; -} - -/* -** Generate samples for one of the Y8950's -** -** 'which' is the virtual Y8950 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void y8950_update_one(void *chip, write_stream_view &buf) -{ - int i; - FM_OPL *OPL = (FM_OPL *)chip; - uint8_t rhythm = OPL->rhythm&0x20; - YM_DELTAT &DELTAT = *OPL->deltat; - - for( i=0; i < buf.samples() ; i++ ) - { - int lt; - - OPL->output[0] = 0; - OPL->output_deltat[0] = 0; - - OPL->advance_lfo(); - - /* deltaT ADPCM */ - if( DELTAT.portstate&0x80 ) - DELTAT.ADPCM_CALC(); - - /* FM part */ - OPL->CALC_CH(OPL->P_CH[0]); - OPL->CALC_CH(OPL->P_CH[1]); - OPL->CALC_CH(OPL->P_CH[2]); - OPL->CALC_CH(OPL->P_CH[3]); - OPL->CALC_CH(OPL->P_CH[4]); - OPL->CALC_CH(OPL->P_CH[5]); - - if(!rhythm) - { - OPL->CALC_CH(OPL->P_CH[6]); - OPL->CALC_CH(OPL->P_CH[7]); - OPL->CALC_CH(OPL->P_CH[8]); - } - else /* Rhythm part */ - { - OPL->CALC_RH(); - } - - lt = OPL->output[0] + (OPL->output_deltat[0]>>11); - - #ifdef SAVE_SAMPLE - if (which==0) - { - SAVE_ALL_CHANNELS - } - #endif - - /* store to sound buffer */ - buf.put_int_clamp(i, lt, 32768 << FINAL_SH); - - OPL->advance(); - } - -} - -void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,device_t *device) -{ - FM_OPL *OPL = (FM_OPL *)chip; - OPL->porthandler_w = PortHandler_w; - OPL->porthandler_r = PortHandler_r; - OPL->port_param = device; -} - -void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,device_t *device) -{ - FM_OPL *OPL = (FM_OPL *)chip; - OPL->keyboardhandler_w = KeyboardHandler_w; - OPL->keyboardhandler_r = KeyboardHandler_r; - OPL->keyboard_param = device; -} - -#endif diff --git a/src/devices/sound/fmopl.h b/src/devices/sound/fmopl.h deleted file mode 100644 index bb484b0e41b..00000000000 --- a/src/devices/sound/fmopl.h +++ /dev/null @@ -1,112 +0,0 @@ -// license:GPL-2.0+ -// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh -#ifndef MAME_SOUND_FMOPL_H -#define MAME_SOUND_FMOPL_H - -#pragma once - -#include - - -/* --- select emulation chips --- */ -#define BUILD_YM3812 (1) -#define BUILD_YM3526 (1) -#define BUILD_Y8950 (1) - -/* select output bits size of output : 8 or 16 */ -#define OPL_SAMPLE_BITS 16 - -typedef s32 OPLSAMPLE; -/* -#if (OPL_SAMPLE_BITS==16) -typedef int16_t OPLSAMPLE; -#endif -#if (OPL_SAMPLE_BITS==8) -typedef int8_t OPLSAMPLE; -#endif -*/ - -typedef uint8_t (*FM_READBYTE)(device_t *device, offs_t offset); -typedef void(*FM_WRITEBYTE)(device_t *device, offs_t offset, uint8_t data); -typedef void (*OPL_TIMERHANDLER)(device_t *device,int timer,const attotime &period); -typedef void (*OPL_IRQHANDLER)(device_t *device,int irq); -typedef void (*OPL_UPDATEHANDLER)(device_t *device,int min_interval_us); -typedef void (*OPL_PORTHANDLER_W)(device_t *device,unsigned char data); -typedef unsigned char (*OPL_PORTHANDLER_R)(device_t *device); - - -#if BUILD_YM3812 - -void *ym3812_init(device_t *device, uint32_t clock, uint32_t rate); -void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate); -void ym3812_shutdown(void *chip); -void ym3812_reset_chip(void *chip); -int ym3812_write(void *chip, int a, int v); -unsigned char ym3812_read(void *chip, int a); -int ym3812_timer_over(void *chip, int c); -void ym3812_update_one(void *chip, write_stream_view &buffer); - -void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); -void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); -void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); - -#endif /* BUILD_YM3812 */ - - -#if BUILD_YM3526 - -/* -** Initialize YM3526 emulator(s). -** -** 'num' is the number of virtual YM3526's to allocate -** 'clock' is the chip clock in Hz -** 'rate' is sampling rate -*/ -void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate); -void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate); -/* shutdown the YM3526 emulators*/ -void ym3526_shutdown(void *chip); -void ym3526_reset_chip(void *chip); -int ym3526_write(void *chip, int a, int v); -unsigned char ym3526_read(void *chip, int a); -int ym3526_timer_over(void *chip, int c); -/* -** Generate samples for one of the YM3526's -** -** 'which' is the virtual YM3526 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void ym3526_update_one(void *chip, write_stream_view &buffer); - -void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); -void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); -void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); - -#endif /* BUILD_YM3526 */ - - -#if BUILD_Y8950 - -/* Y8950 port handlers */ -void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, device_t *device); -void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, device_t *device); -void y8950_set_delta_t_memory(void *chip, FM_READBYTE read_byte, FM_WRITEBYTE write_byte); - -void * y8950_init(device_t *device, uint32_t clock, uint32_t rate); -void y8950_clock_changed(void *chip, uint32_t clock, uint32_t rate); -void y8950_shutdown(void *chip); -void y8950_reset_chip(void *chip); -int y8950_write(void *chip, int a, int v); -unsigned char y8950_read (void *chip, int a); -int y8950_timer_over(void *chip, int c); -void y8950_update_one(void *chip, write_stream_view &buffer); - -void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); -void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); -void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); - -#endif /* BUILD_Y8950 */ - - -#endif // MAME_SOUND_FMOPL_H diff --git a/src/devices/sound/y8950.cpp b/src/devices/sound/y8950.cpp new file mode 100644 index 00000000000..922308b960a --- /dev/null +++ b/src/devices/sound/y8950.cpp @@ -0,0 +1,309 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +#include "emu.h" +#include "y8950.h" + + +DEFINE_DEVICE_TYPE(Y8950, y8950_device, "y8950", "Y8950 OPL MSX-Audio") + + +//********************************************************* +// Y8950 DEVICE +//********************************************************* + +//------------------------------------------------- +// y8950_device - constructor +//------------------------------------------------- + +y8950_device::y8950_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type) : + device_t(mconfig, type, tag, owner, clock), + device_sound_interface(mconfig, *this), + device_rom_interface(mconfig, *this), + m_address(0), + m_io_ddr(0), + m_stream(nullptr), + m_fm(*this), + m_adpcm_b(*this, read8sm_delegate(*this, FUNC(y8950_device::adpcm_b_read)), write8sm_delegate(*this, FUNC(y8950_device::adpcm_b_write))), + m_keyboard_read_handler(*this), + m_keyboard_write_handler(*this), + m_io_read_handler(*this), + m_io_write_handler(*this) +{ +} + + +//------------------------------------------------- +// status_r - return the status port (A0=0) +//------------------------------------------------- + +u8 y8950_device::status_r() +{ + m_stream->update(); + return combine_status(); +} + + +//------------------------------------------------- +// data_r - return specific register data (A0=1) +//------------------------------------------------- + +u8 y8950_device::data_r() +{ + u8 result = 0xff; + switch (m_address) + { + case 0x05: // keyboard in + result = m_keyboard_read_handler(0); + break; + + case 0x09: // ADPCM data + case 0x1a: + result = m_adpcm_b.read(m_address - 0x07); + break; + + case 0x19: // I/O data + result = m_io_read_handler(0); + break; + + default: + logerror("Unexpected read from Y8950 data port %02X\n", m_address); + break; + } + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +u8 y8950_device::read(offs_t offset) +{ + // A0 selects between status/data + return ((offset & 1) == 0) ? status_r() : data_r(); +} + + +//------------------------------------------------- +// address_w - write to the address port (A0=0) +//------------------------------------------------- + +void y8950_device::address_w(u8 value) +{ + m_address = value; +} + + +//------------------------------------------------- +// data_w - write to the data port (A0=1) +//------------------------------------------------- + +void y8950_device::data_w(u8 value) +{ + // force an update + m_stream->update(); + + // handle special addresses + switch (m_address) + { + case 0x04: // IRQ control + m_fm.write(m_address, value); + combine_status(); + break; + + case 0x06: // keyboard out + m_keyboard_write_handler(0, value); + break; + + case 0x08: // split FM/ADPCM-B + m_adpcm_b.write(m_address - 0x07, (value & 0x0f) | 0x80); + m_fm.write(m_address, value & 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, value); + break; + + case 0x18: // I/O direction + m_io_ddr = value & 0x0f; + break; + + case 0x19: // I/O data + m_io_write_handler(0, value & m_io_ddr); + break; + + default: // everything else to FM + m_fm.write(m_address, value); + break; + } +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void y8950_device::write(offs_t offset, u8 value) +{ + // A0 selects between address/data + if ((offset & 1) == 0) + address_w(value); + else + data_w(value); +} + + +//------------------------------------------------- +// device_start - start of emulation +//------------------------------------------------- + +void y8950_device::device_start() +{ + // create our stream + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); + + // resolve callbacks + m_keyboard_read_handler.resolve_safe(0); + m_keyboard_write_handler.resolve_safe(); + m_io_read_handler.resolve_safe(0); + m_io_write_handler.resolve_safe(); + + // save our data + save_item(YMFM_NAME(m_address)); + save_item(YMFM_NAME(m_io_ddr)); + + // save the engines + m_fm.save(*this); + m_adpcm_b.save(*this); +} + + +//------------------------------------------------- +// device_reset - start of emulation +//------------------------------------------------- + +void y8950_device::device_reset() +{ + // reset the engines + m_fm.reset(); + m_adpcm_b.reset(); +} + + +//------------------------------------------------- +// device_clock_changed - update if clock changes +//------------------------------------------------- + +void y8950_device::device_clock_changed() +{ + m_stream->set_sample_rate(m_fm.sample_rate(clock())); +} + + +//------------------------------------------------- +// rom_bank_updated - refresh the stream if the +// ROM banking changes +//------------------------------------------------- + +void y8950_device::rom_bank_updated() +{ + m_stream->update(); +} + + +//------------------------------------------------- +// sound_stream_update - update the sound stream +//------------------------------------------------- + +void y8950_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + // iterate over all target samples + for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // clock the ADPCM-B engine every cycle + m_adpcm_b.clock(0x01); + + // update the FM content; clipping is unknown + s32 sums[std::max(fm_engine::OUTPUTS, ymadpcm_b_engine::OUTPUTS)] = { 0 }; + m_fm.output(sums, 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(sums, 3, 0x01); + + // convert to 10.3 floating point value for the DAC and back + // Y8950 is mono + for (int index = 0; index < fm_engine::OUTPUTS; index++) + outputs[index].put_int(sampindex, ymfm_roundtrip_fp(sums[index]), 32768); + } + + // update the status in case of ADPCM EOS + combine_status(); +} + + +//------------------------------------------------- +// combine_status - combine status flags from +// OPN and ADPCM-B, masking out any indicated by +// the flag control register +//------------------------------------------------- + +u8 y8950_device::combine_status() +{ + // start with current FM status, masking out bits we might set + u8 status = m_fm.status() & ~(STATUS_ADPCM_B_EOS | STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_PLAYING); + + // insert the live ADPCM status bits + u8 adpcm_status = m_adpcm_b.status(); + if ((adpcm_status & ymadpcm_b_channel::STATUS_EOS) != 0) + status |= STATUS_ADPCM_B_EOS; + if ((adpcm_status & ymadpcm_b_channel::STATUS_BRDY) != 0) + status |= STATUS_ADPCM_B_BRDY; + if ((adpcm_status & ymadpcm_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); +} + + +//------------------------------------------------- +// adpcm_b_read - callback to read data for the +// ADPCM-B engine; in this case, from our default +// address space +//------------------------------------------------- + +u8 y8950_device::adpcm_b_read(offs_t offset) +{ + return read_byte(offset); +} + + +//------------------------------------------------- +// adpcm_b_write - callback to write data to the +// ADPCM-B engine; in this case, to our default +// address space +//------------------------------------------------- + +void y8950_device::adpcm_b_write(offs_t offset, u8 data) +{ + space().write_byte(offset, data); +} diff --git a/src/devices/sound/y8950.h b/src/devices/sound/y8950.h new file mode 100644 index 00000000000..d3285ee835d --- /dev/null +++ b/src/devices/sound/y8950.h @@ -0,0 +1,81 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +#ifndef MAME_SOUND_Y8950_H +#define MAME_SOUND_Y8950_H + +#pragma once + +#include "ymfm.h" +#include "ymadpcm.h" + + +// ======================> y8950_device + +DECLARE_DEVICE_TYPE(Y8950, y8950_device); + +class y8950_device : public device_t, public device_sound_interface, public device_rom_interface<21> +{ +public: + // YM2151 is OPL + using fm_engine = ymopl_engine; + + static constexpr u8 STATUS_ADPCM_B_PLAYING = 0x01; + static constexpr u8 STATUS_ADPCM_B_BRDY = 0x08; + static constexpr u8 STATUS_ADPCM_B_EOS = 0x10; + static constexpr u8 ALL_IRQS = STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_EOS | fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB; + + // constructor + y8950_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = Y8950); + + // configuration helpers + auto irq_handler() { return m_fm.irq_handler(); } + auto keyboard_read() { return m_keyboard_read_handler.bind(); } + auto keyboard_write() { return m_keyboard_write_handler.bind(); } + auto io_read() { return m_io_read_handler.bind(); } + auto io_write() { return m_io_write_handler.bind(); } + + // read access + u8 status_r(); // A0=0 + u8 data_r(); // A0=1 + u8 read(offs_t offset); + + // write access + void address_w(u8 data); // A0=0 + void data_w(u8 data); // A0=1 + void write(offs_t offset, u8 data); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_clock_changed() override; + + // ROM device overrides + virtual void rom_bank_updated() override; + + // sound overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + +private: + // combine ADPCM and OPN statuses + u8 combine_status(); + + // ADPCM read/write callbacks + u8 adpcm_b_read(offs_t address); + void adpcm_b_write(offs_t address, u8 data); + + // internal state + u8 m_address; // address register + u8 m_io_ddr; // data direction register for I/O + sound_stream *m_stream; // sound stream + fm_engine m_fm; // core FM engine + ymadpcm_b_engine m_adpcm_b; // ADPCM-B engine + devcb_read8 m_keyboard_read_handler; // keyboard port read + devcb_write8 m_keyboard_write_handler; // keyboard port write + devcb_read8 m_io_read_handler; // I/O port read + devcb_write8 m_io_write_handler; // I/O port write +}; + + +#endif // MAME_SOUND_Y8950_H diff --git a/src/devices/sound/ym2151.cpp b/src/devices/sound/ym2151.cpp index b4769341425..a8e0bf4ab7a 100644 --- a/src/devices/sound/ym2151.cpp +++ b/src/devices/sound/ym2151.cpp @@ -10,51 +10,6 @@ DEFINE_DEVICE_TYPE(YM2164, ym2164_device, "ym2164", "YM2164 OPP") DEFINE_DEVICE_TYPE(YM2414, ym2414_device, "ym2414", "YM2414 OPZ") -//********************************************************* -// INLINE HELPERS -//********************************************************* - -//------------------------------------------------- -// linear_to_fp - given a 32-bit signed input -// value, convert it to a signed 10.3 floating- -// point value -//------------------------------------------------- - -inline s16 linear_to_fp(s32 value) -{ - // start with the absolute value - s32 avalue = std::abs(value); - - // compute shift to fit in 9 bits (bit 10 is the sign) - int shift = (32 - 9) - count_leading_zeros(avalue); - - // if out of range, just return maximum; note that YM3012 DAC does - // not support a shift count of 7, so we clamp at 6 - if (shift >= 7) - shift = 6, avalue = 0x1ff; - else if (shift > 0) - avalue >>= shift; - else - shift = 0; - - // encode with shift in low 3 bits and signed mantissa in upper - return shift | (((value < 0) ? -avalue : avalue) << 3); -} - - -//------------------------------------------------- -// fp_to_linear - given a 10.3 floating-point -// value, convert it to a signed 16-bit value, -// clamping -//------------------------------------------------- - -inline s32 fp_to_linear(s16 value) -{ - return (value >> 3) << BIT(value, 0, 3); -} - - - //********************************************************* // YM2151 DEVICE //********************************************************* @@ -66,10 +21,10 @@ inline s32 fp_to_linear(s16 value) ym2151_device::ym2151_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type) : device_t(mconfig, type, tag, owner, clock), device_sound_interface(mconfig, *this), - m_opm(*this), + m_fm(*this), m_stream(nullptr), m_port_w(*this), - m_busy_duration(m_opm.compute_busy_duration()), + m_busy_duration(m_fm.compute_busy_duration()), m_address(0), m_reset_state(1) { @@ -89,8 +44,8 @@ u8 ym2151_device::read(offs_t offset) logerror("Unexpected read from YM2151 offset %d\n", offset & 3); break; - case 1: // status port, YM2203 compatible - result = m_opm.status(); + case 1: // status port, YM2203 compatible + result = m_fm.status(); break; } return result; @@ -119,14 +74,14 @@ void ym2151_device::write(offs_t offset, u8 value) // force an update m_stream->update(); - // write to OPM - m_opm.write(m_address, value); + // write to FM + m_fm.write(m_address, value); // special cases if (m_address == 0x01 && BIT(value, 1)) { // writes to the test register can reset the LFO - m_opm.reset_lfo(); + m_fm.reset_lfo(); } else if (m_address == 0x1b) { @@ -135,7 +90,7 @@ void ym2151_device::write(offs_t offset, u8 value) } // mark busy for a bit - m_opm.set_busy_end(machine().time() + m_busy_duration); + m_fm.set_busy_end(machine().time() + m_busy_duration); break; } } @@ -161,7 +116,7 @@ WRITE_LINE_MEMBER(ym2151_device::reset_w) void ym2151_device::device_start() { // create our stream - m_stream = stream_alloc(0, 2, clock() / (2 * 4 * 8)); + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); // resolve the write callback m_port_w.resolve_safe(); @@ -174,7 +129,7 @@ void ym2151_device::device_start() save_item(YMFM_NAME(m_reset_state)); // save the engines - m_opm.save(*this); + m_fm.save(*this); } @@ -185,7 +140,7 @@ void ym2151_device::device_start() void ym2151_device::device_reset() { // reset the engines - m_opm.reset(); + m_fm.reset(); } @@ -195,8 +150,8 @@ void ym2151_device::device_reset() void ym2151_device::device_clock_changed() { - m_stream->set_sample_rate(clock() / (2 * 4 * 8)); - m_busy_duration = m_opm.compute_busy_duration(); + m_stream->set_sample_rate(m_fm.sample_rate(clock())); + m_busy_duration = m_fm.compute_busy_duration(); } @@ -210,16 +165,16 @@ void ym2151_device::sound_stream_update(sound_stream &stream, std::vectorupdate(); - // write to OPM - m_opm.write(m_address, value); + // write to FM + m_fm.write(m_address, value); // writes to register 0x1B send the upper 2 bits to the output lines if (m_address == 0x1b) m_port_w(0, value >> 6, 0xff); // mark busy for a bit - m_opm.set_busy_end(machine().time() + m_busy_duration); + m_fm.set_busy_end(machine().time() + m_busy_duration); break; } } diff --git a/src/devices/sound/ym2151.h b/src/devices/sound/ym2151.h index ee400df37c4..d48a5939071 100644 --- a/src/devices/sound/ym2151.h +++ b/src/devices/sound/ym2151.h @@ -16,11 +16,14 @@ DECLARE_DEVICE_TYPE(YM2151, ym2151_device); class ym2151_device : public device_t, public device_sound_interface { public: + // YM2151 is OPM + using fm_engine = ymopm_engine; + // constructor ym2151_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YM2151); // configuration helpers - auto irq_handler() { return m_opm.irq_handler(); } + auto irq_handler() { return m_fm.irq_handler(); } auto port_write_handler() { return m_port_w.bind(); } // read/write access @@ -43,7 +46,7 @@ protected: virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; // internal state - ymopm_engine m_opm; // core OPM engine + fm_engine m_fm; // core FM engine sound_stream *m_stream; // sound stream devcb_write8 m_port_w; // port write handler attotime m_busy_duration; // precomputed busy signal duration diff --git a/src/devices/sound/ym2203.cpp b/src/devices/sound/ym2203.cpp index 9d3f01261c1..d2e5e61fb03 100644 --- a/src/devices/sound/ym2203.cpp +++ b/src/devices/sound/ym2203.cpp @@ -8,51 +8,6 @@ DEFINE_DEVICE_TYPE(YM2203, ym2203_device, "ym2203", "YM2203 OPN") -//********************************************************* -// INLINE HELPERS -//********************************************************* - -//------------------------------------------------- -// linear_to_fp - given a 32-bit signed input -// value, convert it to a signed 10.3 floating- -// point value -//------------------------------------------------- - -inline s16 linear_to_fp(s32 value) -{ - // start with the absolute value - s32 avalue = std::abs(value); - - // compute shift to fit in 9 bits (bit 10 is the sign) - int shift = (32 - 9) - count_leading_zeros(avalue); - - // if out of range, just return maximum; note that YM3012 DAC does - // not support a shift count of 7, so we clamp at 6 - if (shift >= 7) - shift = 6, avalue = 0x1ff; - else if (shift > 0) - avalue >>= shift; - else - shift = 0; - - // encode with shift in low 3 bits and signed mantissa in upper - return shift | (((value < 0) ? -avalue : avalue) << 3); -} - - -//------------------------------------------------- -// fp_to_linear - given a 10.3 floating-point -// value, convert it to a signed 16-bit value, -// clamping -//------------------------------------------------- - -inline s32 fp_to_linear(s16 value) -{ - return (value >> 3) << BIT(value, 0, 3); -} - - - //********************************************************* // YM2203 DEVICE //********************************************************* @@ -63,9 +18,9 @@ inline s32 fp_to_linear(s16 value) ym2203_device::ym2203_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ay8910_device(mconfig, YM2203, tag, owner, clock, PSG_TYPE_YM, 3, 2), - m_opn(*this), + m_fm(*this), m_stream(nullptr), - m_busy_duration(m_opn.compute_busy_duration()), + m_busy_duration(m_fm.compute_busy_duration()), m_address(0) { } @@ -80,8 +35,8 @@ u8 ym2203_device::read(offs_t offset) u8 result = 0; switch (offset & 1) { - case 0: // status port - result = m_opn.status(); + case 0: // status port + result = m_fm.status(); break; case 1: // data port (only SSG) @@ -114,7 +69,7 @@ void ym2203_device::write(offs_t offset, u8 value) // prescaler select : 2d,2e,2f if (m_address == 0x2d) update_prescale(6); - else if (m_address == 0x2e && m_opn.clock_prescale() == 6) + else if (m_address == 0x2e && m_fm.clock_prescale() == 6) update_prescale(3); else if (m_address == 0x2f) update_prescale(2); @@ -129,13 +84,13 @@ void ym2203_device::write(offs_t offset, u8 value) } else { - // write to OPN + // write to FM m_stream->update(); - m_opn.write(m_address, value); + m_fm.write(m_address, value); } // mark busy for a bit - m_opn.set_busy_end(machine().time() + m_busy_duration); + m_fm.set_busy_end(machine().time() + m_busy_duration); break; } } @@ -151,13 +106,13 @@ void ym2203_device::device_start() ay8910_device::device_start(); // create our stream - m_stream = stream_alloc(0, 1, clock() / (4 * 3 * 6)); + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); // save our data save_item(YMFM_NAME(m_address)); - // save the OPN engine - m_opn.save(*this); + // save the FM engine + m_fm.save(*this); } @@ -170,8 +125,8 @@ void ym2203_device::device_reset() // reset the SSG device ay8910_device::device_reset(); - // reset the OPN engine - m_opn.reset(); + // reset the FM engine + m_fm.reset(); } @@ -182,7 +137,7 @@ void ym2203_device::device_reset() void ym2203_device::device_clock_changed() { // refresh via prescale - update_prescale(m_opn.clock_prescale()); + update_prescale(m_fm.clock_prescale()); } @@ -203,15 +158,16 @@ void ym2203_device::sound_stream_update(sound_stream &stream, std::vectorset_sample_rate(clock() / (4 * 3 * newval)); - logerror("Prescale = %d; sample_rate = %d\n", newval, clock() / (4 * 3 * newval)); + // inform the FM engine and refresh our clock rate + m_fm.set_clock_prescale(newval); + m_stream->set_sample_rate(m_fm.sample_rate(clock())); + logerror("Prescale = %d; sample_rate = %d\n", newval, m_fm.sample_rate(clock())); // also scale the SSG streams - // mapping is (OPN->SSG): 6->4, 3->2, 2->1 + // mapping is (FM->SSG): 6->4, 3->2, 2->1 u8 ssg_scale = 2 * newval / 3; // QUESTION: where does the *2 come from?? ay_set_clock(clock() * 2 / ssg_scale); // recompute the busy duration - m_busy_duration = m_opn.compute_busy_duration(); + m_busy_duration = m_fm.compute_busy_duration(); } diff --git a/src/devices/sound/ym2203.h b/src/devices/sound/ym2203.h index f4bff36c692..dbce4549340 100644 --- a/src/devices/sound/ym2203.h +++ b/src/devices/sound/ym2203.h @@ -17,11 +17,14 @@ DECLARE_DEVICE_TYPE(YM2203, ym2203_device); class ym2203_device : public ay8910_device { public: + // YM2151 is OPN + using fm_engine = ymopn_engine; + // constructor ym2203_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); // configuration helpers - auto irq_handler() { return m_opn.irq_handler(); } + auto irq_handler() { return m_fm.irq_handler(); } // read/write access u8 read(offs_t offset); @@ -47,7 +50,7 @@ private: void update_prescale(u8 newval); // internal state - ymopn_engine m_opn; // core OPN engine + fm_engine m_fm; // core FM engine sound_stream *m_stream; // sound stream attotime m_busy_duration; // precomputed busy signal duration u8 m_address; // address register diff --git a/src/devices/sound/ym2413.cpp b/src/devices/sound/ym2413.cpp index 11f4ea2e9de..e680b4a31ff 100644 --- a/src/devices/sound/ym2413.cpp +++ b/src/devices/sound/ym2413.cpp @@ -1,1782 +1,253 @@ -// license:GPL-2.0+ -// copyright-holders:Jarek Burczynski, Ernesto Corvi -/* -** -** File: ym2413.cpp - software implementation of YM2413 -** FM sound generator type OPLL -** -** Copyright Jarek Burczynski -** -** Version 1.0 -** - - Features as listed in LSI-212413A2 data sheet: - 1. FM Sound Generator for real sound creation. - 2. Two Selectable modes: 9 simultaneous sounds or 6 melody sounds plus 5 rhythm sounds - (different tones can be used together in either case). - 3. Built-in Instruments data (15 melody tones, 5 rhythm tones, "CAPTAIN and TELETEXT applicalbe tones). - 4. Built-in DA Converter. - 5. Built-in Quartz Oscillator. - 6. Built-in Vibrato Oscillator/AM Oscillator - 7. TTL Compatible Input. - 8. Si-Gate NMOS LSI - 9. A single 5V power source. - -to do: - -- make sure of the sinus amplitude bits - -- make sure of the EG resolution bits (looks like the biggest - modulation index generated by the modulator is 123, 124 = no modulation) -- find proper algorithm for attack phase of EG - -- tune up instruments ROM - -- support sample replay in test mode (it is NOT as simple as setting bit 0 - in register 0x0f and using register 0x10 for sample data). - Which games use this feature ? - - -*/ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles #include "emu.h" #include "ym2413.h" -#include - -#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ -#define EG_SH 16 /* 16.16 fixed point (EG timing) */ -#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ - -#define FREQ_MASK ((1<>5) - - -/* LFO Amplitude Modulation table (verified on real YM3812) - 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples - Length: 210 elements. +DEFINE_DEVICE_TYPE(YM2413, ym2413_device, "ym2413", "YM2413 OPLL") +DEFINE_DEVICE_TYPE(YM2423, ym2423_device, "ym2423", "YM2423 OPLL-X") +DEFINE_DEVICE_TYPE(YMF281, ymf281_device, "ymf281", "YMF281 OPLLP") +DEFINE_DEVICE_TYPE(DS1001, ds1001_device, "ds1001", "Yamaha DS1001 / Konami 053982") - Each of the elements has to be repeated - exactly 64 times (on 64 consecutive samples). - The whole table takes: 64 * 210 = 13440 samples. -We use data>>1, until we find what it really is on real chip... - -*/ -const uint8_t ym2413_device::lfo_am_table[LFO_AM_TAB_ELEMENTS] = { - 0,0,0,0,0,0,0, - 1,1,1,1, - 2,2,2,2, - 3,3,3,3, - 4,4,4,4, - 5,5,5,5, - 6,6,6,6, - 7,7,7,7, - 8,8,8,8, - 9,9,9,9, - 10,10,10,10, - 11,11,11,11, - 12,12,12,12, - 13,13,13,13, - 14,14,14,14, - 15,15,15,15, - 16,16,16,16, - 17,17,17,17, - 18,18,18,18, - 19,19,19,19, - 20,20,20,20, - 21,21,21,21, - 22,22,22,22, - 23,23,23,23, - 24,24,24,24, - 25,25,25,25, - 26,26,26, - 25,25,25,25, - 24,24,24,24, - 23,23,23,23, - 22,22,22,22, - 21,21,21,21, - 20,20,20,20, - 19,19,19,19, - 18,18,18,18, - 17,17,17,17, - 16,16,16,16, - 15,15,15,15, - 14,14,14,14, - 13,13,13,13, - 12,12,12,12, - 11,11,11,11, - 10,10,10,10, - 9,9,9,9, - 8,8,8,8, - 7,7,7,7, - 6,6,6,6, - 5,5,5,5, - 4,4,4,4, - 3,3,3,3, - 2,2,2,2, - 1,1,1,1 -}; - -/* LFO Phase Modulation table (verified on real YM2413) */ -const int8_t ym2413_device::lfo_pm_table[8*8] = { - /* FNUM2/FNUM = 0 00xxxxxx (0x0000) */ - 0, 0, 0, 0, 0, 0, 0, 0, - - /* FNUM2/FNUM = 0 01xxxxxx (0x0040) */ - 1, 0, 0, 0,-1, 0, 0, 0, - - /* FNUM2/FNUM = 0 10xxxxxx (0x0080) */ - 2, 1, 0,-1,-2,-1, 0, 1, - - /* FNUM2/FNUM = 0 11xxxxxx (0x00C0) */ - 3, 1, 0,-1,-3,-1, 0, 1, - - /* FNUM2/FNUM = 1 00xxxxxx (0x0100) */ - 4, 2, 0,-2,-4,-2, 0, 2, - - /* FNUM2/FNUM = 1 01xxxxxx (0x0140) */ - 5, 2, 0,-2,-5,-2, 0, 2, - - /* FNUM2/FNUM = 1 10xxxxxx (0x0180) */ - 6, 3, 0,-3,-6,-3, 0, 3, - - /* FNUM2/FNUM = 1 11xxxxxx (0x01C0) */ - 7, 3, 0,-3,-7,-3, 0, 3, -}; - - -/* This is not 100% perfect yet but very close */ -/* - - multi parameters are 100% correct (instruments and drums) - - LFO PM and AM enable are 100% correct - - waveform DC and DM select are 100% correct -*/ - -const uint8_t ym2413_device::table[19][8] = { -/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */ -/* 0 1 2 3 4 5 6 7 */ -/* These YM2413(OPLL) patch dumps are done via audio analysis (and a/b testing?) from Jarek and are known to be inaccurate */ - {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, //0 - - {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, //1 - {0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, //2 - {0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, //3 - {0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, //4 - -//{0x22, 0x21, 0x1e, 0x09, 0xf0, 0x76, 0x08, 0x28 }, //5 - {0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, //5 - -//{0x31, 0x22, 0x16, 0x09, 0x90, 0x7f, 0x00, 0x08 }, //6 - {0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, //6 - - {0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, //7 - {0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, //8 - {0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, //9 - -//{0x61, 0x61, 0x0c, 0x08, 0x85, 0xa0, 0x79, 0x07 }, //A - {0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, //A - - {0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, //B - {0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, //C - -//{0x61, 0x10, 0x0c, 0x08, 0xf2, 0xc4, 0x40, 0xc8 }, //D - {0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, //D - - {0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, //E - {0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, //F - -/* drum instruments definitions */ -/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */ -/* 0 1 2 3 4 5 6 7 */ -/* old dumps via audio analysis (and a/b testing?) from Jarek */ -//{0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */ -//{0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */ -//{0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */ -/* Drums dumped from the VRC7 using debug mode, these are likely also correct for ym2413(OPLL) but need verification */ - {0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d },/* BD */ - {0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68 },/* HH, SD */ - {0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55 },/* TOM, TOP CYM */ -}; - -// VRC7 Instruments : Dumped from internal ROM -// reference : https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2 -const uint8_t vrc7snd_device::vrc7_table[19][8] = { -/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */ -/* 0 1 2 3 4 5 6 7 */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //0 (This is the user-defined instrument, should this default to anything?) - - {0x03, 0x21, 0x05, 0x06, 0xe8, 0x81, 0x42, 0x27 }, //1 - {0x13, 0x41, 0x14, 0x0d, 0xd8, 0xf6, 0x23, 0x12 }, //2 - {0x11, 0x11, 0x08, 0x08, 0xfa, 0xb2, 0x20, 0x12 }, //3 - {0x31, 0x61, 0x0c, 0x07, 0xa8, 0x64, 0x61, 0x27 }, //4 - {0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28 }, //5 - {0x02, 0x01, 0x06, 0x00, 0xa3, 0xe2, 0xf4, 0xf4 }, //6 - {0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07 }, //7 - {0x23, 0x21, 0x22, 0x17, 0xa2, 0x72, 0x01, 0x17 }, //8 - {0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01 }, //9 - {0xb5, 0x01, 0x0f, 0x0f, 0xa8, 0xa5, 0x51, 0x02 }, //A - {0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12 }, //B - {0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16 }, //C - {0x01, 0x02, 0xd3, 0x05, 0xc9, 0x95, 0x03, 0x02 }, //D - {0x61, 0x63, 0x0c, 0x00, 0x94, 0xc0, 0x33, 0xf6 }, //E - {0x21, 0x72, 0x0d, 0x00, 0xc1, 0xd5, 0x56, 0x06 }, //F - -/* Drums (silent due to no RO output pin(?) on VRC7, but present internally; these are probably shared with YM2413) */ -/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */ -/* 0 1 2 3 4 5 6 7 */ - {0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d },/* BD */ - {0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68 },/* HH, SD */ - {0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55 },/* TOM, TOP CYM */ -}; - -/* work table */ -#define SLOT7_1 (&P_CH[7].SLOT[SLOT1]) -#define SLOT7_2 (&P_CH[7].SLOT[SLOT2]) -#define SLOT8_1 (&P_CH[8].SLOT[SLOT1]) -#define SLOT8_2 (&P_CH[8].SLOT[SLOT2]) - - -int ym2413_device::limit( int val, int max, int min ) -{ - if ( val > max ) - val = max; - else if ( val < min ) - val = min; - - return val; -} +//********************************************************* +// YM2413 DEVICE +//********************************************************* +//------------------------------------------------- +// ym2413_device - constructor +//------------------------------------------------- -/* advance LFO to next sample */ -void ym2413_device::advance_lfo() +ym2413_device::ym2413_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type, u8 const *instruments) : + device_t(mconfig, type, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_address(0), + m_stream(nullptr), + m_internal(*this, "internal"), + m_fm(*this) { - /* LFO */ - lfo_am_cnt += lfo_am_inc; - if (lfo_am_cnt >= ((uint32_t)LFO_AM_TAB_ELEMENTS<> LFO_SH ] >> 1; - - lfo_pm_cnt += lfo_pm_inc; - LFO_PM = (lfo_pm_cnt>>LFO_SH) & 7; } -/* advance to next sample */ -void ym2413_device::advance() -{ - OPLL_CH *CH; - OPLL_SLOT *op; - unsigned int i; - - /* Envelope Generator */ - eg_timer += eg_timer_add; - - while (eg_timer >= eg_timer_overflow) - { - eg_timer -= eg_timer_overflow; - - eg_cnt++; - - for (i=0; i<9*2; i++) - { - CH = &P_CH[i/2]; - - op = &CH->SLOT[i&1]; - - switch(op->state) - { - case EG_DMP: /* dump phase */ - /*dump phase is performed by both operators in each channel*/ - /*when CARRIER envelope gets down to zero level, - ** phases in BOTH opearators are reset (at the same time ?) - */ - if ( !(eg_cnt & ((1<eg_sh_dp)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_dp + ((eg_cnt>>op->eg_sh_dp)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_ATT; - /* restart Phase Generator */ - op->phase = 0; - } - } - break; - - case EG_ATT: /* attack phase */ - if ( !(eg_cnt & ((1<eg_sh_ar)-1) ) ) - { - op->volume += (~op->volume * - (eg_inc[op->eg_sel_ar + ((eg_cnt>>op->eg_sh_ar)&7)]) - ) >>2; - - if (op->volume <= MIN_ATT_INDEX) - { - op->volume = MIN_ATT_INDEX; - op->state = EG_DEC; - } - } - break; - - case EG_DEC: /* decay phase */ - if ( !(eg_cnt & ((1<eg_sh_dr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_dr + ((eg_cnt>>op->eg_sh_dr)&7)]; - if ( op->volume >= op->sl ) - op->state = EG_SUS; - } - break; - - case EG_SUS: /* sustain phase */ - /* this is important behaviour: - one can change percusive/non-percussive modes on the fly and - the chip will remain in sustain phase - verified on real YM3812 */ - - if(op->eg_type) /* non-percussive mode (sustained tone) */ - { - /* do nothing */ - } - else /* percussive mode */ - { - /* during sustain phase chip adds Release Rate (in percussive mode) */ - if ( !(eg_cnt & ((1<eg_sh_rr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((eg_cnt>>op->eg_sh_rr)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - op->volume = MAX_ATT_INDEX; - } - /* else do nothing in sustain phase */ - } - break; - - case EG_REL: /* release phase */ - /* exclude modulators in melody channels from performing anything in this mode*/ - /* allowed are only carriers in melody mode and rhythm slots in rhythm mode */ - - /*This table shows which operators and on what conditions are allowed to perform EG_REL: - (a) - always perform EG_REL - (n) - never perform EG_REL - (r) - perform EG_REL in Rhythm mode ONLY - 0: 0 (n), 1 (a) - 1: 2 (n), 3 (a) - 2: 4 (n), 5 (a) - 3: 6 (n), 7 (a) - 4: 8 (n), 9 (a) - 5: 10(n), 11(a) - 6: 12(r), 13(a) - 7: 14(r), 15(a) - 8: 16(r), 17(a) - */ - if ( (i&1) || ((rhythm&0x20) && (i>=12)) )/* exclude modulators */ - { - if(op->eg_type) /* non-percussive mode (sustained tone) */ - /*this is correct: use RR when SUS = OFF*/ - /*and use RS when SUS = ON*/ - { - if (CH->sus) - { - if ( !(eg_cnt & ((1<eg_sh_rs)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rs + ((eg_cnt>>op->eg_sh_rs)&7)]; - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - } - } - else - { - if ( !(eg_cnt & ((1<eg_sh_rr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((eg_cnt>>op->eg_sh_rr)&7)]; - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - } - } - } - else /* percussive mode */ - { - if ( !(eg_cnt & ((1<eg_sh_rs)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rs + ((eg_cnt>>op->eg_sh_rs)&7)]; - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - } - } - } - break; - - default: - break; - } - } - } - - for (i=0; i<9*2; i++) - { - CH = &P_CH[i/2]; - op = &CH->SLOT[i&1]; - - /* Phase Generator */ - if(op->vib) - { - uint8_t block; - - unsigned int fnum_lfo = 8*((CH->block_fnum&0x01c0) >> 6); - unsigned int block_fnum = CH->block_fnum * 2; - signed int lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + fnum_lfo ]; - - if (lfo_fn_table_index_offset) /* LFO phase modulation active */ - { - block_fnum += lfo_fn_table_index_offset; - block = (block_fnum&0x1c00) >> 10; - op->phase += (fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; - } - else /* LFO phase modulation = zero */ - { - op->phase += op->freq; - } - } - else /* LFO phase modulation disabled for this operator */ - { - op->phase += op->freq; - } - } - - /* The Noise Generator of the YM3812 is 23-bit shift register. - * Period is equal to 2^23-2 samples. - * Register works at sampling frequency of the chip, so output - * can change on every sample. - * - * Output of the register and input to the bit 22 is: - * bit0 XOR bit14 XOR bit15 XOR bit22 - * - * Simply use bit 22 as the noise output. - */ - - noise_p += noise_f; - i = noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ - noise_p &= FREQ_MASK; - while (i) - { - /* - uint32_t j; - j = ( (noise_rng) ^ (noise_rng>>14) ^ (noise_rng>>15) ^ (noise_rng>>22) ) & 1; - noise_rng = (j<<22) | (noise_rng>>1); - */ - - /* - Instead of doing all the logic operations above, we - use a trick here (and use bit 0 as the noise output). - The difference is only that the noise bit changes one - step ahead. This doesn't matter since we don't know - what is real state of the noise_rng after the reset. - */ - - if (noise_rng & 1) noise_rng ^= 0x800302; - noise_rng >>= 1; - - i--; - } -} - - -int ym2413_device::op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) -{ - uint32_t p; - - p = (env<<5) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<17))) >> FREQ_SH ) & SIN_MASK) ]; - - if (p >= TL_TAB_LEN) - return 0; - return tl_tab[p]; -} +//------------------------------------------------- +// address_w - write to the address port (A0=0) +//------------------------------------------------- -int ym2413_device::op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +void ym2413_device::address_w(u8 value) { - uint32_t p; - int32_t i; - - i = (phase & ~FREQ_MASK) + pm; - -/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/ - - p = (env<<5) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)]; - -/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/ - - if (p >= TL_TAB_LEN) - return 0; - return tl_tab[p]; + m_address = value; } -#define volume_calc(OP) ((OP)->TLL + ((uint32_t)(OP)->volume) + (LFO_AM & (OP)->AMmask)) +//------------------------------------------------- +// data_w - write to the data port (A0=1) +//------------------------------------------------- -/* calculate output */ -void ym2413_device::chan_calc( OPLL_CH *CH ) +void ym2413_device::data_w(u8 value) { - OPLL_SLOT *SLOT; - unsigned int env; - signed int out; - signed int phase_modulation; /* phase modulation input (SLOT 2) */ - - - /* SLOT 1 */ - SLOT = &CH->SLOT[SLOT1]; - env = volume_calc(SLOT); - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - - SLOT->op1_out[0] = SLOT->op1_out[1]; - phase_modulation = SLOT->op1_out[0]; - - SLOT->op1_out[1] = 0; - - if( env < ENV_QUIET ) - { - if (!SLOT->fb_shift) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable ); - } + // force an update + m_stream->update(); - /* SLOT 2 */ - SLOT++; - env = volume_calc(SLOT); - if( env < ENV_QUIET ) - { - output[0] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); - } + // write to FM + m_fm.write(m_address, value); } -/* - operators used in the rhythm sounds generation process: - Envelope Generator: - -channel operator register number Bass High Snare Tom Top -/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal - 6 / 0 12 50 70 90 f0 + - 6 / 1 15 53 73 93 f3 + - 7 / 0 13 51 71 91 f1 + - 7 / 1 16 54 74 94 f4 + - 8 / 0 14 52 72 92 f2 + - 8 / 1 17 55 75 95 f5 + - - Phase Generator: - -channel operator register number Bass High Snare Tom Top -/ slot number MULTIPLE Drum Hat Drum Tom Cymbal - 6 / 0 12 30 + - 6 / 1 15 33 + - 7 / 0 13 31 + + + - 7 / 1 16 34 ----- n o t u s e d ----- - 8 / 0 14 32 + - 8 / 1 17 35 + + - -channel operator register number Bass High Snare Tom Top -number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal - 6 12,15 B6 A6 + - - 7 13,16 B7 A7 + + + - - 8 14,17 B8 A8 + + + - -*/ - -/* calculate rhythm */ - -void ym2413_device::rhythm_calc( OPLL_CH *CH, unsigned int noise ) -{ - OPLL_SLOT *SLOT; - signed int out; - unsigned int env; - signed int phase_modulation; /* phase modulation input (SLOT 2) */ - - - /* Bass Drum (verified on real YM3812): - - depends on the channel 6 'connect' register: - when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) - when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored - - output sample always is multiplied by 2 - */ - - - /* SLOT 1 */ - SLOT = &CH[6].SLOT[SLOT1]; - env = volume_calc(SLOT); - - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - SLOT->op1_out[0] = SLOT->op1_out[1]; - - phase_modulation = SLOT->op1_out[0]; - - SLOT->op1_out[1] = 0; - if( env < ENV_QUIET ) - { - if (!SLOT->fb_shift) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable ); - } - - /* SLOT 2 */ - SLOT++; - env = volume_calc(SLOT); - if( env < ENV_QUIET ) - output[1] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable) * 2; - - - /* Phase generation is based on: */ - // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) - // SD (16) channel 7->slot 1 - // TOM (14) channel 8->slot 1 - // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) - - /* Envelope generation based on: */ - // HH channel 7->slot1 - // SD channel 7->slot2 - // TOM channel 8->slot1 - // TOP channel 8->slot2 - - - /* The following formulas can be well optimized. - I leave them in direct form for now (in case I've missed something). - */ - - /* High Hat (verified on real YM3812) */ - env = volume_calc(SLOT7_1); - if( env < ENV_QUIET ) - { - /* high hat phase generation: - phase = d0 or 234 (based on frequency only) - phase = 34 or 2d0 (based on noise) - */ - - /* base frequency derived from operator 1 in channel 7 */ - uint8_t bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; - uint8_t bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; - uint8_t bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; - - uint8_t res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0xd0; */ - /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ - uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; - - /* enable gate based on frequency of operator 2 in channel 8 */ - uint8_t bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; - uint8_t bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; - - uint8_t res2 = (bit3e | bit5e); - - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ - if (res2) - phase = (0x200|(0xd0>>2)); - - - /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ - /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ - if (phase&0x200) - { - if (noise) - phase = 0x200|0xd0; - } - else - /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ - /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ - { - if (noise) - phase = 0xd0>>2; - } - - output[1] += op_calc(phase<wavetable) * 2; - } - - /* Snare Drum (verified on real YM3812) */ - env = volume_calc(SLOT7_2); - if( env < ENV_QUIET ) - { - /* base frequency derived from operator 1 in channel 7 */ - uint8_t bit8 = ((SLOT7_1->phase>>FREQ_SH)>>8)&1; - - /* when bit8 = 0 phase = 0x100; */ - /* when bit8 = 1 phase = 0x200; */ - uint32_t phase = bit8 ? 0x200 : 0x100; - - /* Noise bit XOR'es phase by 0x100 */ - /* when noisebit = 0 pass the phase from calculation above */ - /* when noisebit = 1 phase ^= 0x100; */ - /* in other words: phase ^= (noisebit<<8); */ - if (noise) - phase ^= 0x100; - - output[1] += op_calc(phase<wavetable) * 2; - } - - /* Tom Tom (verified on real YM3812) */ - env = volume_calc(SLOT8_1); - if( env < ENV_QUIET ) - output[1] += op_calc(SLOT8_1->phase, env, 0, SLOT8_1->wavetable) * 2; - - /* Top Cymbal (verified on real YM2413) */ - env = volume_calc(SLOT8_2); - if( env < ENV_QUIET ) - { - /* base frequency derived from operator 1 in channel 7 */ - uint8_t bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; - uint8_t bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; - uint8_t bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; - - uint8_t res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0x100; */ - /* when res1 = 1 phase = 0x200 | 0x100; */ - uint32_t phase = res1 ? 0x300 : 0x100; - - /* enable gate based on frequency of operator 2 in channel 8 */ - uint8_t bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; - uint8_t bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; - - uint8_t res2 = (bit3e | bit5e); - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | 0x100; */ - if (res2) - phase = 0x300; - - output[1] += op_calc(phase<wavetable) * 2; - } - -} +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- -void ym2413_device::key_on(OPLL_SLOT *SLOT, uint32_t key_set) +void ym2413_device::write(offs_t offset, u8 value) { - if( !SLOT->key ) - { - /* do NOT restart Phase Generator (verified on real YM2413)*/ - /* phase -> Dump */ - SLOT->state = EG_DMP; - } - SLOT->key |= key_set; + // A0 selects between address/data + if ((offset & 1) == 0) + address_w(value); + else + data_w(value); } -void ym2413_device::key_off(OPLL_SLOT *SLOT, uint32_t key_clr) -{ - if( SLOT->key ) - { - SLOT->key &= key_clr; - if( !SLOT->key ) - { - /* phase -> Release */ - if (SLOT->state>EG_REL) - SLOT->state = EG_REL; - } - } -} +//------------------------------------------------- +// device_start - start of emulation +//------------------------------------------------- -/* update phase increment counter of operator (also update the EG rates if necessary) */ -void ym2413_device::calc_fcslot(OPLL_CH *CH, OPLL_SLOT *SLOT) +void ym2413_device::device_start() { - int ksr; - uint32_t SLOT_rs; - uint32_t SLOT_dp; - - /* (frequency) phase increment counter */ - SLOT->freq = CH->fc * SLOT->mul; - ksr = CH->kcode >> SLOT->KSR; - - if( SLOT->ksr != ksr ) - { - SLOT->ksr = ksr; + // create our stream + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); - /* calculate envelope generator rates */ - if ((SLOT->ar + SLOT->ksr) < 16+62) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + // save our data + save_item(YMFM_NAME(m_address)); - } - - if (CH->sus) - SLOT_rs = 16 + (5<<2); - else - SLOT_rs = 16 + (7<<2); + // save the engines + m_fm.save(*this); - SLOT->eg_sh_rs = eg_rate_shift [SLOT_rs + SLOT->ksr ]; - SLOT->eg_sel_rs = eg_rate_select[SLOT_rs + SLOT->ksr ]; - - SLOT_dp = 16 + (13<<2); - SLOT->eg_sh_dp = eg_rate_shift [SLOT_dp + SLOT->ksr ]; - SLOT->eg_sel_dp = eg_rate_select[SLOT_dp + SLOT->ksr ]; + // set up the instrument data + m_fm.set_instrument_data(m_internal); } -/* set multi,am,vib,EG-TYP,KSR,mul */ -void ym2413_device::set_mul(int slot,int v) -{ - OPLL_CH *CH = &P_CH[slot/2]; - OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; - SLOT->mul = mul_tab[v&0x0f]; - SLOT->KSR = (v&0x10) ? 0 : 2; - SLOT->eg_type = (v&0x20); - SLOT->vib = (v&0x40); - SLOT->AMmask = (v&0x80) ? ~0 : 0; - calc_fcslot(CH,SLOT); -} +//------------------------------------------------- +// device_reset - start of emulation +//------------------------------------------------- -/* set ksl, tl */ -void ym2413_device::set_ksl_tl(int chan,int v) +void ym2413_device::device_reset() { - OPLL_CH *CH = &P_CH[chan]; -/* modulator */ - OPLL_SLOT *SLOT = &CH->SLOT[SLOT1]; - - SLOT->ksl = ksl_shift[v >> 6]; - SLOT->TL = (v&0x3f)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + // reset the engines + m_fm.reset(); } -/* set ksl , waveforms, feedback */ -void ym2413_device::set_ksl_wave_fb(int chan,int v) -{ - OPLL_CH *CH = &P_CH[chan]; -/* modulator */ - OPLL_SLOT *SLOT = &CH->SLOT[SLOT1]; - SLOT->wavetable = ((v&0x08)>>3)*SIN_LEN; - SLOT->fb_shift = (v&7) ? (v&7) + 8 : 0; - -/*carrier*/ - SLOT = &CH->SLOT[SLOT2]; - SLOT->ksl = ksl_shift[v >> 6]; - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - - SLOT->wavetable = ((v&0x10)>>4)*SIN_LEN; -} +//------------------------------------------------- +// device_clock_changed - update if clock changes +//------------------------------------------------- -/* set attack rate & decay rate */ -void ym2413_device::set_ar_dr(int slot,int v) +void ym2413_device::device_clock_changed() { - OPLL_CH *CH = &P_CH[slot/2]; - OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; - - if ((SLOT->ar + SLOT->ksr) < 16+62) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - - SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + m_stream->set_sample_rate(m_fm.sample_rate(clock())); } -/* set sustain level & release rate */ -void ym2413_device::set_sl_rr(int slot,int v) -{ - OPLL_CH *CH = &P_CH[slot/2]; - OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; - SLOT->sl = sl_tab[ v>>4 ]; +//------------------------------------------------- +// device_rom_region - return a pointer to our +// ROM region +//------------------------------------------------- - SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; -} +ROM_START( ym2413 ) + ROM_REGION( 0x90, "internal", 0 ) + // + // This is not the exact format + // + ROM_LOAD16_WORD( "ym2413_instruments.bin", 0x0000, 0x0090, CRC(6f582d01) SHA1(bb5537717e0b34849456b5ca7d405403dc3f8fda) ) +ROM_END -void ym2413_device::load_instrument(uint32_t chan, uint32_t slot, uint8_t* inst ) +const tiny_rom_entry *ym2413_device::device_rom_region() const { - set_mul (slot, inst[0]); - set_mul (slot+1, inst[1]); - set_ksl_tl (chan, inst[2]); - set_ksl_wave_fb (chan, inst[3]); - set_ar_dr (slot, inst[4]); - set_ar_dr (slot+1, inst[5]); - set_sl_rr (slot, inst[6]); - set_sl_rr (slot+1, inst[7]); + return ROM_NAME( ym2413 ); } -void ym2413_device::update_instrument_zero( uint8_t r ) -{ - uint8_t* inst = &inst_tab[0][0]; /* point to user instrument */ - uint32_t chan; - uint32_t chan_max; - - chan_max = 9; - if (rhythm & 0x20) - chan_max=6; - - switch(r) - { - case 0: - for (chan=0; chanSLOT[SLOT1]; /* modulator envelope is HH */ - SLOT->TL = ((instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - - /* Load instrument settings for channel nine. (Tom-tom and top cymbal) */ - chan = 8; - inst = &inst_tab[18][0]; - slot = chan*2; - - load_instrument(chan, slot, inst); - - CH = &P_CH[chan]; - SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is TOM */ - SLOT->TL = ((instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } - /* BD key on/off */ - if(v&0x10) - { - key_on (&P_CH[6].SLOT[SLOT1], 2); - key_on (&P_CH[6].SLOT[SLOT2], 2); - } - else - { - key_off(&P_CH[6].SLOT[SLOT1],~2); - key_off(&P_CH[6].SLOT[SLOT2],~2); - } - /* HH key on/off */ - if(v&0x01) key_on (&P_CH[7].SLOT[SLOT1], 2); - else key_off(&P_CH[7].SLOT[SLOT1],~2); - /* SD key on/off */ - if(v&0x08) key_on (&P_CH[7].SLOT[SLOT2], 2); - else key_off(&P_CH[7].SLOT[SLOT2],~2); - /* TOM key on/off */ - if(v&0x04) key_on (&P_CH[8].SLOT[SLOT1], 2); - else key_off(&P_CH[8].SLOT[SLOT1],~2); - /* TOP-CY key on/off */ - if(v&0x02) key_on (&P_CH[8].SLOT[SLOT2], 2); - else key_off(&P_CH[8].SLOT[SLOT2],~2); - } - else - { - if (rhythm&0x20) - /*rhythm on to off*/ - { - logerror("YM2413: Rhythm mode disable\n"); - /* Load instrument settings for channel seven(chan=6 since we're zero based).*/ - chan = 6; - inst = &inst_tab[instvol_r[chan]>>4][0]; - slot = chan*2; - - load_instrument(chan, slot, inst); - - /* Load instrument settings for channel eight.*/ - chan = 7; - inst = &inst_tab[instvol_r[chan]>>4][0]; - slot = chan*2; - - load_instrument(chan, slot, inst); - - /* Load instrument settings for channel nine.*/ - chan = 8; - inst = &inst_tab[instvol_r[chan]>>4][0]; - slot = chan*2; - - load_instrument(chan, slot, inst); - } - /* BD key off */ - key_off(&P_CH[6].SLOT[SLOT1],~2); - key_off(&P_CH[6].SLOT[SLOT2],~2); - /* HH key off */ - key_off(&P_CH[7].SLOT[SLOT1],~2); - /* SD key off */ - key_off(&P_CH[7].SLOT[SLOT2],~2); - /* TOM key off */ - key_off(&P_CH[8].SLOT[SLOT1],~2); - /* TOP-CY off */ - key_off(&P_CH[8].SLOT[SLOT2],~2); - } - rhythm = v&0x3f; - } - break; - } - } - break; - - case 0x10: - case 0x20: - { - int block_fnum; - - chan = r&0x0f; - - if (chan >= 9) - chan -= 9; /* verified on real YM2413 */ - - CH = &P_CH[chan]; - - if(r&0x10) - { /* 10-18: FNUM 0-7 */ - block_fnum = (CH->block_fnum&0x0f00) | v; - } - else - { /* 20-28: suson, keyon, block, FNUM 8 */ - block_fnum = ((v&0x0f)<<8) | (CH->block_fnum&0xff); - - if(v&0x10) - { - key_on (&CH->SLOT[SLOT1], 1); - key_on (&CH->SLOT[SLOT2], 1); - } - else - { - key_off(&CH->SLOT[SLOT1],~1); - key_off(&CH->SLOT[SLOT2],~1); - } - - - if (CH->sus!=(v&0x20)) - logerror("chan=%i sus=%2x\n",chan,v&0x20); - - CH->sus = v & 0x20; - } - /* update */ - if(CH->block_fnum != block_fnum) - { - uint8_t block; - - CH->block_fnum = block_fnum; - - /* BLK 2,1,0 bits -> bits 3,2,1 of kcode, FNUM MSB -> kcode LSB */ - CH->kcode = (block_fnum&0x0f00)>>8; - - CH->ksl_base = static_cast(ksl_tab[block_fnum>>5]); - - block_fnum = block_fnum * 2; - block = (block_fnum&0x1c00) >> 10; - CH->fc = fn_tab[block_fnum&0x03ff] >> (7-block); - - /* refresh Total Level in both SLOTs of this channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - - /* refresh frequency counter in both SLOTs of this channel */ - calc_fcslot(CH,&CH->SLOT[SLOT1]); - calc_fcslot(CH,&CH->SLOT[SLOT2]); - } - } - break; - - case 0x30: /* inst 4 MSBs, VOL 4 LSBs */ - { - uint8_t old_instvol; - - chan = r&0x0f; - - if (chan >= 9) - chan -= 9; /* verified on real YM2413 */ - - old_instvol = instvol_r[chan]; - instvol_r[chan] = v; /* store for later use */ - - CH = &P_CH[chan]; - SLOT = &CH->SLOT[SLOT2]; /* carrier */ - SLOT->TL = ((v&0x0f)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - - - /*check whether we are in rhythm mode and handle instrument/volume register accordingly*/ - if ((chan>=6) && (rhythm&0x20)) - { - /* we're in rhythm mode*/ - - if (chan>=7) /* only for channel 7 and 8 (channel 6 is handled in usual way)*/ - { - SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH(chan=7) or TOM(chan=8) */ - SLOT->TL = ((instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } - } - else - { - if ( (old_instvol&0xf0) == (v&0xf0) ) - return; - - inst = &inst_tab[instvol_r[chan]>>4][0]; - slot = chan*2; - - load_instrument(chan, slot, inst); - - #if 0 - logerror("YM2413: chan#%02i inst=%02i: (r=%2x, v=%2x)\n",chan,v>>4,r,v); - logerror(" 0:%2x 1:%2x\n",inst[0],inst[1]); logerror(" 2:%2x 3:%2x\n",inst[2],inst[3]); - logerror(" 4:%2x 5:%2x\n",inst[4],inst[5]); logerror(" 6:%2x 7:%2x\n",inst[6],inst[7]); - #endif - } - } - break; - - default: - break; - } -} //------------------------------------------------- -// sound_stream_update - handle a stream update +// sound_stream_update - update the sound stream //------------------------------------------------- void ym2413_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) { - for(int i=0; i < outputs[0].samples() ; i++ ) + // iterate over all target samples + for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++) { - output[0] = 0; - output[1] = 0; - - advance_lfo(); - - /* FM part */ - for(int j=0; j<6; j++) - chan_calc(&P_CH[j]); - - if(!(rhythm & 0x20)) - { - for(int j=6; j<9; j++) - chan_calc(&P_CH[j]); - } - else /* Rhythm part */ - { - rhythm_calc(&P_CH[0], noise_rng & 1 ); - } + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); - outputs[0].put_int_clamp(i, output[0], 32768); - outputs[1].put_int_clamp(i, output[1], 32768); + // update the FM; YM3812 is 9-bit, unsure of clipping but guessing + // it is similar to YM2612 + s32 sums[fm_engine::OUTPUTS] = { 0 }; + m_fm.output(sums, 5, 256, fm_engine::ALL_CHANNELS); - advance(); + // the YM3812 is time multiplexed; just simulate this by summing all the + // channels and dividing down + for (int outnum = 0; outnum < fm_engine::OUTPUTS; outnum++) + outputs[outnum].put_int(sampindex, sums[outnum], 256*6*2); } } + +//********************************************************* +// YM2423 DEVICE (OPLL-X) +//********************************************************* + //------------------------------------------------- -// device_start - device-specific startup +// ym2423_device - constructor //------------------------------------------------- -void ym2413_device::device_start() +ym2423_device::ym2423_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + ym2413_device(mconfig, tag, owner, clock, YM2423) { - int rate = clock()/72; - - m_stream = stream_alloc(0,2,rate); - - for (int x=0; x>= 4; /* 12 bits here */ - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - /* 11 bits here (rounded) */ - tl_tab[ x*2 + 0 ] = n; - tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; - - for (int i=1; i<11; i++) - { - tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; - tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; - } - } - - for (int i=0; i>1)+1; - else - n = n>>1; - - /* waveform 0: standard sinus */ - sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); - - /* waveform 1: __ __ */ - /* / \____/ \____*/ - /* output only first half of the sinus waveform (positive one) */ - if (i & (1<<(SIN_BITS-1)) ) - sin_tab[1*SIN_LEN+i] = TL_TAB_LEN; - else - sin_tab[1*SIN_LEN+i] = sin_tab[i]; - } - - /* make fnumber -> increment counter table */ - for( int i = 0 ; i < 1024; i++ ) - { - /* OPLL (YM2413) phase increment counter = 18bit */ - - fn_tab[i] = i * (64 <<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ - } - - /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ - /* One entry from LFO_AM_TABLE lasts for 64 samples */ - lfo_am_inc = (1<set_sample_rate(clock() / 72); } + //------------------------------------------------- -// device_reset - device-specific reset +// device_rom_region - return a pointer to our +// ROM region //------------------------------------------------- -void ym2413_device::device_reset() -{ - eg_timer = 0; - eg_cnt = 0; - - noise_rng = 1; /* noise shift register */ - - /* setup instruments table */ - if (m_inst_table != nullptr) - { - for (int i=0; i<19; i++) - { - for (int c=0; c<8; c++) - { - inst_tab[i][c] = m_inst_table[i][c]; - } - } - } - - - /* reset with register write */ - write_reg(0x0f,0); /*test reg*/ - for(int i = 0x3f ; i >= 0x10 ; i-- ) - write_reg(i, 0x00); - - /* reset operator parameters */ - for(int c = 0 ; c < 9 ; c++ ) - { - OPLL_CH *CH = &P_CH[c]; - for(int s = 0 ; s < 2 ; s++ ) - { - /* wave table */ - CH->SLOT[s].wavetable = 0; - CH->SLOT[s].state = EG_OFF; - CH->SLOT[s].volume = MAX_ATT_INDEX; - } - } -} +ROM_START( ymf281 ) + ROM_REGION( 0x90, "internal", 0 ) + // + // This is not the exact format + // + ROM_LOAD16_WORD( "ymf281_instruments.bin", 0x0000, 0x0090, CRC(1c68abba) SHA1(5242d7b9c677c48e156ba5753db1a73db627a1a9) ) +ROM_END - -void ym2413_device::write(offs_t offset, u8 data) +const tiny_rom_entry *ymf281_device::device_rom_region() const { - if (offset) - data_port_w(data); - else - register_port_w(data); + return ROM_NAME( ymf281 ); } -void ym2413_device::register_port_w(u8 data) -{ - address = data; -} -void ym2413_device::data_port_w(u8 data) -{ - m_stream->update(); - write_reg(address, data); -} +//********************************************************* +// DS1001 DEVICE (Konami VRC7) +//********************************************************* -DEFINE_DEVICE_TYPE(YM2413, ym2413_device, "ym2413", "Yamaha YM2413 OPLL") +//------------------------------------------------- +// ds1001_device - constructor +//------------------------------------------------- -ym2413_device::ym2413_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : ym2413_device(mconfig, YM2413, tag, owner, clock) +ds1001_device::ds1001_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + ym2413_device(mconfig, tag, owner, clock, DS1001) { - for (int i = 0; i < 19; i++) - { - for (int c = 0; c < 8; c++) - { - m_inst_table[i][c] = table[i][c]; - } - } } -ym2413_device::ym2413_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock) - , device_sound_interface(mconfig, *this) - , eg_cnt(0) - , eg_timer(0) - , eg_timer_add(0) - , eg_timer_overflow(0) - , rhythm(0) - , LFO_AM(0) - , LFO_PM(0) - , lfo_am_cnt(0) - , lfo_am_inc(0) - , lfo_pm_cnt(0) - , lfo_pm_inc(0) - , noise_rng(0) - , noise_p(0) - , noise_f(0) - , address(0) - -{ - for (int i = 0; i < 19; i++) - { - std::fill_n(&m_inst_table[i][0], 8, 0); - std::fill_n(&inst_tab[i][0], 8, 0); - } - std::fill(std::begin(tl_tab), std::end(tl_tab), 0); - std::fill(std::begin(sin_tab), std::end(sin_tab), 0); - std::fill(std::begin(instvol_r), std::end(instvol_r), 0); - std::fill(std::begin(fn_tab), std::end(fn_tab), 0); - std::fill(std::begin(output), std::end(output), 0); -} +//------------------------------------------------- +// device_rom_region - return a pointer to our +// ROM region +//------------------------------------------------- -DEFINE_DEVICE_TYPE(VRC7, vrc7snd_device, "vrc7snd", "Konami 053982 VRC VII (Sound)") // die label: D51001 +ROM_START( ds1001 ) + ROM_REGION( 0x90, "internal", 0 ) + // + // This is not the exact format + // + ROM_LOAD16_WORD( "ds1001_instruments.bin", 0x0000, 0x0090, CRC(9d699efc) SHA1(7adf1d77bab12c50ebfa9921774f9aea1e74dd7b) ) +ROM_END -vrc7snd_device::vrc7snd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : ym2413_device(mconfig, VRC7, tag, owner, clock) +const tiny_rom_entry *ds1001_device::device_rom_region() const { - for (int i = 0; i < 19; i++) - { - for (int c = 0; c < 8; c++) - { - m_inst_table[i][c] = vrc7_table[i][c]; - } - } + return ROM_NAME( ds1001 ); } diff --git a/src/devices/sound/ym2413.h b/src/devices/sound/ym2413.h index 27174977332..9cea40d1d00 100644 --- a/src/devices/sound/ym2413.h +++ b/src/devices/sound/ym2413.h @@ -1,203 +1,98 @@ -// license:GPL-2.0+ -// copyright-holders:Jarek Burczynski,Ernesto Corvi +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + #ifndef MAME_SOUND_YM2413_H #define MAME_SOUND_YM2413_H #pragma once +#include "ymfm.h" + + +// ======================> ym2413_device + +DECLARE_DEVICE_TYPE(YM2413, ym2413_device); class ym2413_device : public device_t, public device_sound_interface { public: - ym2413_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + // YM2151 is OPLL + using fm_engine = ymopll_engine; - void write(offs_t offset, u8 data); + // constructor + ym2413_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YM2413, u8 const *instruments = nullptr); - void register_port_w(u8 data); - void data_port_w(u8 data); + // no read access present -protected: - ym2413_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + // write access + void address_w(u8 data); // A0=0 + void data_w(u8 data); // A0=1 + void write(offs_t offset, u8 data); +protected: // device-level overrides virtual void device_start() override; - virtual void device_clock_changed() override; virtual void device_reset() override; + virtual void device_clock_changed() override; + virtual const tiny_rom_entry *device_rom_region() const override; - // sound stream update overrides + // sound overrides virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; - uint8_t m_inst_table[19][8]; - -private: - struct OPLL_SLOT - { - uint32_t ar = 0; /* attack rate: AR<<2 */ - uint32_t dr = 0; /* decay rate: DR<<2 */ - uint32_t rr = 0; /* release rate:RR<<2 */ - uint8_t KSR = 0; /* key scale rate */ - uint8_t ksl = 0; /* keyscale level */ - uint8_t ksr = 0; /* key scale rate: kcode>>KSR */ - uint8_t mul = 0; /* multiple: mul_tab[ML] */ - - /* Phase Generator */ - uint32_t phase = 0; /* frequency counter */ - uint32_t freq = 0; /* frequency counter step */ - uint8_t fb_shift = 0; /* feedback shift value */ - int32_t op1_out[2] = { 0, 0 }; /* slot1 output for feedback */ - - /* Envelope Generator */ - uint8_t eg_type = 0; /* percussive/nonpercussive mode*/ - uint8_t state = 0; /* phase type */ - uint32_t TL = 0; /* total level: TL << 2 */ - int32_t TLL = 0; /* adjusted now TL */ - int32_t volume = 0; /* envelope counter */ - uint32_t sl = 0; /* sustain level: sl_tab[SL] */ - - uint8_t eg_sh_dp = 0; /* (dump state) */ - uint8_t eg_sel_dp = 0; /* (dump state) */ - uint8_t eg_sh_ar = 0; /* (attack state) */ - uint8_t eg_sel_ar = 0; /* (attack state) */ - uint8_t eg_sh_dr = 0; /* (decay state) */ - uint8_t eg_sel_dr = 0; /* (decay state) */ - uint8_t eg_sh_rr = 0; /* (release state for non-perc.)*/ - uint8_t eg_sel_rr = 0; /* (release state for non-perc.)*/ - uint8_t eg_sh_rs = 0; /* (release state for perc.mode)*/ - uint8_t eg_sel_rs = 0; /* (release state for perc.mode)*/ - - uint32_t key = 0; /* 0 = KEY OFF, >0 = KEY ON */ - - /* LFO */ - uint32_t AMmask = 0; /* LFO Amplitude Modulation enable mask */ - uint8_t vib = 0; /* LFO Phase Modulation enable flag (active high)*/ - - /* waveform select */ - unsigned int wavetable = 0; - }; - - struct OPLL_CH - { - OPLL_SLOT SLOT[2]; - /* phase generator state */ - uint32_t block_fnum = 0; /* block+fnum */ - uint32_t fc = 0; /* Freq. freqement base */ - uint32_t ksl_base = 0; /* KeyScaleLevel Base step */ - uint8_t kcode = 0; /* key code (for key scaling) */ - uint8_t sus = 0; /* sus on/off (release speed in percussive mode)*/ - }; - - enum { - RATE_STEPS = (8), - - /* sinwave entries */ - SIN_BITS = 10, - SIN_LEN = (1<increment counter */ - - uint8_t address; /* address register */ + // internal state + u8 m_address; // address register + sound_stream *m_stream; // sound stream + required_region_ptr m_internal; // internal memory region + fm_engine m_fm; // core FM engine +}; - signed int output[2]; - // internal state - sound_stream * m_stream; - - int limit( int val, int max, int min ); - void advance_lfo(); - void advance(); - int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab); - int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab); - void chan_calc( OPLL_CH *CH ); - void rhythm_calc( OPLL_CH *CH, unsigned int noise ); - void key_on(OPLL_SLOT *SLOT, uint32_t key_set); - void key_off(OPLL_SLOT *SLOT, uint32_t key_clr); - void calc_fcslot(OPLL_CH *CH, OPLL_SLOT *SLOT); - void set_mul(int slot,int v); - void set_ksl_tl(int chan,int v); - void set_ksl_wave_fb(int chan,int v); - void set_ar_dr(int slot,int v); - void set_sl_rr(int slot,int v); - void load_instrument(uint32_t chan, uint32_t slot, uint8_t* inst ); - void update_instrument_zero( uint8_t r ); - void write_reg(int r, int v); +// ======================> ym2423_device + +DECLARE_DEVICE_TYPE(YM2423, ym2423_device); + +class ym2423_device : public ym2413_device +{ +public: + // constructor + ym2423_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +protected: + // device-level overrides + virtual const tiny_rom_entry *device_rom_region() const override; +}; + + +// ======================> ymf281_device + +DECLARE_DEVICE_TYPE(YMF281, ymf281_device); + +class ymf281_device : public ym2413_device +{ +public: + // constructor + ymf281_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + +protected: + // device-level overrides + virtual const tiny_rom_entry *device_rom_region() const override; }; -DECLARE_DEVICE_TYPE(YM2413, ym2413_device) -class vrc7snd_device : public ym2413_device +// ======================> ds1001_device + +DECLARE_DEVICE_TYPE(DS1001, ds1001_device); + +class ds1001_device : public ym2413_device { public: - vrc7snd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + // constructor + ds1001_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); -private: - static const uint8_t vrc7_table[19][8]; +protected: + // device-level overrides + virtual const tiny_rom_entry *device_rom_region() const override; }; -DECLARE_DEVICE_TYPE(VRC7, vrc7snd_device) #endif // MAME_SOUND_YM2413_H diff --git a/src/devices/sound/ym2608.cpp b/src/devices/sound/ym2608.cpp index b55fe207a06..35cdff227c8 100644 --- a/src/devices/sound/ym2608.cpp +++ b/src/devices/sound/ym2608.cpp @@ -33,11 +33,11 @@ ym2608_device::ym2608_device(const machine_config &mconfig, const char *tag, dev ay8910_device(mconfig, YM2608, tag, owner, clock, PSG_TYPE_YM, 1, 2), device_rom_interface(mconfig, *this), m_internal(*this, "internal"), - m_opn(*this), + m_fm(*this), m_adpcm_a(*this, read8sm_delegate(*this, FUNC(ym2608_device::adpcm_a_read)), 0), m_adpcm_b(*this, read8sm_delegate(*this, FUNC(ym2608_device::adpcm_b_read)), write8sm_delegate(*this, FUNC(ym2608_device::adpcm_b_write))), m_stream(nullptr), - m_busy_duration(m_opn.compute_busy_duration()), + m_busy_duration(m_fm.compute_busy_duration()), m_address(0), m_irq_enable(0x1f), m_flag_control(0x1c) @@ -54,8 +54,8 @@ u8 ym2608_device::read(offs_t offset) u8 result = 0; switch (offset & 3) { - case 0: // status port, YM2203 compatible - result = m_opn.status() & (ymopna_engine::STATUS_TIMERA | ymopna_engine::STATUS_TIMERB | ymopna_engine::STATUS_BUSY); + case 0: // status port, YM2203 compatible + result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB | fm_engine::STATUS_BUSY); break; case 1: // data port (only SSG) @@ -65,7 +65,8 @@ u8 ym2608_device::read(offs_t offset) result = 1; // ID code break; - case 2: // status port, extended + case 2: // status port, extended + m_stream->update(); result = combine_status(); break; @@ -99,7 +100,7 @@ void ym2608_device::write(offs_t offset, u8 value) // prescaler select : 2d,2e,2f if (m_address == 0x2d) update_prescale(6); - else if (m_address == 0x2e && m_opn.clock_prescale() == 6) + else if (m_address == 0x2e && m_fm.clock_prescale() == 6) update_prescale(3); else if (m_address == 0x2f) update_prescale(2); @@ -128,17 +129,17 @@ void ym2608_device::write(offs_t offset, u8 value) // special IRQ mask register m_stream->update(); m_irq_enable = value; - m_opn.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f); + m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f); } else { - // write to OPN + // write to FM m_stream->update(); - m_opn.write(m_address, value); + m_fm.write(m_address, value); } // mark busy for a bit - m_opn.set_busy_end(machine().time() + m_busy_duration); + m_fm.set_busy_end(machine().time() + m_busy_duration); break; case 2: // upper address port @@ -162,22 +163,22 @@ void ym2608_device::write(offs_t offset, u8 value) // IRQ flag control m_stream->update(); if (BIT(value, 7)) - m_opn.set_reset_status(0, 0xff); + m_fm.set_reset_status(0, 0xff); else { m_flag_control = value; - m_opn.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f); + m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f); } } else { - // write to OPN + // write to FM m_stream->update(); - m_opn.write(m_address, value); + m_fm.write(m_address, value); } // mark busy for a bit - m_opn.set_busy_end(machine().time() + m_busy_duration); + m_fm.set_busy_end(machine().time() + m_busy_duration); break; } } @@ -193,7 +194,7 @@ void ym2608_device::device_start() ay8910_device::device_start(); // create our stream - m_stream = stream_alloc(0, 2, clock() / (4 * 6 * 6)); + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); // save our data save_item(YMFM_NAME(m_address)); @@ -201,17 +202,9 @@ void ym2608_device::device_start() save_item(YMFM_NAME(m_flag_control)); // save the engines - m_opn.save(*this); + m_fm.save(*this); m_adpcm_a.save(*this); m_adpcm_b.save(*this); - - // configure ADPCM percussion sounds - 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 } @@ -225,10 +218,18 @@ void ym2608_device::device_reset() ay8910_device::device_reset(); // reset the engines - m_opn.reset(); + m_fm.reset(); m_adpcm_a.reset(); m_adpcm_b.reset(); + // configure ADPCM percussion sounds + 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 m_irq_enable = 0x1f; m_flag_control = 0x1c; @@ -243,7 +244,7 @@ void ym2608_device::device_reset() void ym2608_device::device_clock_changed() { // refresh via prescale - update_prescale(m_opn.clock_prescale()); + update_prescale(m_fm.clock_prescale()); } @@ -301,13 +302,13 @@ void ym2608_device::sound_stream_update(sound_stream &stream, std::vectorset_sample_rate(clock() / (4 * 6 * newval)); - logerror("Prescale = %d; sample_rate = %d\n", newval, clock() / (4 * 6 * newval)); + // inform the FM engine and refresh our clock rate + m_fm.set_clock_prescale(newval); + m_stream->set_sample_rate(m_fm.sample_rate(clock())); + logerror("Prescale = %d; sample_rate = %d\n", newval, m_fm.sample_rate(clock())); // also scale the SSG streams - // mapping is (OPN->SSG): 6->4, 3->2, 2->1 + // mapping is (FM->SSG): 6->4, 3->2, 2->1 u8 ssg_scale = 2 * newval / 3; // QUESTION: where does the *2 come from?? ay_set_clock(clock() / ssg_scale); // recompute the busy duration - m_busy_duration = m_opn.compute_busy_duration(); + m_busy_duration = m_fm.compute_busy_duration(); } //------------------------------------------------- // combine_status - combine status flags from -// OPN and ADPCM-B, masking out any indicated by +// FM and ADPCM-B, masking out any indicated by // the flag control register //------------------------------------------------- u8 ym2608_device::combine_status() { - u8 status = m_opn.status(); - u8 adpcm_status = m_adpcm_b.status(0); + u8 status = m_fm.status() & ~(STATUS_ADPCM_B_EOS | STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_PLAYING); + u8 adpcm_status = m_adpcm_b.status(); if ((adpcm_status & ymadpcm_b_channel::STATUS_EOS) != 0) status |= STATUS_ADPCM_B_EOS; if ((adpcm_status & ymadpcm_b_channel::STATUS_BRDY) != 0) @@ -372,7 +373,7 @@ u8 ym2608_device::combine_status() if ((adpcm_status & ymadpcm_b_channel::STATUS_PLAYING) != 0) status |= STATUS_ADPCM_B_PLAYING; status &= ~(m_flag_control & 0x1f); - m_opn.set_reset_status(status, ~status); + m_fm.set_reset_status(status, ~status); return status; } diff --git a/src/devices/sound/ym2608.h b/src/devices/sound/ym2608.h index 022e0ef54eb..465b3ba1659 100644 --- a/src/devices/sound/ym2608.h +++ b/src/devices/sound/ym2608.h @@ -18,11 +18,14 @@ DECLARE_DEVICE_TYPE(YM2608, ym2608_device); class ym2608_device : public ay8910_device, public device_rom_interface<21> { public: + // YM2608 is OPNA + using fm_engine = ymopna_engine; + // constructor ym2608_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); // configuration helpers - auto irq_handler() { return m_opn.irq_handler(); } + auto irq_handler() { return m_fm.irq_handler(); } // read/write access u8 read(offs_t offset); @@ -33,9 +36,9 @@ protected: virtual void device_start() override; virtual void device_reset() override; virtual void device_clock_changed() override; + virtual const tiny_rom_entry *device_rom_region() const override; // ROM device overrides - virtual const tiny_rom_entry *device_rom_region() const override; virtual void rom_bank_updated() override; // sound overrides @@ -45,7 +48,7 @@ private: // set a new prescale value and update clocks void update_prescale(u8 newval); - // combine ADPCM and OPN statuses + // combine ADPCM and FM statuses u8 combine_status(); // ADPCM read/write callbacks @@ -55,7 +58,7 @@ private: // internal state required_memory_region m_internal; // internal memory region - ymopna_engine m_opn; // core OPNA engine + fm_engine m_fm; // core FM engine ymadpcm_a_engine m_adpcm_a; // ADPCM-A engine ymadpcm_b_engine m_adpcm_b; // ADPCM-B engine sound_stream *m_stream; // sound stream diff --git a/src/devices/sound/ym2610.cpp b/src/devices/sound/ym2610.cpp index 895507d4554..9c5b3d6a499 100644 --- a/src/devices/sound/ym2610.cpp +++ b/src/devices/sound/ym2610.cpp @@ -6,7 +6,7 @@ DEFINE_DEVICE_TYPE(YM2610, ym2610_device, "ym2610", "YM2610 OPNB") -DEFINE_DEVICE_TYPE(YM2610B, ym2610b_device, "ym2610b", "YM2610 OPNB2") +DEFINE_DEVICE_TYPE(YM2610B, ym2610b_device, "ym2610b", "YM2610B OPNB2") //********************************************************* @@ -17,20 +17,20 @@ DEFINE_DEVICE_TYPE(YM2610B, ym2610b_device, "ym2610b", "YM2610 OPNB2") // ym2610_device - constructor //------------------------------------------------- -ym2610_device::ym2610_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type, u8 opn_mask) : +ym2610_device::ym2610_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type, u8 fm_mask) : ay8910_device(mconfig, type, tag, owner, clock, PSG_TYPE_YM, 1, 0), device_memory_interface(mconfig, *this), m_adpcm_a_config("adpcm-a", ENDIANNESS_LITTLE, 8, 24, 0), m_adpcm_b_config("adpcm-b", ENDIANNESS_LITTLE, 8, 24, 0), m_adpcm_a_region(*this, "adpcma"), m_adpcm_b_region(*this, "adpcmb"), - m_opn(*this), + m_fm(*this), m_adpcm_a(*this, read8sm_delegate(*this, FUNC(ym2610_device::adpcm_a_read)), 8), m_adpcm_b(*this, read8sm_delegate(*this, FUNC(ym2610_device::adpcm_b_read)), write8sm_delegate(*this), 8), m_stream(nullptr), - m_busy_duration(m_opn.compute_busy_duration()), + m_busy_duration(m_fm.compute_busy_duration()), m_address(0), - m_opn_mask(opn_mask), + m_fm_mask(fm_mask), m_eos_status(0x00), m_flag_mask(0xbf) { @@ -56,8 +56,8 @@ u8 ym2610_device::read(offs_t offset) u8 result = 0; switch (offset & 3) { - case 0: // status port, YM2203 compatible - result = m_opn.status() & (ymopna_engine::STATUS_TIMERA | ymopna_engine::STATUS_TIMERB | ymopna_engine::STATUS_BUSY); + case 0: // status port, YM2203 compatible + result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB | fm_engine::STATUS_BUSY); break; case 1: // data port (only SSG) @@ -67,7 +67,8 @@ u8 ym2610_device::read(offs_t offset) result = 1; // ID code break; - case 2: // status port, extended + case 2: // status port, extended + m_stream->update(); result = m_eos_status & m_flag_mask; break; @@ -126,13 +127,13 @@ void ym2610_device::write(offs_t offset, u8 value) } else { - // write to OPN + // write to FM m_stream->update(); - m_opn.write(m_address, value); + m_fm.write(m_address, value); } // mark busy for a bit - m_opn.set_busy_end(machine().time() + m_busy_duration); + m_fm.set_busy_end(machine().time() + m_busy_duration); break; case 2: // upper address port @@ -153,13 +154,13 @@ void ym2610_device::write(offs_t offset, u8 value) } else { - // write to OPN + // write to FM m_stream->update(); - m_opn.write(m_address, value); + m_fm.write(m_address, value); } // mark busy for a bit - m_opn.set_busy_end(machine().time() + m_busy_duration); + m_fm.set_busy_end(machine().time() + m_busy_duration); break; } } @@ -189,7 +190,7 @@ void ym2610_device::device_start() ay8910_device::device_start(); // create our stream - m_stream = stream_alloc(0, 2, clock() / (4 * 6 * 6)); + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); // save our data save_item(YMFM_NAME(m_address)); @@ -197,7 +198,7 @@ void ym2610_device::device_start() save_item(YMFM_NAME(m_flag_mask)); // save the engines - m_opn.save(*this); + m_fm.save(*this); m_adpcm_a.save(*this); m_adpcm_b.save(*this); @@ -225,7 +226,7 @@ void ym2610_device::device_reset() ay8910_device::device_reset(); // reset the engines - m_opn.reset(); + m_fm.reset(); m_adpcm_a.reset(); m_adpcm_b.reset(); @@ -241,11 +242,11 @@ void ym2610_device::device_reset() void ym2610_device::device_clock_changed() { - m_stream->set_sample_rate(clock() / (4 * 6 * 6)); + m_stream->set_sample_rate(m_fm.sample_rate(clock())); ay_set_clock(clock() / 4); // recompute the busy duration - m_busy_duration = m_opn.compute_busy_duration(); + m_busy_duration = m_fm.compute_busy_duration(); } @@ -265,8 +266,8 @@ void ym2610_device::sound_stream_update(sound_stream &stream, std::vectorupdate(); - m_opn.write(m_address, value); + m_fm.write(m_address, value); // mark busy for a bit - m_opn.set_busy_end(machine().time() + m_busy_duration); + m_fm.set_busy_end(machine().time() + m_busy_duration); break; } } @@ -136,7 +136,7 @@ void ym2612_device::write(offs_t offset, u8 value) void ym2612_device::device_start() { // create our stream - m_stream = stream_alloc(0, 2, clock() / (4 * 6 * 6)); + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); // call this for the variants that need to adjust the rate device_clock_changed(); @@ -148,7 +148,7 @@ void ym2612_device::device_start() save_item(YMFM_NAME(m_channel)); // save the engines - m_opn.save(*this); + m_fm.save(*this); } @@ -159,7 +159,7 @@ void ym2612_device::device_start() void ym2612_device::device_reset() { // reset the engines - m_opn.reset(); + m_fm.reset(); // reset our internal state m_dac_enable = 0; @@ -173,10 +173,11 @@ void ym2612_device::device_reset() void ym2612_device::device_clock_changed() { - m_stream->set_sample_rate(clock() / (4 * 6 * (MULTIPLEX_YM2612_YM3438_OUTPUT ? 1 : 6))); + u32 const sample_divider = MULTIPLEX_OUTPUT ? fm_engine::CHANNELS : 1; + m_stream->set_sample_rate(m_fm.sample_rate(clock()) * sample_divider); // recompute the busy duration - m_busy_duration = m_opn.compute_busy_duration(); + m_busy_duration = m_fm.compute_busy_duration(); } @@ -197,22 +198,22 @@ void ym2612_device::sound_stream_update(sound_stream &stream, std::vector> 7; + outputs[0] = outputs[1] = s16(m_dac_data << 7) >> 7; // hiccup in the internal YM2612 DAC means that there is a rather large // step between 0 and -1 (close to 6x the normal step); the approximation @@ -220,38 +221,37 @@ void ym2612_device::sound_stream_update_common(write_stream_view &outl, write_st // fixed in the YM3438 if (discontinuity) { - if (lchan < 0) - lchan -= 2; + if (outputs[0] < 0) + outputs[0] -= 2; else - lchan += 3; - if (rchan < 0) - rchan -= 2; + outputs[0] += 3; + if (outputs[1] < 0) + outputs[1] -= 2; else - rchan += 3; + outputs[1] += 3; } // if multiplexing, just scale to 16 bits and output - if (MULTIPLEX_YM2612_YM3438_OUTPUT) + if (MULTIPLEX_OUTPUT) { - outl.put_int(sampindex, lchan, sample_divider); - outr.put_int(sampindex, rchan, sample_divider); + outl.put_int(sampindex, outputs[0], sample_divider); + outr.put_int(sampindex, outputs[1], sample_divider); sampindex++; - lsum = rsum = 0; } // if not, accumulate the sums else { - lsum += lchan; - rsum += rchan; + sums[0] += outputs[0]; + sums[1] += outputs[1]; // on the last channel, output the average and reset the sums if (m_channel == 5) { - outl.put_int(sampindex, lsum, sample_divider); - outr.put_int(sampindex, rsum, sample_divider); + outl.put_int(sampindex, sums[0], sample_divider); + outr.put_int(sampindex, sums[1], sample_divider); sampindex++; - lsum = rsum = 0; + sums[0] = sums[1] = 0; } } @@ -309,7 +309,7 @@ ymf276_device::ymf276_device(const machine_config &mconfig, const char *tag, dev void ymf276_device::device_clock_changed() { - m_stream->set_sample_rate(clock() / (4 * 6 * 6)); + m_stream->set_sample_rate(m_fm.sample_rate(clock())); } @@ -320,32 +320,30 @@ void ymf276_device::device_clock_changed() void ymf276_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) { // mask off channel 6 if DAC is enabled - u8 const opn_mask = m_dac_enable ? 0x1f : 0x3f; + u8 const fm_mask = m_dac_enable ? 0x1f : 0x3f; // iterate over all target samples for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++) { - // clock the OPN - m_opn.clock(0x3f); + // clock the FM + m_fm.clock(fm_engine::ALL_CHANNELS); - // update the OPN content; OPN2L is 14-bit with intermediate clipping - s32 lsum = 0, rsum = 0; - m_opn.output(lsum, rsum, 0, 8191, opn_mask); + // update the FM content; YMF276 is 14-bit with intermediate clipping + s32 sums[fm_engine::OUTPUTS] = { 0 }; + m_fm.output(sums, 0, 8191, fm_mask); // shifted down 1 bit after mixer - lsum >>= 1; - rsum >>= 1; + for (int index = 0; index < fm_engine::OUTPUTS; index++) + sums[index] >>= 1; // add in DAC if enabled if (m_dac_enable) - { - lsum += s16(m_dac_data << 7) >> 3; - rsum += s16(m_dac_data << 7) >> 3; - } + for (int index = 0; index < fm_engine::OUTPUTS; index++) + sums[index] += s16(m_dac_data << 7) >> 3; // YMF3438 is stereo - outputs[0].put_int_clamp(sampindex, lsum, 32768); - outputs[1].put_int_clamp(sampindex, rsum, 32768); + for (int index = 0; index < fm_engine::OUTPUTS; index++) + outputs[index].put_int_clamp(sampindex, sums[0], 32768); } } diff --git a/src/devices/sound/ym2612.h b/src/devices/sound/ym2612.h index 5b121b36164..658ddd275b7 100644 --- a/src/devices/sound/ym2612.h +++ b/src/devices/sound/ym2612.h @@ -16,11 +16,14 @@ DECLARE_DEVICE_TYPE(YM2612, ym2612_device); class ym2612_device : public device_t, public device_sound_interface { public: + // YM2612 is OPNA + using fm_engine = ymopna_engine; + // constructor ym2612_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YM2612); // configuration helpers - auto irq_handler() { return m_opn.irq_handler(); } + auto irq_handler() { return m_fm.irq_handler(); } // read/write access u8 read(offs_t offset); @@ -39,7 +42,7 @@ protected: void sound_stream_update_common(write_stream_view &outl, write_stream_view &outr, bool discontinuity); // internal state - ymopna_engine m_opn; // core OPN engine + fm_engine m_fm; // core FM engine sound_stream *m_stream; // sound stream attotime m_busy_duration; // precomputed busy signal duration u16 m_address; // address register diff --git a/src/devices/sound/ym3526.cpp b/src/devices/sound/ym3526.cpp new file mode 100644 index 00000000000..76794058e1e --- /dev/null +++ b/src/devices/sound/ym3526.cpp @@ -0,0 +1,153 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +#include "emu.h" +#include "ym3526.h" + + +DEFINE_DEVICE_TYPE(YM3526, ym3526_device, "ym3526", "YM3526 OPL") + + +//********************************************************* +// YM3526 DEVICE +//********************************************************* + +//------------------------------------------------- +// ym3526_device - constructor +//------------------------------------------------- + +ym3526_device::ym3526_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type) : + device_t(mconfig, type, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_address(0), + m_stream(nullptr), + m_fm(*this) +{ +} + + +//------------------------------------------------- +// status_r - return the status port (A0=0) +//------------------------------------------------- + +u8 ym3526_device::status_r() +{ + return m_fm.status() | 0x06; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +u8 ym3526_device::read(offs_t offset) +{ + // datasheet says status only reads when A0=0 + if ((offset & 1) == 0) + return status_r(); + + // when A0=1 datasheet says "the data on the bus are not guaranteed" + logerror("Unexpected read from YM3526 offset %d\n", offset & 1); + return 0xff; +} + + +//------------------------------------------------- +// address_w - write to the address port (A0=0) +//------------------------------------------------- + +void ym3526_device::address_w(u8 value) +{ + m_address = value; +} + + +//------------------------------------------------- +// data_w - write to the data port (A0=1) +//------------------------------------------------- + +void ym3526_device::data_w(u8 value) +{ + // force an update + m_stream->update(); + + // write to FM + m_fm.write(m_address, value); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3526_device::write(offs_t offset, u8 value) +{ + // A0 selects between address/data + if ((offset & 1) == 0) + address_w(value); + else + data_w(value); +} + + +//------------------------------------------------- +// device_start - start of emulation +//------------------------------------------------- + +void ym3526_device::device_start() +{ + // create our stream + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); + + // save our data + save_item(YMFM_NAME(m_address)); + + // save the engines + m_fm.save(*this); +} + + +//------------------------------------------------- +// device_reset - start of emulation +//------------------------------------------------- + +void ym3526_device::device_reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// device_clock_changed - update if clock changes +//------------------------------------------------- + +void ym3526_device::device_clock_changed() +{ + m_stream->set_sample_rate(m_fm.sample_rate(clock())); +} + + +//------------------------------------------------- +// sound_stream_update - update the sound stream +//------------------------------------------------- + +void ym3526_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + // iterate over all target samples + for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; clipping is unknown + s32 sums[fm_engine::OUTPUTS] = { 0 }; + m_fm.output(sums, 1, 32767, fm_engine::ALL_CHANNELS); + + // convert to 10.3 floating point value for the DAC and back + // YM3526 is mono + for (int index = 0; index < fm_engine::OUTPUTS; index++) + outputs[index].put_int(sampindex, ymfm_roundtrip_fp(sums[index]), 32768); + } +} diff --git a/src/devices/sound/ym3526.h b/src/devices/sound/ym3526.h new file mode 100644 index 00000000000..496936befc9 --- /dev/null +++ b/src/devices/sound/ym3526.h @@ -0,0 +1,53 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +#ifndef MAME_SOUND_YM3526_H +#define MAME_SOUND_YM3526_H + +#pragma once + +#include "ymfm.h" + + +// ======================> ym3526_device + +DECLARE_DEVICE_TYPE(YM3526, ym3526_device); + +class ym3526_device : public device_t, public device_sound_interface +{ +public: + // YM3526 is OPL + using fm_engine = ymopl_engine; + + // constructor + ym3526_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YM3526); + + // configuration helpers + auto irq_handler() { return m_fm.irq_handler(); } + + // read access + u8 status_r(); // A0=0 + u8 read(offs_t offset); + + // write access + void address_w(u8 data); // A0=0 + void data_w(u8 data); // A0=1 + void write(offs_t offset, u8 data); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_clock_changed() override; + + // sound overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + + // internal state + u8 m_address; // address register + sound_stream *m_stream; // sound stream + fm_engine m_fm; // core FM engine +}; + + +#endif // MAME_SOUND_YM3526_H diff --git a/src/devices/sound/ym3812.cpp b/src/devices/sound/ym3812.cpp new file mode 100644 index 00000000000..07da3b73b3b --- /dev/null +++ b/src/devices/sound/ym3812.cpp @@ -0,0 +1,151 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +#include "emu.h" +#include "ym3812.h" + + +DEFINE_DEVICE_TYPE(YM3812, ym3812_device, "ym3812", "YM3812 OPL2") + + +//********************************************************* +// YM3812 DEVICE +//********************************************************* + +//------------------------------------------------- +// ym3812_device - constructor +//------------------------------------------------- + +ym3812_device::ym3812_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type) : + device_t(mconfig, type, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_address(0), + m_stream(nullptr), + m_fm(*this) +{ +} + + +//------------------------------------------------- +// status_r - return the status port (A0=0) +//------------------------------------------------- + +u8 ym3812_device::status_r() +{ + return m_fm.status() | 0x06; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +u8 ym3812_device::read(offs_t offset) +{ + // datasheet says status only reads when A0=0 + if ((offset & 1) == 0) + return status_r(); + logerror("Unexpected read from YM3812 offset %d\n", offset & 1); + return 0xff; +} + + +//------------------------------------------------- +// address_w - write to the address port (A0=0) +//------------------------------------------------- + +void ym3812_device::address_w(u8 value) +{ + m_address = value; +} + + +//------------------------------------------------- +// data_w - write to the data port (A0=1) +//------------------------------------------------- + +void ym3812_device::data_w(u8 value) +{ + // force an update + m_stream->update(); + + // write to FM + m_fm.write(m_address, value); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3812_device::write(offs_t offset, u8 value) +{ + // A0 selects between address/data + if ((offset & 1) == 0) + address_w(value); + else + data_w(value); +} + + +//------------------------------------------------- +// device_start - start of emulation +//------------------------------------------------- + +void ym3812_device::device_start() +{ + // create our stream + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); + + // save our data + save_item(YMFM_NAME(m_address)); + + // save the engines + m_fm.save(*this); +} + + +//------------------------------------------------- +// device_reset - start of emulation +//------------------------------------------------- + +void ym3812_device::device_reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// device_clock_changed - update if clock changes +//------------------------------------------------- + +void ym3812_device::device_clock_changed() +{ + m_stream->set_sample_rate(m_fm.sample_rate(clock())); +} + + +//------------------------------------------------- +// sound_stream_update - update the sound stream +//------------------------------------------------- + +void ym3812_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + // iterate over all target samples + for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; clipping is unknown + s32 sums[fm_engine::OUTPUTS] = { 0 }; + m_fm.output(sums, 1, 32767, fm_engine::ALL_CHANNELS); + + // convert to 10.3 floating point value for the DAC and back + // YM3812 is mono + for (int index = 0; index < fm_engine::OUTPUTS; index++) + outputs[index].put_int(sampindex, ymfm_roundtrip_fp(sums[index]), 32768); + } +} diff --git a/src/devices/sound/ym3812.h b/src/devices/sound/ym3812.h new file mode 100644 index 00000000000..4e6c89afde3 --- /dev/null +++ b/src/devices/sound/ym3812.h @@ -0,0 +1,53 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +#ifndef MAME_SOUND_YM3812_H +#define MAME_SOUND_YM3812_H + +#pragma once + +#include "ymfm.h" + + +// ======================> ym3812_device + +DECLARE_DEVICE_TYPE(YM3812, ym3812_device); + +class ym3812_device : public device_t, public device_sound_interface +{ +public: + // YM3812 is OPL2 + using fm_engine = ymopl2_engine; + + // constructor + ym3812_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YM3812); + + // configuration helpers + auto irq_handler() { return m_fm.irq_handler(); } + + // read access + u8 status_r(); // A0=0 + u8 read(offs_t offset); + + // write access + void address_w(u8 data); // A0=0 + void data_w(u8 data); // A0=1 + void write(offs_t offset, u8 data); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_clock_changed() override; + + // sound overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + + // internal state + u8 m_address; // address register + sound_stream *m_stream; // sound stream + fm_engine m_fm; // core FM engine +}; + + +#endif // MAME_SOUND_YM3812_H diff --git a/src/devices/sound/ymadpcm.cpp b/src/devices/sound/ymadpcm.cpp index 451c51f108a..7e7ddcc7f19 100644 --- a/src/devices/sound/ymadpcm.cpp +++ b/src/devices/sound/ymadpcm.cpp @@ -4,7 +4,7 @@ #include "emu.h" #include "ymadpcm.h" -//#define VERBOSE 1 +#define VERBOSE 1 #define LOG_OUTPUT_FUNC osd_printf_verbose #include "logmacro.h" @@ -20,6 +20,30 @@ +//********************************************************* +// ADPCM "A" REGISTERS +//********************************************************* + +//------------------------------------------------- +// ymadpcm_a_registers - constructor +//------------------------------------------------- + +void ymadpcm_a_registers::save(device_t &device) +{ + device.save_item(ADPCM_A_NAME(m_regdata)); +} + + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void ymadpcm_a_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + //********************************************************* // ADPCM "A" CHANNEL //********************************************************* @@ -28,16 +52,17 @@ // ymadpcm_a_channel - constructor //------------------------------------------------- -ymadpcm_a_channel::ymadpcm_a_channel(ymadpcm_a_registers regs, read8sm_delegate reader, u8 addrshift) : +ymadpcm_a_channel::ymadpcm_a_channel(ymadpcm_a_engine &owner, u32 choffs, read8sm_delegate reader, u32 addrshift) : + m_choffs(choffs), m_address_shift(addrshift), - m_reader(std::move(reader)), m_playing(0), m_curnibble(0), m_curbyte(0), m_curaddress(0), m_accumulator(0), m_step_index(0), - m_regs(regs) + m_reader(std::move(reader)), + m_regs(owner.regs()) { } @@ -46,7 +71,7 @@ ymadpcm_a_channel::ymadpcm_a_channel(ymadpcm_a_registers regs, read8sm_delegate // save - register for save states //------------------------------------------------- -void ymadpcm_a_channel::save(device_t &device, u8 index) +void ymadpcm_a_channel::save(device_t &device, u32 index) { device.save_item(ADPCM_A_NAME(m_playing), index); device.save_item(ADPCM_A_NAME(m_curnibble), index); @@ -82,12 +107,18 @@ void ymadpcm_a_channel::keyonoff(bool on) m_playing = on; if (m_playing) { - m_curaddress = m_regs.start() << m_address_shift; + m_curaddress = m_regs.ch_start(m_choffs) << m_address_shift; m_curnibble = 0; m_curbyte = 0; m_accumulator = 0; m_step_index = 0; - LOG("KeyOn ADPCM-A%d: pan=%d%d start=%04X end=%04X level=%02X\n", m_regs.chbase(), m_regs.pan_left(), m_regs.pan_right(), m_regs.start(), m_regs.end(), m_regs.instrument_level()); + LOG("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)); } } @@ -106,7 +137,7 @@ bool ymadpcm_a_channel::clock() } // stop when we hit the end address - if ((m_curaddress >> m_address_shift) >= m_regs.end()) + if ((m_curaddress >> m_address_shift) >= m_regs.ch_end(m_choffs)) { m_playing = m_accumulator = 0; return true; @@ -148,13 +179,7 @@ bool ymadpcm_a_channel::clock() // adjust ADPCM step static s8 const s_step_inc[8] = { -1, -1, -1, -1, 2, 5, 7, 9 }; - m_step_index += s_step_inc[BIT(data, 0, 3)]; - - // clamp to the full range - if (m_step_index > 48) - m_step_index = 48; - else if (m_step_index < 0) - m_step_index = 0; + m_step_index = std::clamp(m_step_index + s_step_inc[BIT(data, 0, 3)], 0, 48); return false; } @@ -165,10 +190,10 @@ bool ymadpcm_a_channel::clock() // panning applied //------------------------------------------------- -void ymadpcm_a_channel::output(s32 &leftout, s32 &rightout) const +void ymadpcm_a_channel::output(s32 outputs[2]) const { // volume combined instrument and total levels - int vol = (m_regs.instrument_level() ^ 0x1f) + (m_regs.total_level() ^ 0x3f); + 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) @@ -184,10 +209,10 @@ void ymadpcm_a_channel::output(s32 &leftout, s32 &rightout) const s16 value = ((s16(m_accumulator << 4) * mul) >> shift) & ~3; // apply to left/right as appropriate - if (m_regs.pan_left()) - leftout += value; - if (m_regs.pan_right()) - rightout += value; + if (m_regs.ch_pan_left(m_choffs)) + outputs[0] += value; + if (m_regs.ch_pan_right(m_choffs)) + outputs[1] += value; } @@ -200,13 +225,11 @@ void ymadpcm_a_channel::output(s32 &leftout, s32 &rightout) const // ymadpcm_a_engine - constructor //------------------------------------------------- -ymadpcm_a_engine::ymadpcm_a_engine(device_t &device, read8sm_delegate reader, u8 addrshift) : - m_regdata(0x30), - m_regs(m_regdata) +ymadpcm_a_engine::ymadpcm_a_engine(device_t &device, read8sm_delegate reader, u32 addrshift) { // create the channels - for (int chnum = 0; chnum < 6; chnum++) - m_channel[chnum] = std::make_unique(m_regs.channel_registers(chnum), reader, addrshift); + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique(*this, chnum, reader, addrshift); } @@ -216,8 +239,8 @@ ymadpcm_a_engine::ymadpcm_a_engine(device_t &device, read8sm_delegate reader, u8 void ymadpcm_a_engine::save(device_t &device) { - // save our state - device.save_item(ADPCM_A_NAME(m_regdata)); + // save register state + m_regs.save(device); // save channel state for (int chnum = 0; chnum < std::size(m_channel); chnum++) @@ -231,6 +254,9 @@ void ymadpcm_a_engine::save(device_t &device) void ymadpcm_a_engine::reset() { + // reset register state + m_regs.reset(); + // reset each channel for (auto &chan : m_channel) chan->reset(); @@ -241,10 +267,10 @@ void ymadpcm_a_engine::reset() // clock - master clocking function //------------------------------------------------- -u8 ymadpcm_a_engine::clock(u8 chanmask) +u32 ymadpcm_a_engine::clock(u32 chanmask) { // clock each channel, setting a bit in result if it finished - u8 result = 0; + u32 result = 0; for (int chnum = 0; chnum < std::size(m_channel); chnum++) if (BIT(chanmask, chnum)) if (m_channel[chnum]->clock()) @@ -259,12 +285,12 @@ u8 ymadpcm_a_engine::clock(u8 chanmask) // update - master update function //------------------------------------------------- -void ymadpcm_a_engine::output(s32 &lsum, s32 &rsum, u8 chanmask) +void ymadpcm_a_engine::output(s32 outputs[2], u32 chanmask) { // compute the output of each channel for (int chnum = 0; chnum < std::size(m_channel); chnum++) if (BIT(chanmask, chnum)) - m_channel[chnum]->output(lsum, rsum); + m_channel[chnum]->output(outputs); } @@ -272,7 +298,7 @@ void ymadpcm_a_engine::output(s32 &lsum, s32 &rsum, u8 chanmask) // write - handle writes to the ADPCM-A registers //------------------------------------------------- -void ymadpcm_a_engine::write(u8 regnum, u8 data) +void ymadpcm_a_engine::write(u32 regnum, u8 data) { // store the raw value to the register array; // most writes are passive, consumed only when needed @@ -287,6 +313,33 @@ void ymadpcm_a_engine::write(u8 regnum, u8 data) +//********************************************************* +// ADPCM "B" REGISTERS +//********************************************************* + +//------------------------------------------------- +// ymadpcm_b_registers - constructor +//------------------------------------------------- + +void ymadpcm_b_registers::save(device_t &device) +{ + device.save_item(ADPCM_B_NAME(m_regdata)); +} + + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void ymadpcm_b_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + + // default limit to wide open + m_regdata[0x0c] = m_regdata[0x0d] = 0xff; +} + + //********************************************************* // ADPCM "B" CHANNEL //********************************************************* @@ -295,9 +348,7 @@ void ymadpcm_a_engine::write(u8 regnum, u8 data) // ymadpcm_b_channel - constructor //------------------------------------------------- -ymadpcm_b_channel::ymadpcm_b_channel(ymadpcm_b_registers regs, read8sm_delegate reader, write8sm_delegate writer, u8 addrshift) : - m_reader(reader), - m_writer(writer), +ymadpcm_b_channel::ymadpcm_b_channel(ymadpcm_b_engine &owner, read8sm_delegate reader, write8sm_delegate writer, u32 addrshift) : m_address_shift(addrshift), m_status(STATUS_BRDY), m_curnibble(0), @@ -308,7 +359,9 @@ ymadpcm_b_channel::ymadpcm_b_channel(ymadpcm_b_registers regs, read8sm_delegate m_accumulator(0), m_prev_accum(0), m_adpcm_step(STEP_MIN), - m_regs(regs) + m_reader(reader), + m_writer(writer), + m_regs(owner.regs()) { } @@ -317,7 +370,7 @@ ymadpcm_b_channel::ymadpcm_b_channel(ymadpcm_b_registers regs, read8sm_delegate // save - register for save states //------------------------------------------------- -void ymadpcm_b_channel::save(device_t &device, u8 index) +void ymadpcm_b_channel::save(device_t &device, u32 index) { device.save_item(ADPCM_B_NAME(m_status), index); device.save_item(ADPCM_B_NAME(m_curnibble), index); @@ -388,6 +441,7 @@ void ymadpcm_b_channel::clock() m_accumulator = 0; m_prev_accum = 0; m_status = (m_status & ~STATUS_PLAYING) | STATUS_EOS; + LOG("ADPCM EOS\n"); return; } } @@ -433,19 +487,19 @@ void ymadpcm_b_channel::clock() // panning applied //------------------------------------------------- -void ymadpcm_b_channel::output(s32 &lsum, s32 &rsum, u8 rshift) const +void ymadpcm_b_channel::output(s32 outputs[2], u32 rshift) const { // do a linear interpolation between samples - s32 result = (m_prev_accum * ((m_position ^ 0xffff) + 1) + m_accumulator * m_position) >> 16; + s32 result = (m_prev_accum * s32((m_position ^ 0xffff) + 1) + m_accumulator * s32(m_position)) >> 16; // apply volume (level) in a linear fashion and reduce - result = (result * m_regs.level()) >> (8 + rshift); + result = (result * s32(m_regs.level())) >> (8 + rshift); // apply to left/right if (m_regs.pan_left()) - lsum += result; + outputs[0] += result; if (m_regs.pan_right()) - rsum += result; + outputs[1] += result; } @@ -453,7 +507,7 @@ void ymadpcm_b_channel::output(s32 &lsum, s32 &rsum, u8 rshift) const // read - handle special register reads //------------------------------------------------- -u8 ymadpcm_b_channel::read(u8 regnum) +u8 ymadpcm_b_channel::read(u32 regnum) { u8 result = 0; @@ -469,7 +523,10 @@ u8 ymadpcm_b_channel::read(u8 regnum) // did we hit the end? if so, signal EOS if (at_end()) + { m_status = STATUS_EOS | STATUS_BRDY; + LOG("ADPCM EOS\n"); + } // otherwise, write the data and signal ready else @@ -486,7 +543,7 @@ u8 ymadpcm_b_channel::read(u8 regnum) // write - handle special register writes //------------------------------------------------- -void ymadpcm_b_channel::write(u8 regnum, u8 value) +void ymadpcm_b_channel::write(u32 regnum, u8 value) { // register 0 can do a reset; also use writes here to reset the // dummy read counter @@ -495,9 +552,26 @@ void ymadpcm_b_channel::write(u8 regnum, u8 value) if (m_regs.execute()) { load_start(); - LOG("KeyOn ADPCM-B: repeat=%d speaker=%d pan=%d%d dac=%d 8bit=%d rom=%d start=%04X end=%04X prescale=%04X deltan=%04X level=%02X limit=%04X\n", m_regs.repeat(), m_regs.speaker(), m_regs.pan_left(), m_regs.pan_right(), m_regs.dac(), m_regs.dram_8bit(), m_regs.rom_ram(), m_regs.start(), m_regs.end(), m_regs.prescale(), m_regs.delta_n(), m_regs.level(), m_regs.limit()); + LOG("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(), + 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()); } - if (m_regs.reset()) + else + m_status &= ~STATUS_EOS; + if (m_regs.resetflag()) reset(); if (m_regs.external()) m_dummy_read = 2; @@ -522,7 +596,10 @@ void ymadpcm_b_channel::write(u8 regnum, u8 value) // did we hit the end? if so, signal EOS if (at_end()) + { + LOG("ADPCM EOS\n"); m_status = STATUS_EOS | STATUS_BRDY; + } // otherwise, write the data and signal ready else @@ -540,7 +617,7 @@ void ymadpcm_b_channel::write(u8 regnum, u8 value) // shift amount based on register settings //------------------------------------------------- -u8 ymadpcm_b_channel::address_shift() const +u32 ymadpcm_b_channel::address_shift() const { // if a constant address shift, just provide that if (m_address_shift != 0) @@ -584,19 +661,10 @@ void ymadpcm_b_channel::load_start() // ymadpcm_b_engine - constructor //------------------------------------------------- -ymadpcm_b_engine::ymadpcm_b_engine(device_t &device, read8sm_delegate reader, write8sm_delegate writer, u8 addrshift) : - m_regdata(0x10), - m_regs(m_regdata) +ymadpcm_b_engine::ymadpcm_b_engine(device_t &device, read8sm_delegate reader, write8sm_delegate writer, u32 addrshift) { // create the channel (only one supported for now, but leaving possibilities open) - m_channel[0] = std::make_unique(m_regs, reader, writer, addrshift); - - // clear registers by default - std::fill_n(&m_regdata[0], m_regdata.size(), 0); - - // set the limit to 0xffff by default - m_regs.write(0x0c, 0xff); - m_regs.write(0x0d, 0xff); + m_channel[0] = std::make_unique(*this, reader, writer, addrshift); } @@ -607,7 +675,7 @@ ymadpcm_b_engine::ymadpcm_b_engine(device_t &device, read8sm_delegate reader, wr void ymadpcm_b_engine::save(device_t &device) { // save our state - device.save_item(ADPCM_B_NAME(m_regdata)); + m_regs.save(device); // save channel state for (int chnum = 0; chnum < std::size(m_channel); chnum++) @@ -621,6 +689,9 @@ void ymadpcm_b_engine::save(device_t &device) void ymadpcm_b_engine::reset() { + // reset registers + m_regs.reset(); + // reset each channel for (auto &chan : m_channel) chan->reset(); @@ -631,7 +702,7 @@ void ymadpcm_b_engine::reset() // clock - master clocking function //------------------------------------------------- -void ymadpcm_b_engine::clock(u8 chanmask) +void ymadpcm_b_engine::clock(u32 chanmask) { // clock each channel, setting a bit in result if it finished for (int chnum = 0; chnum < std::size(m_channel); chnum++) @@ -644,12 +715,12 @@ void ymadpcm_b_engine::clock(u8 chanmask) // output - master output function //------------------------------------------------- -void ymadpcm_b_engine::output(s32 &lsum, s32 &rsum, u8 rshift, u8 chanmask) +void ymadpcm_b_engine::output(s32 outputs[2], u32 rshift, u32 chanmask) { // compute the output of each channel for (int chnum = 0; chnum < std::size(m_channel); chnum++) if (BIT(chanmask, chnum)) - m_channel[chnum]->output(lsum, rsum, rshift); + m_channel[chnum]->output(outputs, rshift); } @@ -657,7 +728,7 @@ void ymadpcm_b_engine::output(s32 &lsum, s32 &rsum, u8 rshift, u8 chanmask) // write - handle writes to the ADPCM-B registers //------------------------------------------------- -void ymadpcm_b_engine::write(u8 regnum, u8 data) +void ymadpcm_b_engine::write(u32 regnum, u8 data) { // store the raw value to the register array; // most writes are passive, consumed only when needed diff --git a/src/devices/sound/ymadpcm.h b/src/devices/sound/ymadpcm.h index 53f6db1e4b5..847af0b93ff 100644 --- a/src/devices/sound/ymadpcm.h +++ b/src/devices/sound/ymadpcm.h @@ -8,6 +8,10 @@ #include "dirom.h" +// forward declarations +class ymadpcm_a_engine; +class ymadpcm_b_engine; + // ======================> ymadpcm_a_registers @@ -29,51 +33,60 @@ // class ymadpcm_a_registers { - // private constructor to directly specify channel base - ymadpcm_a_registers(ymadpcm_a_registers const &src, u8 chbase) : - m_chbase(chbase), - m_regdata(src.m_regdata) - { - } - public: + // constants + static constexpr u32 OUTPUTS = 2; + static constexpr u32 CHANNELS = 6; + static constexpr u32 REGISTERS = 0x30; + static constexpr u32 ALL_CHANNELS = (1 << CHANNELS) - 1; + // constructor - ymadpcm_a_registers(std::vector ®data) : - m_chbase(0), - m_regdata(regdata) + ymadpcm_a_registers() { } + + // register for save states + void save(device_t &device); + + // reset to initial state + void reset(); + + // map channel number to register offset + static constexpr u32 channel_offset(u32 chnum) { + assert(chnum < CHANNELS); + return chnum; } - u8 chbase() const { return m_chbase; } - // direct read/write access - u8 read(u8 index) { return m_regdata[index]; } - void write(u8 index, u8 data) { m_regdata[index] = data; } - - // create a new version of ourself with a different channel/operator base - ymadpcm_a_registers channel_registers(u8 chnum) { return ymadpcm_a_registers(*this, chnum); } + void write(u32 index, u8 data) { m_regdata[index] = data; } // system-wide registers - u8 dump() const /* 1 bit */ { return BIT(m_regdata[0x00], 7); } - u8 dump_mask() const /* 6 bits */ { return BIT(m_regdata[0x00], 0, 6); } - u8 total_level() const /* 6 bits */ { return BIT(m_regdata[0x01], 0, 6); } - u8 test() const /* 8 bits */ { return m_regdata[0x02]; } + u32 dump() const { return BIT(m_regdata[0x00], 7); } + u32 dump_mask() const { return BIT(m_regdata[0x00], 0, 6); } + u32 total_level() const { return BIT(m_regdata[0x01], 0, 6); } + u32 test() const { return m_regdata[0x02]; } // per-channel registers - u8 pan_left() const /* 1 bit */ { return BIT(m_regdata[m_chbase + 0x08], 7); } - u8 pan_right() const /* 1 bit */ { return BIT(m_regdata[m_chbase + 0x08], 6); } - u8 instrument_level() const /* 5 bits */ { return BIT(m_regdata[m_chbase + 0x08], 0, 5); } - u16 start() const /* 16 bits */ { return m_regdata[m_chbase + 0x10] | (m_regdata[m_chbase + 0x18] << 8); } - u16 end() const /* 16 bits */ { return m_regdata[m_chbase + 0x20] | (m_regdata[m_chbase + 0x28] << 8); } + u32 ch_pan_left(u32 choffs) const { return BIT(m_regdata[choffs + 0x08], 7); } + u32 ch_pan_right(u32 choffs) const { return BIT(m_regdata[choffs + 0x08], 6); } + u32 ch_instrument_level(u32 choffs) const { return BIT(m_regdata[choffs + 0x08], 0, 5); } + u32 ch_start(u32 choffs) const { return m_regdata[choffs + 0x10] | (m_regdata[choffs + 0x18] << 8); } + u32 ch_end(u32 choffs) const { return m_regdata[choffs + 0x20] | (m_regdata[choffs + 0x28] << 8); } // per-channel writes - void write_start(u16 address) { write(m_chbase + 0x10, address); write(m_chbase + 0x18, address >> 8); } - void write_end(u16 address) { write(m_chbase + 0x20, address); write(m_chbase + 0x28, address >> 8); } + void write_start(u32 choffs, u32 address) + { + write(choffs + 0x10, address); + write(choffs + 0x18, address >> 8); + } + void write_end(u32 choffs, u32 address) + { + write(choffs + 0x20, address); + write(choffs + 0x28, address >> 8); + } private: // internal state - u8 m_chbase; // base offset for channel-specific data - std::vector &m_regdata; // reference to the raw data + u8 m_regdata[REGISTERS]; // register data }; @@ -83,10 +96,10 @@ class ymadpcm_a_channel { public: // constructor - ymadpcm_a_channel(ymadpcm_a_registers regs, read8sm_delegate reader, u8 addrshift); + ymadpcm_a_channel(ymadpcm_a_engine &owner, u32 choffs, read8sm_delegate reader, u32 addrshift); // register for save states - void save(device_t &device, u8 index); + void save(device_t &device, u32 index); // reset the channel state void reset(); @@ -98,22 +111,20 @@ public: bool clock(); // return the computed output value, with panning applied - void output(s32 &lsum, s32 &rsum) const; - - // direct parameter setting for YM2608 ROM-based samples - void set_start_end(u16 start, u16 end) { m_regs.write_start(start); m_regs.write_end(end); } + void output(s32 outputs[ymadpcm_a_registers::OUTPUTS]) const; private: // internal state - u8 const m_address_shift; // address bits shift-left - read8sm_delegate const m_reader; // read delegate - u8 m_playing; // currently playing? - u8 m_curnibble; // index of the current nibble - u8 m_curbyte; // current byte of data + u32 const m_choffs; // channel offset + u32 const m_address_shift; // address bits shift-left + u32 m_playing; // currently playing? + u32 m_curnibble; // index of the current nibble + u32 m_curbyte; // current byte of data u32 m_curaddress; // current address - s16 m_accumulator; // accumulator - s8 m_step_index; // index in the stepping table - ymadpcm_a_registers m_regs; // register accessor + s32 m_accumulator; // accumulator + s32 m_step_index; // index in the stepping table + read8sm_delegate const m_reader; // read delegate + ymadpcm_a_registers &m_regs; // reference to registers }; @@ -121,11 +132,12 @@ private: class ymadpcm_a_engine { - static constexpr int CHANNELS = 6; - public: + static constexpr int OUTPUTS = ymadpcm_a_registers::OUTPUTS; + static constexpr int CHANNELS = ymadpcm_a_registers::CHANNELS; + // constructor - ymadpcm_a_engine(device_t &device, read8sm_delegate reader, u8 addrshift); + ymadpcm_a_engine(device_t &device, read8sm_delegate reader, u32 addrshift); // save state handling void save(device_t &device); @@ -134,21 +146,28 @@ public: void reset(); // master clocking function - u8 clock(u8 chanmask); + u32 clock(u32 chanmask); // compute sum of channel outputs - void output(s32 &lsum, s32 &rsum, u8 chanmask); + void output(s32 outputs[ymadpcm_a_registers::OUTPUTS], u32 chanmask); // write to the ADPCM-A registers - void write(u8 regnum, u8 data); + void write(u32 regnum, u8 data); // set the start/end address for a channel (for hardcoded YM2608 percussion) - void set_start_end(u8 chnum, u16 start, u16 end) { m_channel[chnum]->set_start_end(start, end); } + void set_start_end(u8 chnum, u16 start, u16 end) + { + u32 choffs = ymadpcm_a_registers::channel_offset(chnum); + m_regs.write_start(choffs, start); + m_regs.write_end(choffs, end); + } + + // return a reference to our registers + ymadpcm_a_registers ®s() { return m_regs; } private: // internal state std::unique_ptr m_channel[CHANNELS]; // array of channels - std::vector m_regdata; // raw register data ymadpcm_a_registers m_regs; // register accessor }; @@ -189,42 +208,50 @@ private: class ymadpcm_b_registers { public: + // constants + static constexpr u32 OUTPUTS = 2; + static constexpr u32 CHANNELS = 1; + static constexpr u32 REGISTERS = 0x10; + static constexpr u32 ALL_CHANNELS = (1 << CHANNELS) - 1; + // constructor - ymadpcm_b_registers(std::vector ®data) : - m_regdata(regdata) - { - } + ymadpcm_b_registers() { } + + // register for save states + void save(device_t &device); + + // reset to initial state + void reset(); // direct read/write access - u8 read(u8 index) { return m_regdata[index]; } - void write(u8 index, u8 data) { m_regdata[index] = data; } + void write(u32 index, u8 data) { m_regdata[index] = data; } // system-wide registers - u8 execute() const /* 1 bit */ { return BIT(m_regdata[0x00], 7); } - u8 record() const /* 1 bit */ { return BIT(m_regdata[0x00], 6); } - u8 external() const /* 1 bit */ { return BIT(m_regdata[0x00], 5); } - u8 repeat() const /* 1 bit */ { return BIT(m_regdata[0x00], 4); } - u8 speaker() const /* 1 bit */ { return BIT(m_regdata[0x00], 3); } - u8 reset() const /* 1 bit */ { return BIT(m_regdata[0x00], 0); } - u8 pan_left() const /* 1 bit */ { return BIT(m_regdata[0x01], 7); } - u8 pan_right() const /* 1 bit */ { return BIT(m_regdata[0x01], 6); } - u8 start_conversion() const /* 1 bit */ { return BIT(m_regdata[0x01], 3); } - u8 dac_enable() const /* 1 bit */ { return BIT(m_regdata[0x01], 2); } - u8 dram_8bit() const /* 1 bit */ { return BIT(m_regdata[0x01], 1); } - u8 rom_ram() const /* 1 bit */ { return BIT(m_regdata[0x01], 0); } - u16 start() const /* 16 bits */ { return m_regdata[0x02] | (m_regdata[0x03] << 8); } - u16 end() const /* 16 bits */ { return m_regdata[0x04] | (m_regdata[0x05] << 8); } - u16 prescale() const /* 11 bits */ { return m_regdata[0x06] | (BIT(m_regdata[0x07], 0, 3) << 8); } - u8 cpudata() const /* 8 bits */ { return m_regdata[0x08]; } - u16 delta_n() const /* 16 bits */ { return m_regdata[0x09] | (m_regdata[0x0a] << 8); } - u8 level() const /* 8 bits */ { return m_regdata[0x0b]; } - u16 limit() const /* 16 bits */ { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); } - u8 dac() const /* 8 bits */ { return m_regdata[0x0e]; } - u8 pcm() const /* 8 bits */ { return m_regdata[0x0f]; } + u32 execute() const { return BIT(m_regdata[0x00], 7); } + u32 record() const { return BIT(m_regdata[0x00], 6); } + u32 external() const { return BIT(m_regdata[0x00], 5); } + u32 repeat() const { return BIT(m_regdata[0x00], 4); } + u32 speaker() const { return BIT(m_regdata[0x00], 3); } + u32 resetflag() const { return BIT(m_regdata[0x00], 0); } + u32 pan_left() const { return BIT(m_regdata[0x01], 7); } + u32 pan_right() const { return BIT(m_regdata[0x01], 6); } + u32 start_conversion() const { return BIT(m_regdata[0x01], 3); } + u32 dac_enable() const { return BIT(m_regdata[0x01], 2); } + u32 dram_8bit() const { return BIT(m_regdata[0x01], 1); } + u32 rom_ram() const { return BIT(m_regdata[0x01], 0); } + u32 start() const { return m_regdata[0x02] | (m_regdata[0x03] << 8); } + u32 end() const { return m_regdata[0x04] | (m_regdata[0x05] << 8); } + u32 prescale() const { return m_regdata[0x06] | (BIT(m_regdata[0x07], 0, 3) << 8); } + u32 cpudata() const { return m_regdata[0x08]; } + u32 delta_n() const { return m_regdata[0x09] | (m_regdata[0x0a] << 8); } + u32 level() const { return m_regdata[0x0b]; } + u32 limit() const { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); } + u32 dac() const { return m_regdata[0x0e]; } + u32 pcm() const { return m_regdata[0x0f]; } private: // internal state - std::vector &m_regdata; // reference to the raw data + u8 m_regdata[REGISTERS]; // register data }; @@ -241,10 +268,10 @@ public: static constexpr u8 STATUS_PLAYING = 0x04; // constructor - ymadpcm_b_channel(ymadpcm_b_registers regs, read8sm_delegate reader, write8sm_delegate writer, u8 addrshift); + ymadpcm_b_channel(ymadpcm_b_engine &owner, read8sm_delegate reader, write8sm_delegate writer, u32 addrshift); // register for save states - void save(device_t &device, u8 index); + void save(device_t &device, u32 index); // reset the channel state void reset(); @@ -256,20 +283,20 @@ public: void clock(); // return the computed output value, with panning applied - void output(s32 &lsum, s32 &rsum, u8 rshift) const; + void output(s32 outputs[ymadpcm_b_registers::OUTPUTS], u32 rshift) const; // return the status register u8 status() const { return m_status; } // handle special register reads - u8 read(u8 regnum); + u8 read(u32 regnum); // handle special register writes - void write(u8 regnum, u8 value); + void write(u32 regnum, u8 value); private: // helper - return the current address shift - u8 address_shift() const; + u32 address_shift() const; // load the start address void load_start(); @@ -281,19 +308,19 @@ private: bool at_end() const { return (m_curaddress >> address_shift()) > m_regs.end(); } // internal state - read8sm_delegate const m_reader; // read delegate - write8sm_delegate const m_writer;// write delegate - u8 const m_address_shift; // address bits shift-left - u8 m_status; // currently playing? - u8 m_curnibble; // index of the current nibble - u8 m_curbyte; // current byte of data - u8 m_dummy_read; // dummy read tracker - u16 m_position; // current fractional position + u32 const m_address_shift; // address bits shift-left + u32 m_status; // currently playing? + u32 m_curnibble; // index of the current nibble + u32 m_curbyte; // current byte of data + u32 m_dummy_read; // dummy read tracker + u32 m_position; // current fractional position u32 m_curaddress; // current address s32 m_accumulator; // accumulator s32 m_prev_accum; // previous accumulator (for linear interp) s32 m_adpcm_step; // next forecast - ymadpcm_b_registers m_regs; // register accessor + read8sm_delegate const m_reader; // read delegate + write8sm_delegate const m_writer;// write delegate + ymadpcm_b_registers &m_regs; // reference to registers }; @@ -301,11 +328,12 @@ private: class ymadpcm_b_engine { - static constexpr int CHANNELS = 1; - public: + static constexpr int OUTPUTS = ymadpcm_b_registers::OUTPUTS; + static constexpr int CHANNELS = ymadpcm_b_registers::CHANNELS; + // constructor - ymadpcm_b_engine(device_t &device, read8sm_delegate reader, write8sm_delegate writer, u8 addrshift = 0); + ymadpcm_b_engine(device_t &device, read8sm_delegate reader, write8sm_delegate writer, u32 addrshift = 0); // save state handling void save(device_t &device); @@ -314,24 +342,26 @@ public: void reset(); // master clocking function - void clock(u8 chanmask); + void clock(u32 chanmask); // compute sum of channel outputs - void output(s32 &lsum, s32 &rsum, u8 rshift, u8 chanmask); + void output(s32 outputs[2], u32 rshift, u32 chanmask); // read from the ADPCM-B registers - u8 read(u8 regnum) { return m_channel[0]->read(regnum); } + u32 read(u32 regnum) { return m_channel[0]->read(regnum); } // write to the ADPCM-B registers - void write(u8 regnum, u8 data); + void write(u32 regnum, u8 data); // status - u8 status(u8 chnum = 0) const { return m_channel[chnum]->status(); } + u8 status() const { return m_channel[0]->status(); } + + // return a reference to our registers + ymadpcm_b_registers ®s() { return m_regs; } private: // internal state std::unique_ptr m_channel[CHANNELS]; // array of channels - std::vector m_regdata; // raw register data ymadpcm_b_registers m_regs; // register accessor }; diff --git a/src/devices/sound/ymdeltat.cpp b/src/devices/sound/ymdeltat.cpp deleted file mode 100644 index ea13da9782b..00000000000 --- a/src/devices/sound/ymdeltat.cpp +++ /dev/null @@ -1,628 +0,0 @@ -// license:GPL-2.0+ -// copyright-holders:Jarek Burczynski -/* -** -** File: ymdeltat.c -** -** YAMAHA DELTA-T adpcm sound emulation subroutine -** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B) -** -** Base program is YM2610 emulator by Hiromitsu Shioya. -** Written by Tatsuyuki Satoh -** Improvements by Jarek Burczynski (bujar at mame dot net) -** -** -** History: -** -** 03-08-2003 Jarek Burczynski: -** - fixed BRDY flag implementation. -** -** 24-07-2003 Jarek Burczynski, Frits Hilderink: -** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset -** -** 22-07-2003 Jarek Burczynski, Frits Hilderink: -** - fixed external memory support -** -** 15-06-2003 Jarek Burczynski: -** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08) -** - implemented support for the Limit address register -** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM -** - implemented external memory access (read/write) via the ADPCM data reg reads/writes -** Thanks go to Frits Hilderink for the example code. -** -** 14-06-2003 Jarek Burczynski: -** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO -** - modified EOS handling -** -** 05-04-2003 Jarek Burczynski: -** - implemented partial support for external/processor memory on sample replay -** -** 01-12-2002 Jarek Burczynski: -** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi -** - renamed/removed some YM_DELTAT struct fields -** -** 28-12-2001 Acho A. Tang -** - added EOS status report on ADPCM playback. -** -** 05-08-2001 Jarek Burczynski: -** - now_step is initialized with 0 at the start of play. -** -** 12-06-2001 Jarek Burczynski: -** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC. -** Checked on real YM2610 chip - address register is 24 bits wide. -** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem. -** -** TO DO: -** Check size of the address register on the other chips.... -** -** Version 0.72 -** -** sound chips that have this unit: -** YM2608 OPNA -** YM2610/B OPNB -** Y8950 MSX AUDIO -** -*/ - -#include "emu.h" -#include "ymdeltat.h" - -#define YM_DELTAT_SHIFT (16) - -#define YM_DELTAT_DELTA_MAX (24576) -#define YM_DELTAT_DELTA_MIN (127) -#define YM_DELTAT_DELTA_DEF (127) - -#define YM_DELTAT_DECODE_RANGE 32768 -#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) -#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) - - -/* Forecast to next Forecast (rate = *8) */ -/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ -static constexpr int32_t ym_deltat_decode_tableB1[16] = { - 1, 3, 5, 7, 9, 11, 13, 15, - -1, -3, -5, -7, -9, -11, -13, -15, -}; -/* delta to next delta (rate= *64) */ -/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ -static constexpr int32_t ym_deltat_decode_tableB2[16] = { - 57, 57, 57, 57, 77, 102, 128, 153, - 57, 57, 57, 57, 77, 102, 128, 153 -}; - -#if 0 -void YM_DELTAT::BRDY_callback() -{ - logerror("BRDY_callback reached (flag set) !\n"); - - /* set BRDY bit in status register */ - if(status_set_handler) - if(status_change_BRDY_bit) - (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); -} -#endif - -uint8_t YM_DELTAT::ADPCM_Read() -{ - uint8_t v = 0; - - /* external memory read */ - if ((portstate & 0xe0) == 0x20) - { - /* two dummy reads */ - if (memread) - { - now_addr = start << 1; - memread--; - return 0; - } - - - if (now_addr != (end << 1)) - { - v = read_byte(device, now_addr>>1); - - /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", now_addr >> 1, v);*/ - - now_addr += 2; /* two nibbles at a time */ - - /* reset BRDY bit in status register, which means we are reading the memory now */ - if (status_reset_handler && status_change_BRDY_bit) - (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); - - /* setup a timer that will callback us in 10 master clock cycles for Y8950 - * in the callback set the BRDY flag to 1 , which means we have another data ready. - * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. - */ - /* set BRDY bit in status register */ - if (status_set_handler && status_change_BRDY_bit) - (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); - } - else - { - /* set EOS bit in status register */ - if (status_set_handler && status_change_EOS_bit) - (status_set_handler)(status_change_which_chip, status_change_EOS_bit); - } - } - - return v; -} - - -/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ -static constexpr uint8_t dram_rightshift[4]={3,0,0,0}; - -/* DELTA-T ADPCM write register */ -void YM_DELTAT::ADPCM_Write(int r, int v) -{ - if (r >= 0x10) return; - reg[r] = v; /* stock data */ - - switch (r) - { - case 0x00: -/* -START: - Accessing *external* memory is started when START bit (D7) is set to "1", so - you must set all conditions needed for recording/playback before starting. - If you access *CPU-managed* memory, recording/playback starts after - read/write of ADPCM data register $08. - -REC: - 0 = ADPCM synthesis (playback) - 1 = ADPCM analysis (record) - -MEMDATA: - 0 = processor (*CPU-managed*) memory (means: using register $08) - 1 = external memory (using start/end/limit registers to access memory: RAM or ROM) - - -SPOFF: - controls output pin that should disable the speaker while ADPCM analysis - -RESET and REPEAT only work with external memory. - - -some examples: -value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: - C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register - E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register - 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register - a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register - - 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 - 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 - -*/ - /* handle emulation mode */ - if (emulation_mode == EMULATION_MODE_YM2610) - { - v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */ - v &= ~0x40; /* YM2610 has no rec bit */ - } - - portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */ - - if (portstate & 0x80)/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ - { - /* set PCM BUSY bit */ - PCM_BSY = 1; - - /* start ADPCM */ - now_step = 0; - acc = 0; - prev_acc = 0; - adpcml = 0; - adpcmd = YM_DELTAT_DELTA_DEF; - now_data = 0; - - } - - if (portstate & 0x20) /* do we access external memory? */ - { - now_addr = start << 1; - memread = 2; /* two dummy reads needed before accesing external memory via register $08*/ - } - else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */ - { - now_addr = 0; - } - - if (portstate & 0x01) - { - portstate = 0x00; - - /* clear PCM BUSY bit (in status register) */ - PCM_BSY = 0; - - /* set BRDY flag */ - if (status_set_handler && status_change_BRDY_bit) - (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); - } - break; - - case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ - /* handle emulation mode */ - if (emulation_mode == EMULATION_MODE_YM2610) - { - v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */ - } - - pan = &output_pointer[(v >> 6) & 0x03]; - if ((control2 & 3) != (v & 3)) - { - /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ - if (DRAMportshift != dram_rightshift[v & 3]) - { - DRAMportshift = dram_rightshift[v & 3]; - - /* final shift value depends on chip type and memory type selected: - 8 for YM2610 (ROM only), - 5 for ROM for Y8950 and YM2608, - 5 for x8bit DRAMs for Y8950 and YM2608, - 2 for x1bit DRAMs for Y8950 and YM2608. - */ - - /* refresh addresses */ - start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift); - end = (reg[0x5] * 0x0100 | reg[0x4]) << (portshift - DRAMportshift); - end += (1 << (portshift - DRAMportshift)) - 1; - limit = (reg[0xd]*0x0100 | reg[0xc]) << (portshift - DRAMportshift); - } - } - control2 = v; - break; - - case 0x02: /* Start Address L */ - case 0x03: /* Start Address H */ - start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift); - /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",reg[0x2], reg[0x3],start );*/ - break; - - case 0x04: /* Stop Address L */ - case 0x05: /* Stop Address H */ - end = (reg[0x5]*0x0100 | reg[0x4]) << (portshift - DRAMportshift); - end += (1 << (portshift - DRAMportshift)) - 1; - /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",reg[0x4], reg[0x5],end );*/ - break; - - case 0x06: /* Prescale L (ADPCM and Record frq) */ - case 0x07: /* Prescale H */ - break; - - case 0x08: /* ADPCM data */ -/* -some examples: -value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: - C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register - E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register - 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register - a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register - - 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 - 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 - -*/ - - /* external memory write */ - if ((portstate & 0xe0) == 0x60) - { - if (memread) - { - now_addr = start << 1; - memread = 0; - } - - /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", now_addr >> 1, v);*/ - - if (now_addr != (end << 1)) - { - write_byte(device, now_addr >> 1, v); - now_addr += 2; /* two nybbles at a time */ - - /* reset BRDY bit in status register, which means we are processing the write */ - if (status_reset_handler && status_change_BRDY_bit) - (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); - - /* setup a timer that will callback us in 10 master clock cycles for Y8950 - * in the callback set the BRDY flag to 1 , which means we have written the data. - * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. - */ - /* set BRDY bit in status register */ - if (status_set_handler && status_change_BRDY_bit) - (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); - - } - else - { - /* set EOS bit in status register */ - if (status_set_handler && status_change_EOS_bit) - (status_set_handler)(status_change_which_chip, status_change_EOS_bit); - } - - return; - } - - /* ADPCM synthesis from CPU */ - if ((portstate & 0xe0) == 0x80) - { - CPU_data = v; - - /* Reset BRDY bit in status register, which means we are full of data */ - if (status_reset_handler && status_change_BRDY_bit) - (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); - return; - } - - break; - - case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ - case 0x0a: /* DELTA-N H */ - delta = (reg[0xa] * 0x0100 | reg[0x9]); - step = uint32_t(double(delta /* *(1<<(YM_DELTAT_SHIFT-16)) */) * freqbase); - /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",reg[0x9], reg[0xa]);*/ - break; - - case 0x0b: /* Output level control (volume, linear) */ - { - const int32_t oldvol = volume; - volume = (v & 0xff) * (output_range / 256) / YM_DELTAT_DECODE_RANGE; -/* v * ((1<<16)>>8) >> 15; -* thus: v * (1<<8) >> 15; -* thus: output_range must be (1 << (15+8)) at least -* v * ((1<<23)>>8) >> 15; -* v * (1<<15) >> 15; -*/ - /*logerror("DELTAT vol = %2x\n",v&0xff);*/ - if (oldvol != 0) - { - adpcml = int(double(adpcml) / double(oldvol) * double(volume)); - } - } - break; - - case 0x0c: /* Limit Address L */ - case 0x0d: /* Limit Address H */ - limit = (reg[0xd] * 0x0100 | reg[0xc]) << (portshift - DRAMportshift); - /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",reg[0xc], reg[0xd],limit );*/ - break; - } -} - -void YM_DELTAT::ADPCM_Reset(int panidx, int mode, device_t *dev) -{ - device = dev; - now_addr = 0; - now_step = 0; - step = 0; - start = 0; - end = 0; - limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */ - volume = 0; - pan = &output_pointer[panidx]; - acc = 0; - prev_acc = 0; - adpcmd = 127; - adpcml = 0; - emulation_mode = uint8_t(mode); - portstate = (emulation_mode == EMULATION_MODE_YM2610) ? 0x20 : 0; - control2 = (emulation_mode == EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */ - DRAMportshift = dram_rightshift[control2 & 3]; - - /* The flag mask register disables the BRDY after the reset, however - ** as soon as the mask is enabled the flag needs to be set. */ - - /* set BRDY bit in status register */ - if (status_set_handler && status_change_BRDY_bit) - (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); -} - -void YM_DELTAT::postload(uint8_t *regs) -{ - /* to keep adpcml */ - volume = 0; - /* update */ - for (int r = 1; r < 16; r++) - ADPCM_Write(r, regs[r]); - reg[0] = regs[0]; - - /* current rom data */ - now_data = read_byte(device, now_addr >> 1); - -} -void YM_DELTAT::savestate(device_t *device) -{ -#ifdef MAME_EMU_SAVE_H - YM_DELTAT *const DELTAT = this; // makes the save name sensible - device->save_item(NAME(DELTAT->portstate)); - device->save_item(NAME(DELTAT->now_addr)); - device->save_item(NAME(DELTAT->now_step)); - device->save_item(NAME(DELTAT->acc)); - device->save_item(NAME(DELTAT->prev_acc)); - device->save_item(NAME(DELTAT->adpcmd)); - device->save_item(NAME(DELTAT->adpcml)); -#endif -} - - -#define YM_DELTAT_Limit(val,max,min) \ -{ \ - if ( val > max ) val = max; \ - else if ( val < min ) val = min; \ -} - -static inline void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT) -{ - uint32_t step; - int data; - - DELTAT->now_step += DELTAT->step; - if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; - DELTAT->now_step &= (1<now_addr == (DELTAT->limit<<1) ) - DELTAT->now_addr = 0; - - if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */ - if( DELTAT->portstate&0x10 ){ - /* repeat start */ - DELTAT->now_addr = DELTAT->start<<1; - DELTAT->acc = 0; - DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; - DELTAT->prev_acc = 0; - }else{ - /* set EOS bit in status register */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_EOS_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); - - /* clear PCM BUSY bit (reflected in status register) */ - DELTAT->PCM_BSY = 0; - - DELTAT->portstate = 0; - DELTAT->adpcml = 0; - DELTAT->prev_acc = 0; - return; - } - } - - if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f; - else - { - DELTAT->now_data = DELTAT->read_byte(DELTAT->device, DELTAT->now_addr>>1); - data = DELTAT->now_data >> 4; - } - - DELTAT->now_addr++; - /* 12-06-2001 JB: */ - /* YM2610 address register is 24 bits wide.*/ - /* The "+1" is there because we use 1 bit more for nibble calculations.*/ - /* WARNING: */ - /* Side effect: we should take the size of the mapped ROM into account */ - DELTAT->now_addr &= ( (1<<(24+1))-1); - - /* store accumulator value */ - DELTAT->prev_acc = DELTAT->acc; - - /* Forecast to next Forecast */ - DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); - YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); - - /* delta to next delta */ - DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; - YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); - - /* ElSemi: Fix interpolator. */ - /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/ - - }while(--step); - - } - - /* ElSemi: Fix interpolator. */ - DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); - DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); - DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; - - /* output for work of output channels (outd[OPNxxxx])*/ - *(DELTAT->pan) += DELTAT->adpcml; -} - - - -static inline void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT) -{ - uint32_t step; - int data; - - DELTAT->now_step += DELTAT->step; - if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; - DELTAT->now_step &= (1<now_addr&1 ) - { - data = DELTAT->now_data & 0x0f; - - DELTAT->now_data = DELTAT->CPU_data; - - /* after we used CPU_data, we set BRDY bit in status register, - * which means we are ready to accept another byte of data */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); - } - else - { - data = DELTAT->now_data >> 4; - } - - DELTAT->now_addr++; - - /* store accumulator value */ - DELTAT->prev_acc = DELTAT->acc; - - /* Forecast to next Forecast */ - DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); - YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); - - /* delta to next delta */ - DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; - YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); - - - }while(--step); - - } - - /* ElSemi: Fix interpolator. */ - DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); - DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); - DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; - - /* output for work of output channels (outd[OPNxxxx])*/ - *(DELTAT->pan) += DELTAT->adpcml; -} - - - -/* ADPCM B (Delta-T control type) */ -void YM_DELTAT::ADPCM_CALC() -{ -/* -some examples: -value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: - 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register - a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register - C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register - E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register - - 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 - 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 - -*/ - - if ( (portstate & 0xe0)==0xa0 ) - { - YM_DELTAT_synthesis_from_external_memory(this); - return; - } - - if ( (portstate & 0xe0)==0x80 ) - { - /* ADPCM synthesis from CPU-managed memory (from reg $08) */ - YM_DELTAT_synthesis_from_CPU_memory(this); /* change output based on data in ADPCM data reg ($08) */ - return; - } - -//todo: ADPCM analysis -// if ( (portstate & 0xe0)==0xc0 ) -// if ( (portstate & 0xe0)==0xe0 ) - - return; -} diff --git a/src/devices/sound/ymdeltat.h b/src/devices/sound/ymdeltat.h deleted file mode 100644 index def88c712b9..00000000000 --- a/src/devices/sound/ymdeltat.h +++ /dev/null @@ -1,88 +0,0 @@ -// license:GPL-2.0+ -// copyright-holders:Jarek Burczynski -#ifndef MAME_SOUND_YMDELTAT_H -#define MAME_SOUND_YMDELTAT_H - -#pragma once - - -typedef uint8_t (*FM_READBYTE)(device_t *device, offs_t offset); -typedef void (*FM_WRITEBYTE)(device_t *device, offs_t offset, uint8_t data); -typedef void (*STATUS_CHANGE_HANDLER)(void *chip, uint8_t status_bits); - - -/* DELTA-T (adpcm type B) struct */ -struct YM_DELTAT { /* AT: rearranged and tightened structure */ - static constexpr int EMULATION_MODE_NORMAL = 0; - static constexpr int EMULATION_MODE_YM2610 = 1; - - FM_READBYTE read_byte; - FM_WRITEBYTE write_byte; - int32_t *output_pointer;/* pointer of output pointers */ - int32_t *pan; /* pan : &output_pointer[pan] */ - double freqbase; -#if 0 - double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */ - double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */ -#endif - uint32_t memory_size; - int output_range; - uint32_t now_addr; /* current address */ - uint32_t now_step; /* correct step */ - uint32_t step; /* step */ - uint32_t start; /* start address */ - uint32_t limit; /* limit address */ - uint32_t end; /* end address */ - uint32_t delta; /* delta scale */ - int32_t volume; /* current volume */ - int32_t acc; /* shift Measurement value*/ - int32_t adpcmd; /* next Forecast */ - int32_t adpcml; /* current value */ - int32_t prev_acc; /* leveling value */ - uint8_t now_data; /* current rom data */ - uint8_t CPU_data; /* current data from reg 08 */ - uint8_t portstate; /* port status */ - uint8_t control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */ - uint8_t portshift; /* address bits shift-left: - ** 8 for YM2610, - ** 5 for Y8950 and YM2608 */ - - uint8_t DRAMportshift; /* address bits shift-right: - ** 0 for ROM and x8bit DRAMs, - ** 3 for x1 DRAMs */ - - uint8_t memread; /* needed for reading/writing external memory */ - - /* handlers and parameters for the status flags support */ - STATUS_CHANGE_HANDLER status_set_handler; - STATUS_CHANGE_HANDLER status_reset_handler; - - /* note that different chips have these flags on different - ** bits of the status register - */ - void * status_change_which_chip; /* this chip id */ - uint8_t status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/ - uint8_t status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */ - uint8_t status_change_ZERO_bit; /* 1 if silence lasts for more than 290 milliseconds on ADPCM recording */ - - /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above, - ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608) - */ - uint8_t PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */ - - uint8_t reg[16]; /* adpcm registers */ - uint8_t emulation_mode; /* which chip we're emulating */ - device_t *device; - - /*void BRDY_callback();*/ - - uint8_t ADPCM_Read(); - void ADPCM_Write(int r, int v); - void ADPCM_Reset(int panidx, int mode, device_t *dev); - void ADPCM_CALC(); - - void postload(uint8_t *regs); - void savestate(device_t *device); -}; - -#endif // MAME_SOUND_YMDELTAT_H diff --git a/src/devices/sound/ymf262.cpp b/src/devices/sound/ymf262.cpp index 0f7b32f7e5c..a7c77f743c7 100644 --- a/src/devices/sound/ymf262.cpp +++ b/src/devices/sound/ymf262.cpp @@ -1,2803 +1,138 @@ -// license:GPL-2.0+ -// copyright-holders:Jarek Burczynski -/* -** -** File: ymf262.c - software implementation of YMF262 -** FM sound generator type OPL3 -** -** Copyright Jarek Burczynski -** -** Version 0.2 -** - -Revision History: - -03-03-2003: initial release - - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips - - thanks to Stiletto for the datasheets - - Features as listed in 4MF262A6 data sheet: - 1. Registers are compatible with YM3812 (OPL2) FM sound source. - 2. Up to six sounds can be used as four-operator melody sounds for variety. - 3. 18 simultaneous melody sounds, or 15 melody sounds with 5 rhythm sounds (with two operators). - 4. 6 four-operator melody sounds and 6 two-operator melody sounds, or 6 four-operator melody - sounds, 3 two-operator melody sounds and 5 rhythm sounds (with four operators). - 5. 8 selectable waveforms. - 6. 4-channel sound output. - 7. YMF262 compabile DAC (YAC512) is available. - 8. LFO for vibrato and tremolo effedts. - 9. 2 programable timers. - 10. Shorter register access time compared with YM3812. - 11. 5V single supply silicon gate CMOS process. - 12. 24 Pin SOP Package (YMF262-M), 48 Pin SQFP Package (YMF262-S). - - -differences between OPL2 and OPL3 not documented in Yamaha datahasheets: -- sinus table is a little different: the negative part is off by one... - -- in order to enable selection of four different waveforms on OPL2 - one must set bit 5 in register 0x01(test). - on OPL3 this bit is ignored and 4-waveform select works *always*. - (Don't confuse this with OPL3's 8-waveform select.) - -- Envelope Generator: all 15 x rates take zero time on OPL3 - (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates - take zero time) - -- channel calculations: output of operator 1 is in perfect sync with - output of operator 2 on OPL3; on OPL and OPL2 output of operator 1 - is always delayed by one sample compared to output of operator 2 - - -differences between OPL2 and OPL3 shown in datasheets: -- YMF262 does not support CSM mode - - -*/ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles #include "emu.h" #include "ymf262.h" -/* output final shift */ -#if (OPL3_SAMPLE_BITS==16) - #define FINAL_SH (0) - #define MAXOUT (+32767) - #define MINOUT (-32768) -#else - #define FINAL_SH (8) - #define MAXOUT (+127) - #define MINOUT (-128) -#endif - - -#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ -#define EG_SH 16 /* 16.16 fixed point (EG timing) */ -#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ -#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ - -#define FREQ_MASK ((1<>8)&0xff,sample[0]); \ - } - #else /*save to STEREO file */ - #define SAVE_ALL_CHANNELS \ - { signed int pom = a; \ - fputc((unsigned short)pom&0xff,sample[0]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ - pom = b; \ - fputc((unsigned short)pom&0xff,sample[0]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ - } - #endif -#endif - - -#define OPL3_TYPE_YMF262 (0) /* 36 operators, 8 waveforms */ - - -struct OPL3_SLOT -{ - uint32_t ar; /* attack rate: AR<<2 */ - uint32_t dr; /* decay rate: DR<<2 */ - uint32_t rr; /* release rate:RR<<2 */ - uint8_t KSR; /* key scale rate */ - uint8_t ksl; /* keyscale level */ - uint8_t ksr; /* key scale rate: kcode>>KSR */ - uint8_t mul; /* multiple: mul_tab[ML] */ - - /* Phase Generator */ - uint32_t Cnt; /* frequency counter */ - uint32_t Incr; /* frequency counter step */ - uint8_t FB; /* feedback shift value */ - uint8_t conn_enum; /* slot output route */ - int32_t *connect; /* slot output pointer */ - int32_t op1_out[2]; /* slot1 output for feedback */ - uint8_t CON; /* connection (algorithm) type */ - - /* Envelope Generator */ - uint8_t eg_type; /* percussive/non-percussive mode */ - uint8_t state; /* phase type */ - uint32_t TL; /* total level: TL << 2 */ - int32_t TLL; /* adjusted now TL */ - int32_t volume; /* envelope counter */ - uint32_t sl; /* sustain level: sl_tab[SL] */ - - uint32_t eg_m_ar; /* (attack state) */ - uint8_t eg_sh_ar; /* (attack state) */ - uint8_t eg_sel_ar; /* (attack state) */ - uint32_t eg_m_dr; /* (decay state) */ - uint8_t eg_sh_dr; /* (decay state) */ - uint8_t eg_sel_dr; /* (decay state) */ - uint32_t eg_m_rr; /* (release state) */ - uint8_t eg_sh_rr; /* (release state) */ - uint8_t eg_sel_rr; /* (release state) */ - - uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */ - - /* LFO */ - uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ - uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/ - - /* waveform select */ - uint8_t waveform_number; - unsigned int wavetable; - - //unsigned char reserved[128-84];//speedup: pump up the struct size to power of 2 - unsigned char reserved[128-100];//speedup: pump up the struct size to power of 2 - -}; - -struct OPL3_CH -{ - OPL3_SLOT SLOT[2]; - - uint32_t block_fnum; /* block+fnum */ - uint32_t fc; /* Freq. Increment base */ - uint32_t ksl_base; /* KeyScaleLevel Base step */ - uint8_t kcode; /* key code (for key scaling) */ - - /* - there are 12 2-operator channels which can be combined in pairs - to form six 4-operator channel, they are: - 0 and 3, - 1 and 4, - 2 and 5, - 9 and 12, - 10 and 13, - 11 and 14 - */ - uint8_t extended; /* set to 1 if this channel forms up a 4op channel with another channel(only used by first of pair of channels, ie 0,1,2 and 9,10,11) */ - - unsigned char reserved[512-272];//speedup:pump up the struct size to power of 2 - -}; - -/* OPL3 state */ -struct OPL3 -{ - OPL3_CH P_CH[18]; /* OPL3 chips have 18 channels */ - - uint32_t pan[18*4]; /* channels output masks (0xffffffff = enable); 4 masks per one channel */ - uint32_t pan_ctrl_value[18]; /* output control values 1 per one channel (1 value contains 4 masks) */ - - signed int chanout[18]; - signed int phase_modulation; /* phase modulation input (SLOT 2) */ - signed int phase_modulation2; /* phase modulation input (SLOT 3 in 4 operator channels) */ - - uint32_t eg_cnt; /* global envelope generator counter */ - uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/divider */ - uint32_t eg_timer_add; /* step of eg_timer */ - uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */ - - uint32_t fn_tab[1024]; /* fnumber->increment counter */ - - /* LFO */ - uint32_t LFO_AM; - int32_t LFO_PM; - - uint8_t lfo_am_depth; - uint8_t lfo_pm_depth_range; - uint32_t lfo_am_cnt; - uint32_t lfo_am_inc; - uint32_t lfo_pm_cnt; - uint32_t lfo_pm_inc; - - uint32_t noise_rng; /* 23 bit noise shift register */ - uint32_t noise_p; /* current noise 'phase' */ - uint32_t noise_f; /* current noise period */ - - uint8_t OPL3_mode; /* OPL3 extension enable flag */ - - uint8_t rhythm; /* Rhythm mode */ - - int T[2]; /* timer counters */ - uint8_t st[2]; /* timer enable */ - - uint32_t address; /* address register */ - uint8_t status; /* status flag */ - uint8_t statusmask; /* status mask */ - - uint8_t nts; /* NTS (note select) */ - - /* external event callback handlers */ - OPL3_TIMERHANDLER timer_handler; - device_t *TimerParam; - OPL3_IRQHANDLER IRQHandler; - device_t *IRQParam; - OPL3_UPDATEHANDLER UpdateHandler; - device_t *UpdateParam; - - uint8_t type; /* chip type */ - int clock; /* master clock (Hz) */ - int rate; /* sampling rate (Hz) */ - int divider; /* clock divider */ - double freqbase; /* frequency base */ - attotime TimerBase; /* Timer base time (==sampling time)*/ - device_t *device; - - /* Optional handlers */ - void SetTimerHandler(OPL3_TIMERHANDLER handler, device_t *device) - { - timer_handler = handler; - TimerParam = device; - } - void SetIRQHandler(OPL3_IRQHANDLER handler, device_t *device) - { - IRQHandler = handler; - IRQParam = device; - } - void SetUpdateHandler(OPL3_UPDATEHANDLER handler, device_t *device) - { - UpdateHandler = handler; - UpdateParam = device; - } -}; - -} // anonymous namespace +DEFINE_DEVICE_TYPE(YMF262, ymf262_device, "ymf262", "YMF262 OPL3") +//********************************************************* +// YMF262 DEVICE +//********************************************************* -/* mapping of register number (offset) to slot number used by the emulator */ -static const int slot_array[32]= -{ - 0, 2, 4, 1, 3, 5,-1,-1, - 6, 8,10, 7, 9,11,-1,-1, - 12,14,16,13,15,17,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1 -}; +//------------------------------------------------- +// ymf262_device - constructor +//------------------------------------------------- -/* key scale level */ -/* table is 3dB/octave , DV converts this into 6dB/octave */ -/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ -#define DV (0.1875/2.0) -static const double ksl_tab[8*16]= +ymf262_device::ymf262_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type) : + device_t(mconfig, type, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_address(0), + m_stream(nullptr), + m_fm(*this) { - /* OCT 0 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - /* OCT 1 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, - 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, - /* OCT 2 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, - 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, - 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, - /* OCT 3 */ - 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, - 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, - 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, - 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, - /* OCT 4 */ - 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, - 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, - 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, - 10.875/DV,11.250/DV,11.625/DV,12.000/DV, - /* OCT 5 */ - 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, - 9.000/DV,10.125/DV,10.875/DV,11.625/DV, - 12.000/DV,12.750/DV,13.125/DV,13.500/DV, - 13.875/DV,14.250/DV,14.625/DV,15.000/DV, - /* OCT 6 */ - 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, - 12.000/DV,13.125/DV,13.875/DV,14.625/DV, - 15.000/DV,15.750/DV,16.125/DV,16.500/DV, - 16.875/DV,17.250/DV,17.625/DV,18.000/DV, - /* OCT 7 */ - 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, - 15.000/DV,16.125/DV,16.875/DV,17.625/DV, - 18.000/DV,18.750/DV,19.125/DV,19.500/DV, - 19.875/DV,20.250/DV,20.625/DV,21.000/DV -}; -#undef DV - -/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ -static const uint32_t ksl_shift[4] = { 31, 1, 2, 0 }; - - -/* sustain level table (3dB per step) */ -/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ -#define SC(db) (uint32_t) ( db * (2.0/ENV_STEP) ) -static const uint32_t sl_tab[16]={ - SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), - SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) -}; -#undef SC - - -#define RATE_STEPS (8) -static const unsigned char eg_inc[15*RATE_STEPS]={ -/*cycle:0 1 2 3 4 5 6 7*/ - -/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ -/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ -/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ -/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ - -/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ -/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ -/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ -/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ - -/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ -/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ -/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ -/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ - -/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 for decay */ -/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */ -/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ -}; - - -#define O(a) (a*RATE_STEPS) - -/* note that there is no O(13) in this table - it's directly in the code */ -static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ -/* 16 infinite time rates */ -O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), -O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), - -/* rates 00-12 */ -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), - -/* rate 13 */ -O( 4),O( 5),O( 6),O( 7), - -/* rate 14 */ -O( 8),O( 9),O(10),O(11), - -/* rate 15 */ -O(12),O(12),O(12),O(12), - -/* 16 dummy rates (same as 15 3) */ -O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), -O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), - -}; -#undef O - -/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ -/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ -/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ - -#define O(a) (a*1) -static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ -/* 16 infinite time rates */ -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), - -/* rates 00-12 */ -O(12),O(12),O(12),O(12), -O(11),O(11),O(11),O(11), -O(10),O(10),O(10),O(10), -O( 9),O( 9),O( 9),O( 9), -O( 8),O( 8),O( 8),O( 8), -O( 7),O( 7),O( 7),O( 7), -O( 6),O( 6),O( 6),O( 6), -O( 5),O( 5),O( 5),O( 5), -O( 4),O( 4),O( 4),O( 4), -O( 3),O( 3),O( 3),O( 3), -O( 2),O( 2),O( 2),O( 2), -O( 1),O( 1),O( 1),O( 1), -O( 0),O( 0),O( 0),O( 0), - -/* rate 13 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 14 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 15 */ -O( 0),O( 0),O( 0),O( 0), - -/* 16 dummy rates (same as 15 3) */ -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), - -}; -#undef O - - -/* multiple table */ -#define ML 2 -static const uint8_t mul_tab[16]= { -/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ - ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML, - 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML -}; -#undef ML - -/* TL_TAB_LEN is calculated as: - -* (12+1)=13 - sinus amplitude bits (Y axis) -* additional 1: to compensate for calculations of negative part of waveform -* (if we don't add it then the greatest possible _negative_ value would be -2 -* and we really need -1 for waveform #7) -* 2 - sinus sign bit (Y axis) -* TL_RES_LEN - sinus resolution (X axis) -*/ -#define TL_TAB_LEN (13*2*TL_RES_LEN) -static signed int tl_tab[TL_TAB_LEN]; - -#define ENV_QUIET (TL_TAB_LEN>>4) - -/* sin waveform table in 'decibel' scale */ -/* there are eight waveforms on OPL3 chips */ -static unsigned int sin_tab[SIN_LEN * 8]; - - -/* LFO Amplitude Modulation table (verified on real YM3812) - 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples - - Length: 210 elements. - - Each of the elements has to be repeated - exactly 64 times (on 64 consecutive samples). - The whole table takes: 64 * 210 = 13440 samples. - - When AM = 1 data is used directly - When AM = 0 data is divided by 4 before being used (losing precision is important) -*/ - -#define LFO_AM_TAB_ELEMENTS 210 - -static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS] = { -0,0,0,0,0,0,0, -1,1,1,1, -2,2,2,2, -3,3,3,3, -4,4,4,4, -5,5,5,5, -6,6,6,6, -7,7,7,7, -8,8,8,8, -9,9,9,9, -10,10,10,10, -11,11,11,11, -12,12,12,12, -13,13,13,13, -14,14,14,14, -15,15,15,15, -16,16,16,16, -17,17,17,17, -18,18,18,18, -19,19,19,19, -20,20,20,20, -21,21,21,21, -22,22,22,22, -23,23,23,23, -24,24,24,24, -25,25,25,25, -26,26,26, -25,25,25,25, -24,24,24,24, -23,23,23,23, -22,22,22,22, -21,21,21,21, -20,20,20,20, -19,19,19,19, -18,18,18,18, -17,17,17,17, -16,16,16,16, -15,15,15,15, -14,14,14,14, -13,13,13,13, -12,12,12,12, -11,11,11,11, -10,10,10,10, -9,9,9,9, -8,8,8,8, -7,7,7,7, -6,6,6,6, -5,5,5,5, -4,4,4,4, -3,3,3,3, -2,2,2,2, -1,1,1,1 -}; - -/* LFO Phase Modulation table (verified on real YM3812) */ -static const int8_t lfo_pm_table[8*8*2] = { -/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ -4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ -5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ -6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ -7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ -}; - - -/* lock level of common table */ -static int num_lock = 0; - -/* work table */ -#define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1]) -#define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2]) -#define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1]) -#define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2]) - - -static inline void OPL3_SLOT_CONNECT(OPL3 *chip, OPL3_SLOT *slot) { - if (slot->conn_enum == CONN_NULL) { - slot->connect = nullptr; - } else if (slot->conn_enum >= CONN_CHAN0 && slot->conn_enum < CONN_PHASEMOD) { - slot->connect = &chip->chanout[slot->conn_enum - CONN_CHAN0]; - } else if (slot->conn_enum == CONN_PHASEMOD) { - slot->connect = &chip->phase_modulation; - } else if (slot->conn_enum == CONN_PHASEMOD2) { - slot->connect = &chip->phase_modulation2; - } } -#if 0 -static inline int limit( int val, int max, int min ) { - if ( val > max ) - val = max; - else if ( val < min ) - val = min; - return val; -} -#endif +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- -/* status set and IRQ handling */ -static inline void OPL3_STATUS_SET(OPL3 *chip,int flag) +u8 ymf262_device::read(offs_t offset) { - /* set status flag masking out disabled IRQs */ - chip->status |= (flag & chip->statusmask); - if(!(chip->status & 0x80)) + u8 result = 0x00; + switch (offset & 3) { - if(chip->status & 0x7f) - { /* IRQ on */ - chip->status |= 0x80; - /* callback user interrupt handler (IRQ is OFF to ON) */ - if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,1); - } - } -} + case 0: // status port (A0=0, A1=0) + result = m_fm.status(); + break; -/* status reset and IRQ handling */ -static inline void OPL3_STATUS_RESET(OPL3 *chip,int flag) -{ - /* reset status flag */ - chip->status &= ~flag; - if(chip->status & 0x80) - { - if (!(chip->status & 0x7f)) - { - chip->status &= 0x7f; - /* callback user interrupt handler (IRQ is ON to OFF) */ - if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,0); - } + default: // datasheet says anything else is not guaranteed + logerror("Unexpected read from YMF262 offset %d\n", offset & 3); + break; } -} - -/* IRQ mask set */ -static inline void OPL3_STATUSMASK_SET(OPL3 *chip,int flag) -{ - chip->statusmask = flag; - /* IRQ handling check */ - OPL3_STATUS_SET(chip,0); - OPL3_STATUS_RESET(chip,0); + return result; } -/* advance LFO to next sample */ -static inline void advance_lfo(OPL3 *chip) -{ - uint8_t tmp; - - /* LFO */ - chip->lfo_am_cnt += chip->lfo_am_inc; - if (chip->lfo_am_cnt >= ((uint32_t)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((uint32_t)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; - - if (chip->lfo_am_depth) - chip->LFO_AM = tmp; - else - chip->LFO_AM = tmp>>2; +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- - chip->lfo_pm_cnt += chip->lfo_pm_inc; - chip->LFO_PM = ((chip->lfo_pm_cnt>>LFO_SH) & 7) | chip->lfo_pm_depth_range; -} - -/* advance to next sample */ -static inline void advance(OPL3 *chip) +void ymf262_device::write(offs_t offset, u8 value) { - OPL3_CH *CH; - OPL3_SLOT *op; - int i; - - chip->eg_timer += chip->eg_timer_add; - - while (chip->eg_timer >= chip->eg_timer_overflow) + switch (offset & 1) { - chip->eg_timer -= chip->eg_timer_overflow; + case 0: // address ports - A1 references upper bank + m_address = value | (BIT(offset, 1) << 8); - chip->eg_cnt++; - - for (i=0; i<9*2*2; i++) - { - CH = &chip->P_CH[i/2]; - op = &CH->SLOT[i&1]; -#if 1 - /* Envelope Generator */ - switch(op->state) - { - case EG_ATT: /* attack phase */ -// if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) ) - if ( !(chip->eg_cnt & op->eg_m_ar) ) - { - op->volume += (~op->volume * - (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)]) - ) >>3; - - if (op->volume <= MIN_ATT_INDEX) - { - op->volume = MIN_ATT_INDEX; - op->state = EG_DEC; - } - - } - break; - - case EG_DEC: /* decay phase */ -// if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) ) - if ( !(chip->eg_cnt & op->eg_m_dr) ) - { - op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)]; - - if ( op->volume >= op->sl ) - op->state = EG_SUS; - - } - break; - - case EG_SUS: /* sustain phase */ - - /* this is important behaviour: - one can change percusive/non-percussive modes on the fly and - the chip will remain in sustain phase - verified on real YM3812 */ - - if(op->eg_type) /* non-percussive mode */ - { - /* do nothing */ - } - else /* percussive mode */ - { - /* during sustain phase chip adds Release Rate (in percussive mode) */ -// if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) - if ( !(chip->eg_cnt & op->eg_m_rr) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - op->volume = MAX_ATT_INDEX; - } - /* else do nothing in sustain phase */ - } + // 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; break; - case EG_REL: /* release phase */ -// if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) - if ( !(chip->eg_cnt & op->eg_m_rr) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + case 1: // data ports (A1 is ignored) - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } + // force an update + m_stream->update(); - } + // write to FM + m_fm.write(m_address, value); break; - - default: - break; - } -#endif - } - } - - for (i=0; i<9*2*2; i++) - { - CH = &chip->P_CH[i/2]; - op = &CH->SLOT[i&1]; - - /* Phase Generator */ - if(op->vib) - { - uint8_t block; - unsigned int block_fnum = CH->block_fnum; - - unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; - - signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + 16*fnum_lfo ]; - - if (lfo_fn_table_index_offset) /* LFO phase modulation active */ - { - block_fnum += lfo_fn_table_index_offset; - block = (block_fnum&0x1c00) >> 10; - op->Cnt += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; - } - else /* LFO phase modulation = zero */ - { - op->Cnt += op->Incr; - } - } - else /* LFO phase modulation disabled for this operator */ - { - op->Cnt += op->Incr; - } - } - - /* The Noise Generator of the YM3812 is 23-bit shift register. - * Period is equal to 2^23-2 samples. - * Register works at sampling frequency of the chip, so output - * can change on every sample. - * - * Output of the register and input to the bit 22 is: - * bit0 XOR bit14 XOR bit15 XOR bit22 - * - * Simply use bit 22 as the noise output. - */ - - chip->noise_p += chip->noise_f; - i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ - chip->noise_p &= FREQ_MASK; - while (i) - { - /* - uint32_t j; - j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1; - chip->noise_rng = (j<<22) | (chip->noise_rng>>1); - */ - - /* - Instead of doing all the logic operations above, we - use a trick here (and use bit 0 as the noise output). - The difference is only that the noise bit changes one - step ahead. This doesn't matter since we don't know - what is real state of the noise_rng after the reset. - */ - - if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302; - chip->noise_rng >>= 1; - - i--; } } -static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) -{ - uint32_t p; - - p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; - - if (p >= TL_TAB_LEN) - return 0; - return tl_tab[p]; -} +//------------------------------------------------- +// device_start - start of emulation +//------------------------------------------------- -static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +void ymf262_device::device_start() { - uint32_t p; + // create our stream + m_stream = stream_alloc(0, fm_engine::OUTPUTS, m_fm.sample_rate(clock())); - p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm))>>FREQ_SH) & SIN_MASK)]; + // save our data + save_item(YMFM_NAME(m_address)); - if (p >= TL_TAB_LEN) - return 0; - return tl_tab[p]; + // save the engines + m_fm.save(*this); } -#define volume_calc(OP) ((OP)->TLL + ((uint32_t)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask)) +//------------------------------------------------- +// device_reset - start of emulation +//------------------------------------------------- -/* calculate output of a standard 2 operator channel - (or 1st part of a 4-op channel) */ -static inline void chan_calc( OPL3 *chip, OPL3_CH *CH ) +void ymf262_device::device_reset() { - OPL3_SLOT *SLOT; - unsigned int env; - signed int out; - - chip->phase_modulation = 0; - chip->phase_modulation2= 0; - - /* SLOT 1 */ - SLOT = &CH->SLOT[SLOT1]; - env = volume_calc(SLOT); - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - SLOT->op1_out[0] = SLOT->op1_out[1]; - SLOT->op1_out[1] = 0; - if (env < ENV_QUIET) - { - if (!SLOT->FB) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); - } - if (SLOT->connect) { - *SLOT->connect += SLOT->op1_out[1]; - } -//logerror("out0=%5i vol0=%4i ", SLOT->op1_out[1], env ); - - /* SLOT 2 */ - SLOT++; - env = volume_calc(SLOT); - if ((env < ENV_QUIET) && SLOT->connect) - *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); - -//logerror("out1=%5i vol1=%4i\n", op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable), env ); - + // reset the engines + m_fm.reset(); } -/* calculate output of a 2nd part of 4-op channel */ -static inline void chan_calc_ext( OPL3 *chip, OPL3_CH *CH ) -{ - OPL3_SLOT *SLOT; - unsigned int env; - - chip->phase_modulation = 0; - - /* SLOT 1 */ - SLOT = &CH->SLOT[SLOT1]; - env = volume_calc(SLOT); - if (env < ENV_QUIET && SLOT->connect) - *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation2, SLOT->wavetable ); - - /* SLOT 2 */ - SLOT++; - env = volume_calc(SLOT); - if (env < ENV_QUIET && SLOT->connect) - *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); - -} - -/* - operators used in the rhythm sounds generation process: - - Envelope Generator: -channel operator register number Bass High Snare Tom Top -/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal - 6 / 0 12 50 70 90 f0 + - 6 / 1 15 53 73 93 f3 + - 7 / 0 13 51 71 91 f1 + - 7 / 1 16 54 74 94 f4 + - 8 / 0 14 52 72 92 f2 + - 8 / 1 17 55 75 95 f5 + +//------------------------------------------------- +// device_clock_changed - update if clock changes +//------------------------------------------------- - Phase Generator: - -channel operator register number Bass High Snare Tom Top -/ slot number MULTIPLE Drum Hat Drum Tom Cymbal - 6 / 0 12 30 + - 6 / 1 15 33 + - 7 / 0 13 31 + + + - 7 / 1 16 34 ----- n o t u s e d ----- - 8 / 0 14 32 + - 8 / 1 17 35 + + - -channel operator register number Bass High Snare Tom Top -number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal - 6 12,15 B6 A6 + - - 7 13,16 B7 A7 + + + - - 8 14,17 B8 A8 + + + - -*/ - -/* calculate rhythm */ - -static inline void chan_calc_rhythm( OPL3 *chip, OPL3_CH *CH, unsigned int noise ) +void ymf262_device::device_clock_changed() { - OPL3_SLOT *SLOT; - signed int *chanout = chip->chanout; - signed int out; - unsigned int env; - - - /* Bass Drum (verified on real YM3812): - - depends on the channel 6 'connect' register: - when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) - when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored - - output sample always is multiplied by 2 - */ - - chip->phase_modulation = 0; - - /* SLOT 1 */ - SLOT = &CH[6].SLOT[SLOT1]; - env = volume_calc(SLOT); - - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - SLOT->op1_out[0] = SLOT->op1_out[1]; - - if (!SLOT->CON) - chip->phase_modulation = SLOT->op1_out[0]; - //else ignore output of operator 1 - - SLOT->op1_out[1] = 0; - if( env < ENV_QUIET ) - { - if (!SLOT->FB) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); - } - - /* SLOT 2 */ - SLOT++; - env = volume_calc(SLOT); - if( env < ENV_QUIET ) - chanout[6] += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable) * 2; - - - /* Phase generation is based on: */ - // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) - // SD (16) channel 7->slot 1 - // TOM (14) channel 8->slot 1 - // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) - - /* Envelope generation based on: */ - // HH channel 7->slot1 - // SD channel 7->slot2 - // TOM channel 8->slot1 - // TOP channel 8->slot2 - - - /* The following formulas can be well optimized. - I leave them in direct form for now (in case I've missed something). - */ - - /* High Hat (verified on real YM3812) */ - env = volume_calc(SLOT7_1); - if( env < ENV_QUIET ) - { - /* high hat phase generation: - phase = d0 or 234 (based on frequency only) - phase = 34 or 2d0 (based on noise) - */ - - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; - unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; - unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; - - unsigned char res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0xd0; */ - /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ - uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; - - /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; - unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; - - unsigned char res2 = (bit3e ^ bit5e); - - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ - if (res2) - phase = (0x200|(0xd0>>2)); - - - /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ - /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ - if (phase&0x200) - { - if (noise) - phase = 0x200|0xd0; - } - else - /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ - /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ - { - if (noise) - phase = 0xd0>>2; - } - - chanout[7] += op_calc(phase<wavetable) * 2; - } - - /* Snare Drum (verified on real YM3812) */ - env = volume_calc(SLOT7_2); - if( env < ENV_QUIET ) - { - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; - - /* when bit8 = 0 phase = 0x100; */ - /* when bit8 = 1 phase = 0x200; */ - uint32_t phase = bit8 ? 0x200 : 0x100; - - /* Noise bit XOR'es phase by 0x100 */ - /* when noisebit = 0 pass the phase from calculation above */ - /* when noisebit = 1 phase ^= 0x100; */ - /* in other words: phase ^= (noisebit<<8); */ - if (noise) - phase ^= 0x100; - - chanout[7] += op_calc(phase<wavetable) * 2; - } - - /* Tom Tom (verified on real YM3812) */ - env = volume_calc(SLOT8_1); - if( env < ENV_QUIET ) - chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; - - /* Top Cymbal (verified on real YM3812) */ - env = volume_calc(SLOT8_2); - if( env < ENV_QUIET ) - { - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; - unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; - unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; - - unsigned char res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0x100; */ - /* when res1 = 1 phase = 0x200 | 0x100; */ - uint32_t phase = res1 ? 0x300 : 0x100; - - /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; - unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; - - unsigned char res2 = (bit3e ^ bit5e); - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | 0x100; */ - if (res2) - phase = 0x300; - - chanout[8] += op_calc(phase<wavetable) * 2; - } - + m_stream->set_sample_rate(m_fm.sample_rate(clock())); } -/* generic table initialize */ -static int init_tables(void) -{ - signed int i,x; - signed int n; - double o,m; - - - for (x=0; x>= 4; /* 12 bits here */ - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - /* 11 bits here (rounded) */ - n <<= 1; /* 12 bits here (as in real chip) */ - tl_tab[ x*2 + 0 ] = n; - tl_tab[ x*2 + 1 ] = ~tl_tab[ x*2 + 0 ]; /* this *is* different from OPL2 (verified on real YMF262) */ +//------------------------------------------------- +// sound_stream_update - update the sound stream +//------------------------------------------------- - for (i=1; i<13; i++) - { - tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; - tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = ~tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; /* this *is* different from OPL2 (verified on real YMF262) */ - } - #if 0 - logerror("tl %04i", x*2); - for (i=0; i<13; i++) - logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +0 + i*2*TL_RES_LEN ] ); /* positive */ - logerror("\n"); - - logerror("tl %04i", x*2); - for (i=0; i<13; i++) - logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +1 + i*2*TL_RES_LEN ] ); /* negative */ - logerror("\n"); - #endif - } - - for (i=0; i const &inputs, std::vector &outputs) +{ + // iterate over all target samples + for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++) { - /* non-standard sinus */ - m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */ - - /* we never reach zero here due to ((i*2)+1) */ - - if (m>0.0) - o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ - else - o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ - - o = o / (ENV_STEP/4); - - n = (int)(2.0*o); - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); - sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + // update the FM content; clipping is unknown + s32 sums[fm_engine::OUTPUTS] = { 0 }; + m_fm.output(sums, 0, 32767, fm_engine::ALL_CHANNELS); - /*logerror("YMF262.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + // YMF262 outputs straight 16-bit data in 4 channels + for (int index = 0; index < fm_engine::OUTPUTS; index++) + outputs[index].put_int(sampindex, sums[index], 32768); } - - for (i=0; i>1) ]; - - /* waveform 3: _ _ _ _ */ - /* / |_/ |_/ |_/ |_*/ - /* abs(output only first quarter of the sinus waveform) */ - - if (i & (1<<(SIN_BITS-2)) ) - sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; - else - sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; - - /* waveform 4: */ - /* /\ ____/\ ____*/ - /* \/ \/ */ - /* output whole sinus waveform in half the cycle(step=2) and output 0 on the other half of cycle */ - - if (i & (1<<(SIN_BITS-1)) ) - sin_tab[4*SIN_LEN+i] = TL_TAB_LEN; - else - sin_tab[4*SIN_LEN+i] = sin_tab[i*2]; - - /* waveform 5: */ - /* /\/\____/\/\____*/ - /* */ - /* output abs(whole sinus) waveform in half the cycle(step=2) and output 0 on the other half of cycle */ - - if (i & (1<<(SIN_BITS-1)) ) - sin_tab[5*SIN_LEN+i] = TL_TAB_LEN; - else - sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1) ]; - - /* waveform 6: ____ ____ */ - /* */ - /* ____ ____*/ - /* output maximum in half the cycle and output minimum on the other half of cycle */ - - if (i & (1<<(SIN_BITS-1)) ) - sin_tab[6*SIN_LEN+i] = 1; /* negative */ - else - sin_tab[6*SIN_LEN+i] = 0; /* positive */ - - /* waveform 7: */ - /* |\____ |\____ */ - /* \| \|*/ - /* output sawtooth waveform */ - - if (i & (1<<(SIN_BITS-1)) ) - x = ((SIN_LEN-1)-i)*16 + 1; /* negative: from 8177 to 1 */ - else - x = i*16; /*positive: from 0 to 8176 */ - - if (x > TL_TAB_LEN) - x = TL_TAB_LEN; /* clip to the allowed range */ - - sin_tab[7*SIN_LEN+i] = x; - - //logerror("YMF262.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); - //logerror("YMF262.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); - //logerror("YMF262.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] ); - //logerror("YMF262.C: sin4[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[4*SIN_LEN+i], tl_tab[sin_tab[4*SIN_LEN+i]] ); - //logerror("YMF262.C: sin5[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[5*SIN_LEN+i], tl_tab[sin_tab[5*SIN_LEN+i]] ); - //logerror("YMF262.C: sin6[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[6*SIN_LEN+i], tl_tab[sin_tab[6*SIN_LEN+i]] ); - //logerror("YMF262.C: sin7[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[7*SIN_LEN+i], tl_tab[sin_tab[7*SIN_LEN+i]] ); - } - /*logerror("YMF262.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ - -#ifdef SAVE_SAMPLE - sample[0]=fopen("sampsum.pcm","wb"); -#endif - - return 1; -} - -static void OPLCloseTable( void ) -{ -#ifdef SAVE_SAMPLE - fclose(sample[0]); -#endif -} - - - -static void OPL3_initalize(OPL3 *chip) -{ - int i; - - /* frequency base */ - chip->freqbase = (chip->rate) ? ((double)chip->clock / chip->divider) / chip->rate : 0; -#if 0 - chip->rate = (double)chip->clock / chip->divider; - chip->freqbase = 1.0; -#endif - - /* logerror("YMF262: freqbase=%f\n", chip->freqbase); */ - - /* Timer base time */ - chip->TimerBase = chip->clock ? attotime::from_hz(chip->clock) * chip->divider : attotime::zero; - - /* make fnumber -> increment counter table */ - for( i=0 ; i < 1024 ; i++ ) - { - /* opn phase increment counter = 20bit */ - chip->fn_tab[i] = (uint32_t)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ -#if 0 - logerror("YMF262.C: fn_tab[%4i] = %08x (dec=%8i)\n", - i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 ); -#endif - } - -#if 0 - for( i=0 ; i < 16 ; i++ ) - { - logerror("YMF262.C: sl_tab[%i] = %08x\n", - i, sl_tab[i] ); - } - for( i=0 ; i < 8 ; i++ ) - { - int j; - logerror("YMF262.C: ksl_tab[oct=%2i] =",i); - for (j=0; j<16; j++) - { - logerror("%08x ", static_cast(ksl_tab[i*16+j]) ); - } - logerror("\n"); - } -#endif - - - /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ - /* One entry from LFO_AM_TABLE lasts for 64 samples */ - chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; - - /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ - chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; - - /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/ - - /* Noise generator: a step takes 1 sample */ - chip->noise_f = (1.0 / 1.0) * (1<freqbase; - - chip->eg_timer_add = (1<freqbase; - chip->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, chip->eg_timer_overflow);*/ - -} - -static void OPL3_clock_changed(OPL3 *chip, int clock, int rate) -{ - chip->clock = clock; - chip->rate = rate; - - /* init global tables */ - OPL3_initalize(chip); -} - -static inline void FM_KEYON(OPL3_SLOT *SLOT, uint32_t key_set) -{ - if( !SLOT->key ) - { - /* restart Phase Generator */ - SLOT->Cnt = 0; - /* phase -> Attack */ - SLOT->state = EG_ATT; - } - SLOT->key |= key_set; -} - -static inline void FM_KEYOFF(OPL3_SLOT *SLOT, uint32_t key_clr) -{ - if( SLOT->key ) - { - SLOT->key &= key_clr; - - if( !SLOT->key ) - { - /* phase -> Release */ - if (SLOT->state>EG_REL) - SLOT->state = EG_REL; - } - } -} - -/* update phase increment counter of operator (also update the EG rates if necessary) */ -static inline void CALC_FCSLOT(OPL3_CH *CH,OPL3_SLOT *SLOT) -{ - int ksr; - - /* (frequency) phase increment counter */ - SLOT->Incr = CH->fc * SLOT->mul; - ksr = CH->kcode >> SLOT->KSR; - - if( SLOT->ksr != ksr ) - { - SLOT->ksr = ksr; - - /* calculate envelope generator rates */ - if ((SLOT->ar + SLOT->ksr) < 16+60) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_m_ar = (1<eg_sh_ar)-1; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_m_ar = (1<eg_sh_ar)-1; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_m_dr = (1<eg_sh_dr)-1; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_m_rr = (1<eg_sh_rr)-1; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; - } -} - -/* set multi,am,vib,EG-TYP,KSR,mul */ -static inline void set_mul(OPL3 *chip,int slot,int v) -{ - OPL3_CH *CH = &chip->P_CH[slot/2]; - OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->mul = mul_tab[v&0x0f]; - SLOT->KSR = (v&0x10) ? 0 : 2; - SLOT->eg_type = (v&0x20); - SLOT->vib = (v&0x40); - SLOT->AMmask = (v&0x80) ? ~0 : 0; - - if (chip->OPL3_mode & 1) - { - int chan_no = slot/2; - - /* in OPL3 mode */ - //DO THIS: - //if this is one of the slots of 1st channel forming up a 4-op channel - //do normal operation - //else normal 2 operator function - //OR THIS: - //if this is one of the slots of 2nd channel forming up a 4-op channel - //update it using channel data of 1st channel of a pair - //else normal 2 operator function - switch(chan_no) - { - case 0: case 1: case 2: - case 9: case 10: case 11: - if (CH->extended) - { - /* normal */ - CALC_FCSLOT(CH,SLOT); - } - else - { - /* normal */ - CALC_FCSLOT(CH,SLOT); - } - break; - case 3: case 4: case 5: - case 12: case 13: case 14: - if ((CH-3)->extended) - { - /* update this SLOT using frequency data for 1st channel of a pair */ - CALC_FCSLOT(CH-3,SLOT); - } - else - { - /* normal */ - CALC_FCSLOT(CH,SLOT); - } - break; - default: - /* normal */ - CALC_FCSLOT(CH,SLOT); - break; - } - } - else - { - /* in OPL2 mode */ - CALC_FCSLOT(CH,SLOT); - } -} - -/* set ksl & tl */ -static inline void set_ksl_tl(OPL3 *chip,int slot,int v) -{ - OPL3_CH *CH = &chip->P_CH[slot/2]; - OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->ksl = ksl_shift[v >> 6]; - SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ - - if (chip->OPL3_mode & 1) - { - int chan_no = slot/2; - - /* in OPL3 mode */ - //DO THIS: - //if this is one of the slots of 1st channel forming up a 4-op channel - //do normal operation - //else normal 2 operator function - //OR THIS: - //if this is one of the slots of 2nd channel forming up a 4-op channel - //update it using channel data of 1st channel of a pair - //else normal 2 operator function - switch(chan_no) - { - case 0: case 1: case 2: - case 9: case 10: case 11: - if (CH->extended) - { - /* normal */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } - else - { - /* normal */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } - break; - case 3: case 4: case 5: - case 12: case 13: case 14: - if ((CH-3)->extended) - { - /* update this SLOT using frequency data for 1st channel of a pair */ - SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl); - } - else - { - /* normal */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } - break; - default: - /* normal */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - break; - } - } - else - { - /* in OPL2 mode */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } - -} - -/* set attack rate & decay rate */ -static inline void set_ar_dr(OPL3 *chip,int slot,int v) -{ - OPL3_CH *CH = &chip->P_CH[slot/2]; - OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; - - if ((SLOT->ar + SLOT->ksr) < 16+60) /* verified on real YMF262 - all 15 x rates take "zero" time */ - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_m_ar = (1<eg_sh_ar)-1; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_m_ar = (1<eg_sh_ar)-1; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - - SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_m_dr = (1<eg_sh_dr)-1; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; -} - -/* set sustain level & release rate */ -static inline void set_sl_rr(OPL3 *chip,int slot,int v) -{ - OPL3_CH *CH = &chip->P_CH[slot/2]; - OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->sl = sl_tab[ v>>4 ]; - - SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_m_rr = (1<eg_sh_rr)-1; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; -} - - -static void update_channels(OPL3 *chip, OPL3_CH *CH) -{ - /* update channel passed as a parameter and a channel at CH+=3; */ - if (CH->extended) - { /* we've just switched to combined 4 operator mode */ - - } - else - { /* we've just switched to normal 2 operator mode */ - - } - -} - -/* write a value v to register r on OPL chip */ -static void OPL3WriteReg(OPL3 *chip, int r, int v) -{ - OPL3_CH *CH; - unsigned int ch_offset = 0; - int slot; - int block_fnum; - - if(r&0x100) - { - switch(r) - { - case 0x101: /* test register */ - return; - - case 0x104: /* 6 channels enable */ - { - uint8_t prev; - - CH = &chip->P_CH[0]; /* channel 0 */ - prev = CH->extended; - CH->extended = (v>>0) & 1; - if(prev != CH->extended) - update_channels(chip, CH); - CH++; /* channel 1 */ - prev = CH->extended; - CH->extended = (v>>1) & 1; - if(prev != CH->extended) - update_channels(chip, CH); - CH++; /* channel 2 */ - prev = CH->extended; - CH->extended = (v>>2) & 1; - if(prev != CH->extended) - update_channels(chip, CH); - - - CH = &chip->P_CH[9]; /* channel 9 */ - prev = CH->extended; - CH->extended = (v>>3) & 1; - if(prev != CH->extended) - update_channels(chip, CH); - CH++; /* channel 10 */ - prev = CH->extended; - CH->extended = (v>>4) & 1; - if(prev != CH->extended) - update_channels(chip, CH); - CH++; /* channel 11 */ - prev = CH->extended; - CH->extended = (v>>5) & 1; - if(prev != CH->extended) - update_channels(chip, CH); - - } - return; - - case 0x105: /* OPL3 extensions enable register */ - - chip->OPL3_mode = v&0x01; /* OPL3 mode when bit0=1 otherwise it is OPL2 mode */ - - /* following behaviour was tested on real YMF262, - switching OPL3/OPL2 modes on the fly: - - does not change the waveform previously selected (unless when ....) - - does not update CH.A, CH.B, CH.C and CH.D output selectors (registers c0-c8) (unless when ....) - - does not disable channels 9-17 on OPL3->OPL2 switch - - does not switch 4 operator channels back to 2 operator channels - */ - - return; - - default: - if (r < 0x120) - chip->device->logerror("YMF262: write to unknown register (set#2): %03x value=%02x\n",r,v); - break; - } - - ch_offset = 9; /* register page #2 starts from channel 9 (counting from 0) */ - } - - /* adjust bus to 8 bits */ - r &= 0xff; - v &= 0xff; - - - switch(r&0xe0) - { - case 0x00: /* 00-1f:control */ - switch(r&0x1f) - { - case 0x01: /* test register */ - break; - case 0x02: /* Timer 1 */ - chip->T[0] = (256-v)*4; - break; - case 0x03: /* Timer 2 */ - chip->T[1] = (256-v)*16; - break; - case 0x04: /* IRQ clear / mask and Timer enable */ - if(v&0x80) - { /* IRQ flags clear */ - OPL3_STATUS_RESET(chip,0x60); - } - else - { /* set IRQ mask ,timer enable */ - uint8_t st1 = v & 1; - uint8_t st2 = (v>>1) & 1; - - /* IRQRST,T1MSK,t2MSK,x,x,x,ST2,ST1 */ - OPL3_STATUS_RESET(chip, v & 0x60); - OPL3_STATUSMASK_SET(chip, (~v) & 0x60 ); - - /* timer 2 */ - if(chip->st[1] != st2) - { - attotime period = st2 ? chip->TimerBase * chip->T[1] : attotime::zero; - chip->st[1] = st2; - if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,1,period); - } - /* timer 1 */ - if(chip->st[0] != st1) - { - attotime period = st1 ? chip->TimerBase * chip->T[0] : attotime::zero; - chip->st[0] = st1; - if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,0,period); - } - } - break; - case 0x08: /* x,NTS,x,x, x,x,x,x */ - chip->nts = v; - break; - - default: - chip->device->logerror("YMF262: write to unknown register: %02x value=%02x\n",r,v); - break; - } - break; - case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_mul(chip, slot + ch_offset*2, v); - break; - case 0x40: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_ksl_tl(chip, slot + ch_offset*2, v); - break; - case 0x60: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_ar_dr(chip, slot + ch_offset*2, v); - break; - case 0x80: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_sl_rr(chip, slot + ch_offset*2, v); - break; - case 0xa0: - if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ - { - if (ch_offset != 0) /* 0xbd register is present in set #1 only */ - return; - - chip->lfo_am_depth = v & 0x80; - chip->lfo_pm_depth_range = (v&0x40) ? 8 : 0; - - chip->rhythm = v&0x3f; - - if(chip->rhythm&0x20) - { - /* BD key on/off */ - if(v&0x10) - { - FM_KEYON (&chip->P_CH[6].SLOT[SLOT1], 2); - FM_KEYON (&chip->P_CH[6].SLOT[SLOT2], 2); - } - else - { - FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); - FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); - } - /* HH key on/off */ - if(v&0x01) FM_KEYON (&chip->P_CH[7].SLOT[SLOT1], 2); - else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); - /* SD key on/off */ - if(v&0x08) FM_KEYON (&chip->P_CH[7].SLOT[SLOT2], 2); - else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); - /* TOM key on/off */ - if(v&0x04) FM_KEYON (&chip->P_CH[8].SLOT[SLOT1], 2); - else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); - /* TOP-CY key on/off */ - if(v&0x02) FM_KEYON (&chip->P_CH[8].SLOT[SLOT2], 2); - else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); - } - else - { - /* BD key off */ - FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); - FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); - /* HH key off */ - FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); - /* SD key off */ - FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); - /* TOM key off */ - FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); - /* TOP-CY off */ - FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); - } - return; - } - - /* keyon,block,fnum */ - if( (r&0x0f) > 8) return; - CH = &chip->P_CH[(r&0x0f) + ch_offset]; - - if(!(r&0x10)) - { /* a0-a8 */ - block_fnum = (CH->block_fnum&0x1f00) | v; - } - else - { /* b0-b8 */ - block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); - - if (chip->OPL3_mode & 1) - { - int chan_no = (r&0x0f) + ch_offset; - - /* in OPL3 mode */ - //DO THIS: - //if this is 1st channel forming up a 4-op channel - //ALSO keyon/off slots of 2nd channel forming up 4-op channel - //else normal 2 operator function keyon/off - //OR THIS: - //if this is 2nd channel forming up 4-op channel just do nothing - //else normal 2 operator function keyon/off - switch(chan_no) - { - case 0: case 1: case 2: - case 9: case 10: case 11: - if (CH->extended) - { - //if this is 1st channel forming up a 4-op channel - //ALSO keyon/off slots of 2nd channel forming up 4-op channel - if(v&0x20) - { - FM_KEYON (&CH->SLOT[SLOT1], 1); - FM_KEYON (&CH->SLOT[SLOT2], 1); - FM_KEYON (&(CH+3)->SLOT[SLOT1], 1); - FM_KEYON (&(CH+3)->SLOT[SLOT2], 1); - } - else - { - FM_KEYOFF(&CH->SLOT[SLOT1],~1); - FM_KEYOFF(&CH->SLOT[SLOT2],~1); - FM_KEYOFF(&(CH+3)->SLOT[SLOT1],~1); - FM_KEYOFF(&(CH+3)->SLOT[SLOT2],~1); - } - } - else - { - //else normal 2 operator function keyon/off - if(v&0x20) - { - FM_KEYON (&CH->SLOT[SLOT1], 1); - FM_KEYON (&CH->SLOT[SLOT2], 1); - } - else - { - FM_KEYOFF(&CH->SLOT[SLOT1],~1); - FM_KEYOFF(&CH->SLOT[SLOT2],~1); - } - } - break; - - case 3: case 4: case 5: - case 12: case 13: case 14: - if ((CH-3)->extended) - { - //if this is 2nd channel forming up 4-op channel just do nothing - } - else - { - //else normal 2 operator function keyon/off - if(v&0x20) - { - FM_KEYON (&CH->SLOT[SLOT1], 1); - FM_KEYON (&CH->SLOT[SLOT2], 1); - } - else - { - FM_KEYOFF(&CH->SLOT[SLOT1],~1); - FM_KEYOFF(&CH->SLOT[SLOT2],~1); - } - } - break; - - default: - if(v&0x20) - { - FM_KEYON (&CH->SLOT[SLOT1], 1); - FM_KEYON (&CH->SLOT[SLOT2], 1); - } - else - { - FM_KEYOFF(&CH->SLOT[SLOT1],~1); - FM_KEYOFF(&CH->SLOT[SLOT2],~1); - } - break; - } - } - else - { - if(v&0x20) - { - FM_KEYON (&CH->SLOT[SLOT1], 1); - FM_KEYON (&CH->SLOT[SLOT2], 1); - } - else - { - FM_KEYOFF(&CH->SLOT[SLOT1],~1); - FM_KEYOFF(&CH->SLOT[SLOT2],~1); - } - } - } - /* update */ - if(CH->block_fnum != block_fnum) - { - uint8_t block = block_fnum >> 10; - - CH->block_fnum = block_fnum; - - CH->ksl_base = static_cast(ksl_tab[block_fnum>>6]); - CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block); - - /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ - CH->kcode = (CH->block_fnum&0x1c00)>>9; - - /* the info below is actually opposite to what is stated in the Manuals (verifed on real YMF262) */ - /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ - /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ - if (chip->nts&0x40) - CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ - else - CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ - - if (chip->OPL3_mode & 1) - { - int chan_no = (r&0x0f) + ch_offset; - /* in OPL3 mode */ - //DO THIS: - //if this is 1st channel forming up a 4-op channel - //ALSO update slots of 2nd channel forming up 4-op channel - //else normal 2 operator function keyon/off - //OR THIS: - //if this is 2nd channel forming up 4-op channel just do nothing - //else normal 2 operator function keyon/off - switch(chan_no) - { - case 0: case 1: case 2: - case 9: case 10: case 11: - if (CH->extended) - { - //if this is 1st channel forming up a 4-op channel - //ALSO update slots of 2nd channel forming up 4-op channel - - /* refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - (CH+3)->SLOT[SLOT1].TLL = (CH+3)->SLOT[SLOT1].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT1].ksl); - (CH+3)->SLOT[SLOT2].TLL = (CH+3)->SLOT[SLOT2].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT2].ksl); - - /* refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ - CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); - CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); - CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT1]); - CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT2]); - } - else - { - //else normal 2 operator function - /* refresh Total Level in both SLOTs of this channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - - /* refresh frequency counter in both SLOTs of this channel */ - CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); - CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); - } - break; - - case 3: case 4: case 5: - case 12: case 13: case 14: - if ((CH-3)->extended) - { - //if this is 2nd channel forming up 4-op channel just do nothing - } - else - { - //else normal 2 operator function - /* refresh Total Level in both SLOTs of this channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - - /* refresh frequency counter in both SLOTs of this channel */ - CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); - CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); - } - break; - - default: - /* refresh Total Level in both SLOTs of this channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - - /* refresh frequency counter in both SLOTs of this channel */ - CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); - CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); - break; - } - } - else - { - /* in OPL2 mode */ - - /* refresh Total Level in both SLOTs of this channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - - /* refresh frequency counter in both SLOTs of this channel */ - CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); - CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); - } - } - break; - - case 0xc0: - /* CH.D, CH.C, CH.B, CH.A, FB(3bits), C */ - if( (r&0xf) > 8) return; - - CH = &chip->P_CH[(r&0xf) + ch_offset]; - - if( chip->OPL3_mode & 1 ) - { - int base = ((r&0xf) + ch_offset) * 4; - - /* OPL3 mode */ - chip->pan[ base ] = (v & 0x10) ? ~0 : 0; /* ch.A */ - chip->pan[ base +1 ] = (v & 0x20) ? ~0 : 0; /* ch.B */ - chip->pan[ base +2 ] = (v & 0x40) ? ~0 : 0; /* ch.C */ - chip->pan[ base +3 ] = (v & 0x80) ? ~0 : 0; /* ch.D */ - } - else - { - int base = ((r&0xf) + ch_offset) * 4; - - /* OPL2 mode - always enabled */ - chip->pan[ base ] = ~0; /* ch.A */ - chip->pan[ base +1 ] = ~0; /* ch.B */ - chip->pan[ base +2 ] = ~0; /* ch.C */ - chip->pan[ base +3 ] = ~0; /* ch.D */ - } - - chip->pan_ctrl_value[ (r&0xf) + ch_offset ] = v; /* store control value for OPL3/OPL2 mode switching on the fly */ - - CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; - CH->SLOT[SLOT1].CON = v&1; - - if( chip->OPL3_mode & 1 ) - { - int chan_no = (r&0x0f) + ch_offset; - - switch(chan_no) - { - case 0: case 1: case 2: - case 9: case 10: case 11: - if (CH->extended) - { - uint8_t conn = (CH->SLOT[SLOT1].CON<<1) | ((CH+3)->SLOT[SLOT1].CON<<0); - switch(conn) - { - case 0: - /* 1 -> 2 -> 3 -> 4 - out */ - - CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; - (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; - break; - case 1: - /* 1 -> 2 -\ - 3 -> 4 -+- out */ - - CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; - (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; - break; - case 2: - /* 1 -----------\ - 2 -> 3 -> 4 -+- out */ - - CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; - CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; - (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; - break; - case 3: - /* 1 ------\ - 2 -> 3 -+- out - 4 ------/ */ - CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; - CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; - (CH+3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no + 3; - (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; - break; - } - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); - OPL3_SLOT_CONNECT(chip, &(CH+3)->SLOT[SLOT1]); - OPL3_SLOT_CONNECT(chip, &(CH+3)->SLOT[SLOT2]); - } - else - { - /* 2 operators mode */ - CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); - } - break; - - case 3: case 4: case 5: - case 12: case 13: case 14: - if ((CH-3)->extended) - { - uint8_t conn = ((CH-3)->SLOT[SLOT1].CON<<1) | (CH->SLOT[SLOT1].CON<<0); - switch(conn) - { - case 0: - /* 1 -> 2 -> 3 -> 4 - out */ - - (CH-3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; - CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; - break; - case 1: - /* 1 -> 2 -\ - 3 -> 4 -+- out */ - - (CH-3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - (CH-3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no - 3; - CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; - break; - case 2: - /* 1 -----------\ - 2 -> 3 -> 4 -+- out */ - - (CH-3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no - 3; - (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; - CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; - break; - case 3: - /* 1 ------\ - 2 -> 3 -+- out - 4 ------/ */ - (CH-3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no - 3; - (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; - CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; - break; - } - OPL3_SLOT_CONNECT(chip, &(CH-3)->SLOT[SLOT1]); - OPL3_SLOT_CONNECT(chip, &(CH-3)->SLOT[SLOT2]); - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); - } - else - { - /* 2 operators mode */ - CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); - } - break; - - default: - /* 2 operators mode */ - CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); - break; - } - } - else - { - /* OPL2 mode - always 2 operators mode */ - CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; - CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); - OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); - } - break; - - case 0xe0: /* waveform select */ - slot = slot_array[r&0x1f]; - if(slot < 0) return; - - slot += ch_offset*2; - - CH = &chip->P_CH[slot/2]; - - - /* store 3-bit value written regardless of current OPL2 or OPL3 mode... (verified on real YMF262) */ - v &= 7; - CH->SLOT[slot&1].waveform_number = v; - - /* ... but select only waveforms 0-3 in OPL2 mode */ - if( !(chip->OPL3_mode & 1) ) - { - v &= 3; /* we're in OPL2 mode */ - } - CH->SLOT[slot&1].wavetable = v * SIN_LEN; - break; - } -} - -/* lock/unlock for common table */ -static int OPL3_LockTable(device_t *device) -{ - num_lock++; - if(num_lock>1) return 0; - - /* first time */ - - if( !init_tables() ) - { - num_lock--; - return -1; - } - - return 0; -} - -static void OPL3_UnLockTable(void) -{ - if(num_lock) num_lock--; - if(num_lock) return; - - /* last time */ - OPLCloseTable(); -} - -static void OPL3ResetChip(OPL3 *chip) -{ - int c,s; - - chip->eg_timer = 0; - chip->eg_cnt = 0; - - chip->noise_rng = 1; /* noise shift register */ - chip->nts = 0; /* note split */ - OPL3_STATUS_RESET(chip,0x60); - - /* reset with register write */ - OPL3WriteReg(chip,0x01,0); /* test register */ - OPL3WriteReg(chip,0x02,0); /* Timer1 */ - OPL3WriteReg(chip,0x03,0); /* Timer2 */ - OPL3WriteReg(chip,0x04,0); /* IRQ mask clear */ - - -//FIX IT registers 101, 104 and 105 - - -//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) - for(c = 0xff ; c >= 0x20 ; c-- ) - OPL3WriteReg(chip,c,0); -//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) - for(c = 0x1ff ; c >= 0x120 ; c-- ) - OPL3WriteReg(chip,c,0); - - - - /* reset operator parameters */ - for( c = 0 ; c < 9*2 ; c++ ) - { - OPL3_CH *CH = &chip->P_CH[c]; - for(s = 0 ; s < 2 ; s++ ) - { - CH->SLOT[s].state = EG_OFF; - CH->SLOT[s].volume = MAX_ATT_INDEX; - } - } -} - -/* Create one of virtual YMF262 */ -/* 'clock' is chip clock in Hz */ -/* 'rate' is sampling rate */ -static OPL3 *OPL3Create(device_t *device, int clock, int rate, int type, int divider) -{ - OPL3 *chip; - - if (OPL3_LockTable(device) == -1) return nullptr; - - /* allocate memory block */ - chip = auto_alloc_clear(device->machine(), ()); - - chip->device = device; - chip->type = type; - chip->divider = divider; - OPL3_clock_changed(chip, clock, rate); - - /* reset chip */ - OPL3ResetChip(chip); - return chip; -} - -/* Destroy one of virtual YMF262 */ -static void OPL3Destroy(OPL3 *chip) -{ - OPL3_UnLockTable(); - auto_free(chip->device->machine(), chip); -} - - -/* YMF262 I/O interface */ -static int OPL3Write(OPL3 *chip, int a, int v) -{ - /* data bus is 8 bits */ - v &= 0xff; - - - switch(a&3) - { - case 0: /* address port 0 (register set #1) */ - chip->address = v; - break; - - case 1: /* data port - ignore A1 */ - case 3: /* data port - ignore A1 */ - if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0); - OPL3WriteReg(chip,chip->address,v); - break; - - case 2: /* address port 1 (register set #2) */ - - /* verified on real YMF262: - in OPL3 mode: - address line A1 is stored during *address* write and ignored during *data* write. - - in OPL2 mode: - register set#2 writes go to register set#1 (ignoring A1) - verified on registers from set#2: 0x01, 0x04, 0x20-0xef - The only exception is register 0x05. - */ - if( chip->OPL3_mode & 1 ) - { - /* OPL3 mode */ - chip->address = v | 0x100; - } - else - { - /* in OPL2 mode the only accessible in set #2 is register 0x05 */ - if( v==5 ) - chip->address = v | 0x100; - else - chip->address = v; /* verified range: 0x01, 0x04, 0x20-0xef(set #2 becomes set #1 in opl2 mode) */ - } - break; - } - - return chip->status>>7; -} - -static unsigned char OPL3Read(OPL3 *chip,int a) -{ - if( a==0 ) - { - /* status port */ - return chip->status; - } - - return 0x00; /* verified on real YMF262 */ -} - - - -static int OPL3TimerOver(OPL3 *chip,int c) -{ - if( c ) - { /* Timer B */ - OPL3_STATUS_SET(chip,0x20); - } - else - { /* Timer A */ - OPL3_STATUS_SET(chip,0x40); - } - /* reload timer */ - if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,chip->TimerBase * chip->T[c]); - return chip->status>>7; -} - -static void OPL3_save_state(OPL3 *chip, device_t *device) { - for (int ch=0; ch<18; ch++) { - OPL3_CH *channel = &chip->P_CH[ch]; - device->save_item(NAME(channel->block_fnum), ch); - device->save_item(NAME(channel->fc), ch); - device->save_item(NAME(channel->ksl_base), ch); - device->save_item(NAME(channel->kcode), ch); - device->save_item(NAME(channel->extended), ch); - - for (int sl=0; sl<2; sl++) { - OPL3_SLOT *slot = &channel->SLOT[sl]; - device->save_item(NAME(slot->ar), ch*2+sl); - device->save_item(NAME(slot->dr), ch*2+sl); - device->save_item(NAME(slot->rr), ch*2+sl); - device->save_item(NAME(slot->KSR), ch*2+sl); - device->save_item(NAME(slot->ksl), ch*2+sl); - device->save_item(NAME(slot->ksr), ch*2+sl); - device->save_item(NAME(slot->mul), ch*2+sl); - - device->save_item(NAME(slot->Cnt), ch*2+sl); - device->save_item(NAME(slot->Incr), ch*2+sl); - device->save_item(NAME(slot->FB), ch*2+sl); - device->save_item(NAME(slot->conn_enum), ch*2+sl); - device->save_item(NAME(slot->op1_out), ch*2+sl); - device->save_item(NAME(slot->CON), ch*2+sl); - - device->save_item(NAME(slot->eg_type), ch*2+sl); - device->save_item(NAME(slot->state), ch*2+sl); - device->save_item(NAME(slot->TL), ch*2+sl); - device->save_item(NAME(slot->TLL), ch*2+sl); - device->save_item(NAME(slot->volume), ch*2+sl); - device->save_item(NAME(slot->sl), ch*2+sl); - - device->save_item(NAME(slot->eg_m_ar), ch*2+sl); - device->save_item(NAME(slot->eg_sh_ar), ch*2+sl); - device->save_item(NAME(slot->eg_sel_ar), ch*2+sl); - device->save_item(NAME(slot->eg_m_dr), ch*2+sl); - device->save_item(NAME(slot->eg_sh_dr), ch*2+sl); - device->save_item(NAME(slot->eg_sel_dr), ch*2+sl); - device->save_item(NAME(slot->eg_m_rr), ch*2+sl); - device->save_item(NAME(slot->eg_sh_rr), ch*2+sl); - device->save_item(NAME(slot->eg_sel_rr), ch*2+sl); - - device->save_item(NAME(slot->key), ch*2+sl); - - device->save_item(NAME(slot->AMmask), ch*2+sl); - device->save_item(NAME(slot->vib), ch*2+sl); - - device->save_item(NAME(slot->waveform_number), ch*2+sl); - device->save_item(NAME(slot->wavetable), ch*2+sl); - } - } - - device->save_item(NAME(chip->pan)); - device->save_item(NAME(chip->pan_ctrl_value)); - - device->save_item(NAME(chip->lfo_am_depth)); - device->save_item(NAME(chip->lfo_pm_depth_range)); - - device->save_item(NAME(chip->OPL3_mode)); - device->save_item(NAME(chip->rhythm)); - - device->save_item(NAME(chip->T)); - device->save_item(NAME(chip->st)); - - device->save_item(NAME(chip->address)); - device->save_item(NAME(chip->status)); - device->save_item(NAME(chip->statusmask)); - - device->save_item(NAME(chip->nts)); -} - -void * ymf262_init(device_t *device, int clock, int rate) -{ - void *chip = OPL3Create(device,clock,rate,OPL3_TYPE_YMF262,8*36); - OPL3_save_state((OPL3 *)chip, device); - - return chip; -} - -void * ymf278b_init(device_t *device, int clock, int rate) -{ - void *chip = OPL3Create(device,clock,rate,OPL3_TYPE_YMF262,19*36); - OPL3_save_state((OPL3 *)chip, device); - - return chip; -} - -void ymf262_clock_changed(void *chip, int clock, int rate) -{ - OPL3_clock_changed((OPL3 *)chip, clock, rate); -} - -void ymf262_post_load(void *chip) { - OPL3 *opl3 = (OPL3 *)chip; - for (int ch=0; ch<18; ch++) { - for (int sl=0; sl<2; sl++) { - OPL3_SLOT_CONNECT(opl3, &(opl3->P_CH[ch].SLOT[sl])); - } - } -} - -void ymf262_shutdown(void *chip) -{ - OPL3Destroy((OPL3 *)chip); -} -void ymf262_reset_chip(void *chip) -{ - OPL3ResetChip((OPL3 *)chip); -} - -int ymf262_write(void *chip, int a, int v) -{ - return OPL3Write((OPL3 *)chip, a, v); -} - -unsigned char ymf262_read(void *chip, int a) -{ - /* Note on status register: */ - - /* YM3526(OPL) and YM3812(OPL2) return bit2 and bit1 in HIGH state */ - - /* YMF262(OPL3) always returns bit2 and bit1 in LOW state */ - /* which can be used to identify the chip */ - - /* YMF278(OPL4) returns bit2 in LOW and bit1 in HIGH state ??? info from manual - not verified */ - - return OPL3Read((OPL3 *)chip, a); -} -int ymf262_timer_over(void *chip, int c) -{ - return OPL3TimerOver((OPL3 *)chip, c); -} - -void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER timer_handler, device_t *device) -{ - reinterpret_cast(chip)->SetTimerHandler(timer_handler, device); -} -void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device) -{ - reinterpret_cast(chip)->SetIRQHandler(IRQHandler, device); -} -void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device) -{ - reinterpret_cast(chip)->SetUpdateHandler(UpdateHandler, device); -} - - -/* -** Generate samples for one of the YMF262's -** -** 'which' is the virtual YMF262 number -** '**buffers' is table of 4 pointers to the buffers: CH.A, CH.B, CH.C and CH.D -** 'length' is the number of samples that should be generated -*/ -void ymf262_update_one(void *_chip, std::vector &buffers) -{ - int i; - OPL3 *chip = (OPL3 *)_chip; - signed int *chanout = chip->chanout; - uint8_t rhythm = chip->rhythm&0x20; - - auto &ch_a = buffers[0]; // DO2 (mixed) left output for OPL4 - auto &ch_b = buffers[1]; // DO2 (mixed) right output for OPL4 - auto &ch_c = buffers[2]; // DO0 (FM only) left output for OPL4 - auto &ch_d = buffers[3]; // DO0 (FM only) right output for OPL4 - - for( i=0; i < ch_a.samples() ; i++ ) - { - int a,b,c,d; - - advance_lfo(chip); - - /* clear channel outputs */ - memset(chip->chanout, 0, sizeof(chip->chanout)); - -#if 1 - /* register set #1 */ - chan_calc(chip, &chip->P_CH[0]); /* extended 4op ch#0 part 1 or 2op ch#0 */ - if (chip->P_CH[0].extended) - chan_calc_ext(chip, &chip->P_CH[3]); /* extended 4op ch#0 part 2 */ - else - chan_calc(chip, &chip->P_CH[3]); /* standard 2op ch#3 */ - - - chan_calc(chip, &chip->P_CH[1]); /* extended 4op ch#1 part 1 or 2op ch#1 */ - if (chip->P_CH[1].extended) - chan_calc_ext(chip, &chip->P_CH[4]); /* extended 4op ch#1 part 2 */ - else - chan_calc(chip, &chip->P_CH[4]); /* standard 2op ch#4 */ - - - chan_calc(chip, &chip->P_CH[2]); /* extended 4op ch#2 part 1 or 2op ch#2 */ - if (chip->P_CH[2].extended) - chan_calc_ext(chip, &chip->P_CH[5]); /* extended 4op ch#2 part 2 */ - else - chan_calc(chip, &chip->P_CH[5]); /* standard 2op ch#5 */ - - - if(!rhythm) - { - chan_calc(chip, &chip->P_CH[6]); - chan_calc(chip, &chip->P_CH[7]); - chan_calc(chip, &chip->P_CH[8]); - } - else /* Rhythm part */ - { - chan_calc_rhythm(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 ); - } - - /* register set #2 */ - chan_calc(chip, &chip->P_CH[ 9]); - if (chip->P_CH[9].extended) - chan_calc_ext(chip, &chip->P_CH[12]); - else - chan_calc(chip, &chip->P_CH[12]); - - - chan_calc(chip, &chip->P_CH[10]); - if (chip->P_CH[10].extended) - chan_calc_ext(chip, &chip->P_CH[13]); - else - chan_calc(chip, &chip->P_CH[13]); - - - chan_calc(chip, &chip->P_CH[11]); - if (chip->P_CH[11].extended) - chan_calc_ext(chip, &chip->P_CH[14]); - else - chan_calc(chip, &chip->P_CH[14]); - - - /* channels 15,16,17 are fixed 2-operator channels only */ - chan_calc(chip, &chip->P_CH[15]); - chan_calc(chip, &chip->P_CH[16]); - chan_calc(chip, &chip->P_CH[17]); -#endif - - /* accumulator register set #1 */ - a = chanout[0] & chip->pan[0]; - b = chanout[0] & chip->pan[1]; - c = chanout[0] & chip->pan[2]; - d = chanout[0] & chip->pan[3]; -#if 1 - a += chanout[1] & chip->pan[4]; - b += chanout[1] & chip->pan[5]; - c += chanout[1] & chip->pan[6]; - d += chanout[1] & chip->pan[7]; - a += chanout[2] & chip->pan[8]; - b += chanout[2] & chip->pan[9]; - c += chanout[2] & chip->pan[10]; - d += chanout[2] & chip->pan[11]; - - a += chanout[3] & chip->pan[12]; - b += chanout[3] & chip->pan[13]; - c += chanout[3] & chip->pan[14]; - d += chanout[3] & chip->pan[15]; - a += chanout[4] & chip->pan[16]; - b += chanout[4] & chip->pan[17]; - c += chanout[4] & chip->pan[18]; - d += chanout[4] & chip->pan[19]; - a += chanout[5] & chip->pan[20]; - b += chanout[5] & chip->pan[21]; - c += chanout[5] & chip->pan[22]; - d += chanout[5] & chip->pan[23]; - - a += chanout[6] & chip->pan[24]; - b += chanout[6] & chip->pan[25]; - c += chanout[6] & chip->pan[26]; - d += chanout[6] & chip->pan[27]; - a += chanout[7] & chip->pan[28]; - b += chanout[7] & chip->pan[29]; - c += chanout[7] & chip->pan[30]; - d += chanout[7] & chip->pan[31]; - a += chanout[8] & chip->pan[32]; - b += chanout[8] & chip->pan[33]; - c += chanout[8] & chip->pan[34]; - d += chanout[8] & chip->pan[35]; - - /* accumulator register set #2 */ - a += chanout[9] & chip->pan[36]; - b += chanout[9] & chip->pan[37]; - c += chanout[9] & chip->pan[38]; - d += chanout[9] & chip->pan[39]; - a += chanout[10] & chip->pan[40]; - b += chanout[10] & chip->pan[41]; - c += chanout[10] & chip->pan[42]; - d += chanout[10] & chip->pan[43]; - a += chanout[11] & chip->pan[44]; - b += chanout[11] & chip->pan[45]; - c += chanout[11] & chip->pan[46]; - d += chanout[11] & chip->pan[47]; - - a += chanout[12] & chip->pan[48]; - b += chanout[12] & chip->pan[49]; - c += chanout[12] & chip->pan[50]; - d += chanout[12] & chip->pan[51]; - a += chanout[13] & chip->pan[52]; - b += chanout[13] & chip->pan[53]; - c += chanout[13] & chip->pan[54]; - d += chanout[13] & chip->pan[55]; - a += chanout[14] & chip->pan[56]; - b += chanout[14] & chip->pan[57]; - c += chanout[14] & chip->pan[58]; - d += chanout[14] & chip->pan[59]; - - a += chanout[15] & chip->pan[60]; - b += chanout[15] & chip->pan[61]; - c += chanout[15] & chip->pan[62]; - d += chanout[15] & chip->pan[63]; - a += chanout[16] & chip->pan[64]; - b += chanout[16] & chip->pan[65]; - c += chanout[16] & chip->pan[66]; - d += chanout[16] & chip->pan[67]; - a += chanout[17] & chip->pan[68]; - b += chanout[17] & chip->pan[69]; - c += chanout[17] & chip->pan[70]; - d += chanout[17] & chip->pan[71]; -#endif - - #ifdef SAVE_SAMPLE - if (which==0) - { - SAVE_ALL_CHANNELS - } - #endif - - /* store to sound buffer */ - ch_a.put_int_clamp(i, a, 32768 << FINAL_SH); - ch_b.put_int_clamp(i, a, 32768 << FINAL_SH); - ch_c.put_int_clamp(i, a, 32768 << FINAL_SH); - ch_d.put_int_clamp(i, a, 32768 << FINAL_SH); - - advance(chip); - } - } diff --git a/src/devices/sound/ymf262.h b/src/devices/sound/ymf262.h index fe131169fcf..78086532e7c 100644 --- a/src/devices/sound/ymf262.h +++ b/src/devices/sound/ymf262.h @@ -1,43 +1,48 @@ -// license:GPL-2.0+ -// copyright-holders:Jarek Burczynski +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + #ifndef MAME_SOUND_YMF262_H #define MAME_SOUND_YMF262_H #pragma once -/* select number of output bits: 8 or 16 */ -#define OPL3_SAMPLE_BITS 16 - -typedef s32 OPL3SAMPLE; -/* -#if (OPL3_SAMPLE_BITS==16) -typedef int16_t OPL3SAMPLE; -#endif -#if (OPL3_SAMPLE_BITS==8) -typedef int8_t OPL3SAMPLE; -#endif -*/ - -typedef void (*OPL3_TIMERHANDLER)(device_t *device,int timer,const attotime &period); -typedef void (*OPL3_IRQHANDLER)(device_t *device,int irq); -typedef void (*OPL3_UPDATEHANDLER)(device_t *device,int min_interval_us); - - -void *ymf262_init(device_t *device, int clock, int rate); -void *ymf278b_init(device_t *device, int clock, int rate); - -void ymf262_clock_changed(void *chip, int clock, int rate); -void ymf262_post_load(void *chip); -void ymf262_shutdown(void *chip); -void ymf262_reset_chip(void *chip); -int ymf262_write(void *chip, int a, int v); -unsigned char ymf262_read(void *chip, int a); -int ymf262_timer_over(void *chip, int c); -void ymf262_update_one(void *chip, std::vector &buffers); - -void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER TimerHandler, device_t *device); -void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device); -void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device); +#include "ymfm.h" + + +// ======================> ymf262_device + +DECLARE_DEVICE_TYPE(YMF262, ymf262_device); + +class ymf262_device : public device_t, public device_sound_interface +{ +public: + // YMF262 is OPL3 + using fm_engine = ymopl3_engine; + + // constructor + ymf262_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YMF262); + + // configuration helpers + auto irq_handler() { return m_fm.irq_handler(); } + + // read/write access + u8 read(offs_t offset); + void write(offs_t offset, u8 data); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_clock_changed() override; + + // sound overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + + // internal state + u16 m_address; // address register + sound_stream *m_stream; // sound stream + fm_engine m_fm; // core FM engine +}; #endif // MAME_SOUND_YMF262_H diff --git a/src/devices/sound/ymf278b.cpp b/src/devices/sound/ymf278b.cpp index 042b8951022..9c27849ee1e 100644 --- a/src/devices/sound/ymf278b.cpp +++ b/src/devices/sound/ymf278b.cpp @@ -50,7 +50,6 @@ #include "emu.h" #include "ymf278b.h" -#include "ymf262.h" #include @@ -58,6 +57,18 @@ #define LOG(x) do { if (VERBOSE) logerror x; } while (0) +// 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. The calculations below produce the fractional +// number of extra FM samples we need to consume for each output sample, +// as a 0.24 fixed point fraction. +static constexpr double NOMINAL_CLOCK = 33868800; +static constexpr double NOMINAL_FM_RATE = NOMINAL_CLOCK / double(ymopl4_registers::DEFAULT_PRESCALE * ymopl4_registers::OPERATORS); +static constexpr double NOMINAL_OUTPUT_RATE = NOMINAL_CLOCK / 768.0; +static constexpr uint32_t FM_STEP = uint32_t((NOMINAL_FM_RATE / NOMINAL_OUTPUT_RATE - 1.0) * double(1 << 24)); + + /**************************************************************************/ int ymf278b_device::compute_rate(YMF278BSlot *slot, int val) @@ -221,17 +232,6 @@ void ymf278b_device::sound_stream_update(sound_stream &stream, std::vector> 16, 32768); - outputs[1].add_int(i, (*mixp++ * vr) >> 16, 32768); + // the FM_STEP value is the fractional number of extra samples consumed per + // output sample; when this overflows, we need to clock the FM engine an + // extra time; since the PCM side of the chip doesn't do interpolation, I'm + // assuming this resampling stage doesn't either + m_fm_pos += FM_STEP; + if (BIT(m_fm_pos, 24)) + { + m_fm.clock(fm_engine::ALL_CHANNELS); + m_fm_pos &= 0xffffff; + } + + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; clipping is unknown + s32 sums[fm_engine::OUTPUTS] = { 0 }; + m_fm.output(sums, 1, 32767, fm_engine::ALL_CHANNELS); + + // DO2 output: mixed FM channels 0+1 and wavetable channels 0+1 + outputs[0].put(i, stream_buffer::sample_t(*mixp++) * wtl + stream_buffer::sample_t(sums[0]) * fml); + outputs[1].put(i, stream_buffer::sample_t(*mixp++) * wtr + stream_buffer::sample_t(sums[1]) * fmr); + + // DO0 output: FM channels 2+3 only + outputs[2].put_int(i, sums[2], 32768); + outputs[3].put_int(i, sums[3], 32768); + + // DO1 output: wavetable channels 2+3 only outputs[4].put_int(i, *mixp++, 32768); outputs[5].put_int(i, *mixp++, 32768); } } -void ymf278b_device::irq_check() -{ - int prev_line = m_irq_line; - m_irq_line = m_current_irq ? 1 : 0; - if (m_irq_line != prev_line && !m_irq_handler.isnull()) - m_irq_handler(m_irq_line); -} - enum { - TIMER_A = 0, - TIMER_B, TIMER_BUSY_CLEAR, TIMER_LD_CLEAR }; @@ -345,28 +362,12 @@ void ymf278b_device::device_timer(emu_timer &timer, device_timer_id id, int para { switch(id) { - case TIMER_A: - if(!(m_enable & 0x40)) - { - m_current_irq |= 0x40; - irq_check(); - } - break; - - case TIMER_B: - if(!(m_enable & 0x20)) - { - m_current_irq |= 0x20; - irq_check(); - } - break; - case TIMER_BUSY_CLEAR: - m_status_busy = 0; + m_fm.set_reset_status(0, STATUS_BUSY); break; case TIMER_LD_CLEAR: - m_status_ld = 0; + m_fm.set_reset_status(0, STATUS_LD); break; } } @@ -374,91 +375,6 @@ void ymf278b_device::device_timer(emu_timer &timer, device_timer_id id, int para /**************************************************************************/ -void ymf278b_device::A_w(uint8_t reg, uint8_t data) -{ - // FM register array 0 (compatible with YMF262) - switch(reg) - { - // LSI TEST - case 0x00: - case 0x01: - break; - - // timer a count - case 0x02: - if (data != m_timer_a_count) - { - m_timer_a_count = data; - - // change period, ~80.8us * t - if (m_enable & 1) - m_timer_a->adjust(m_timer_a->remaining(), 0, m_timer_base * (256-data) * 4); - } - break; - - // timer b count - case 0x03: - if (data != m_timer_b_count) - { - m_timer_b_count = data; - - // change period, ~323.1us * t - if (m_enable & 2) - m_timer_b->adjust(m_timer_b->remaining(), 0, m_timer_base * (256-data) * 16); - } - break; - - // timer control - case 0x04: - if(data & 0x80) - m_current_irq = 0; - else - { - // reset timers - if((m_enable ^ data) & 1) - { - attotime period = (data & 1) ? m_timer_base * (256-m_timer_a_count) * 4 : attotime::never; - m_timer_a->adjust(period, 0, period); - } - if((m_enable ^ data) & 2) - { - attotime period = (data & 2) ? m_timer_base * (256-m_timer_b_count) * 16 : attotime::never; - m_timer_b->adjust(period, 0, period); - } - - m_enable = data; - m_current_irq &= ~data; - } - irq_check(); - break; - - default: - logerror("YMF278B: Port A write %02x, %02x\n", reg, data); - break; - } -} - -void ymf278b_device::B_w(uint8_t reg, uint8_t data) -{ - // FM register array 1 (compatible with YMF262) - switch(reg) - { - // LSI TEST - case 0x00: - case 0x01: - break; - - // expansion register (NEW2/NEW) - case 0x05: - m_exp = data; - break; - - default: - logerror("YMF278B: Port B write %02x, %02x\n", reg, data); - break; - } -} - void ymf278b_device::retrigger_sample(YMF278BSlot *slot) { // activate channel @@ -515,7 +431,7 @@ void ymf278b_device::C_w(uint8_t reg, uint8_t data) C_w(8 + snum + (i-2) * 24, p[i]); // status register LD bit is on for approx 300us - m_status_ld = 1; + m_fm.set_reset_status(STATUS_LD, 0); period = clocks_to_attotime(10); m_timer_ld->adjust(period); @@ -689,29 +605,32 @@ void ymf278b_device::C_w(uint8_t reg, uint8_t data) void ymf278b_device::timer_busy_start(int is_pcm) { // status register BUSY bit is on for 56(FM) or 88(PCM) cycles - m_status_busy = 1; + m_fm.set_reset_status(STATUS_BUSY, 0); m_timer_busy->adjust(attotime::from_hz(m_clock / (is_pcm ? 88 : 56))); } void ymf278b_device::write(offs_t offset, u8 data) { - switch (offset) + uint32_t old; + switch (offset & 7) { case 0: case 2: timer_busy_start(0); m_port_AB = data; - m_lastport = offset>>1 & 1; - ymf262_write(m_ymf262, offset, data); + m_lastport = BIT(offset, 1); break; case 1: case 3: timer_busy_start(0); - if (m_lastport) B_w(m_port_AB, data); - else A_w(m_port_AB, data); - m_last_fm_data = data; - ymf262_write(m_ymf262, offset, data); + old = m_fm.regs().new2flag(); + m_fm.write(m_port_AB | (m_lastport << 8), data); + + // if the new2 flag is turned on, the next status read will set bit 1 + // but only for the first status read after new2 is set + if (old == 0 && m_fm.regs().new2flag() != 0) + m_next_status_id = true; break; case 4: @@ -721,7 +640,7 @@ void ymf278b_device::write(offs_t offset, u8 data) case 5: // PCM regs are only accessible if NEW2 is set - if (~m_exp & 2) + if (!m_fm.regs().new2flag()) break; m_stream->update(); @@ -741,32 +660,42 @@ u8 ymf278b_device::read(offs_t offset) { uint8_t ret = 0; - switch (offset) + switch (offset & 7) { // status register case 0: - { - // bits 0 and 1 are only valid if NEW2 is set - uint8_t newbits = 0; - if (m_exp & 2) - newbits = (m_status_ld << 1) | m_status_busy; + ret = m_fm.status(); - ret = newbits | m_current_irq | (m_irq_line ? 0x80 : 0x00); + // if new2 flag is not set, we're in OPL2 or OPL3 mode + if (!m_fm.regs().new2flag()) + { + // these bits are not reported in OPL2/3 mode + ret &= ~(STATUS_BUSY | STATUS_LD); + + // if in OPL2 mode, bits 1 and 2 are returned on + if (!m_fm.regs().newflag()) + ret |= 0x06; + } + else if (m_next_status_id) + { + // if new2 flag was just changed to on, then the next read will be 0x02 + ret |= 0x02; + m_next_status_id = false; + } break; - } // FM regs can be read too (on contrary to what the datasheet says) case 1: case 3: // but they're not implemented here yet // This may be incorrect, but it makes the mbwave moonsound detection in msx drivers pass. - ret = m_last_fm_data; + ret = m_fm.regs().read(m_port_AB | (m_lastport << 8)); break; // PCM regs case 5: // only accessible if NEW2 is set - if (~m_exp & 2) + if (!m_fm.regs().new2flag()) break; switch (m_port_C) @@ -797,15 +726,6 @@ u8 ymf278b_device::read(offs_t offset) /**************************************************************************/ -//------------------------------------------------- -// device_post_load - device-specific post load -//------------------------------------------------- - -void ymf278b_device::device_post_load() -{ - ymf262_post_load(m_ymf262); -} - //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- @@ -815,9 +735,6 @@ void ymf278b_device::device_reset() int i; // clear registers - for (i = 0; i <= 4; i++) - A_w(i, 0); - B_w(5, 0); for (i = 0; i < 8; i++) C_w(i, 0); for (i = 0xff; i >= 8; i--) @@ -826,6 +743,7 @@ void ymf278b_device::device_reset() m_port_AB = m_port_C = 0; m_lastport = 0; + m_next_status_id = false; m_memadr = 0; // init/silence channels @@ -851,23 +769,10 @@ void ymf278b_device::device_reset() compute_envelope(slot); } - m_timer_a->reset(); - m_timer_b->reset(); - m_timer_busy->reset(); m_status_busy = 0; - m_timer_ld->reset(); m_status_ld = 0; - - m_irq_line = 0; - m_current_irq = 0; - if (!m_irq_handler.isnull()) - m_irq_handler(0); - - ymf262_reset_chip(m_ymf262); -} + m_timer_busy->reset(); + m_timer_ld->reset(); -void ymf278b_device::device_stop() -{ - ymf262_shutdown(m_ymf262); - m_ymf262 = nullptr; + m_fm.reset(); } void ymf278b_device::device_clock_changed() @@ -875,18 +780,13 @@ void ymf278b_device::device_clock_changed() int old_rate = m_rate; m_clock = clock(); m_rate = m_clock/768; + m_fm_pos = 0; if (m_rate > old_rate) { m_mix_buffer.resize(m_rate*4,0); } m_stream->set_sample_rate(m_rate); - - m_timer_base = m_clock ? attotime::from_hz(m_clock) * (19 * 36) : attotime::zero; - - // YMF262 related - - ymf262_clock_changed(m_ymf262, clock(), m_rate); } void ymf278b_device::rom_bank_updated() @@ -929,22 +829,15 @@ void ymf278b_device::register_save_state() save_item(NAME(m_wavetblhdr)); save_item(NAME(m_memmode)); save_item(NAME(m_memadr)); - save_item(NAME(m_status_busy)); - save_item(NAME(m_status_ld)); - save_item(NAME(m_exp)); save_item(NAME(m_fm_l)); save_item(NAME(m_fm_r)); + save_item(NAME(m_fm_pos)); save_item(NAME(m_pcm_l)); save_item(NAME(m_pcm_r)); - save_item(NAME(m_timer_a_count)); - save_item(NAME(m_timer_b_count)); - save_item(NAME(m_enable)); - save_item(NAME(m_current_irq)); - save_item(NAME(m_irq_line)); save_item(NAME(m_port_AB)); save_item(NAME(m_port_C)); save_item(NAME(m_lastport)); - save_item(NAME(m_last_fm_data)); + save_item(NAME(m_next_status_id)); for (i = 0; i < 24; ++i) { @@ -996,11 +889,8 @@ void ymf278b_device::device_start() m_clock = clock(); m_rate = m_clock / 768; - m_irq_handler.resolve(); + m_fm_pos = 0; - m_timer_base = m_clock ? attotime::from_hz(m_clock) * (19*36) : attotime::zero; - m_timer_a = timer_alloc(TIMER_A); - m_timer_b = timer_alloc(TIMER_B); m_timer_busy = timer_alloc(TIMER_BUSY_CLEAR); m_timer_ld = timer_alloc(TIMER_LD_CLEAR); @@ -1037,16 +927,7 @@ void ymf278b_device::device_start() register_save_state(); // YMF262 related - - /* stream system initialize */ - m_ymf262 = ymf278b_init(this, clock(), m_rate); - if (!m_ymf262) - throw emu_fatalerror("ymf278b_device(%s): Error creating YMF262 chip", tag()); - - /* YMF262 setup */ - ymf262_set_timer_handler (m_ymf262, ymf278b_device::static_timer_handler, this); - ymf262_set_irq_handler (m_ymf262, ymf278b_device::static_irq_handler, this); - ymf262_set_update_handler(m_ymf262, ymf278b_device::static_update_request, this); + m_fm.save(*this); } @@ -1056,7 +937,6 @@ ymf278b_device::ymf278b_device(const machine_config &mconfig, const char *tag, d : device_t(mconfig, YMF278B, tag, owner, clock) , device_sound_interface(mconfig, *this) , device_rom_interface(mconfig, *this) - , m_irq_handler(*this) - , m_last_fm_data(0) + , m_fm(*this) { } diff --git a/src/devices/sound/ymf278b.h b/src/devices/sound/ymf278b.h index 2d20ad253bf..b98b35bfe49 100644 --- a/src/devices/sound/ymf278b.h +++ b/src/devices/sound/ymf278b.h @@ -6,24 +6,31 @@ #pragma once #include "dirom.h" +#include "sound/ymfm.h" class ymf278b_device : public device_t, public device_sound_interface, public device_rom_interface<22> { public: + static constexpr u8 STATUS_BUSY = 0x01; + static constexpr u8 STATUS_LD = 0x02; + + // YMF278B is OPL4 + using fm_engine = ymopl4_engine; + + // constructor ymf278b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); // configuration helpers - auto irq_handler() { return m_irq_handler.bind(); } + auto irq_handler() { return m_fm.irq_handler(); } + // read/write access u8 read(offs_t offset); void write(offs_t offset, u8 data); protected: // device-level overrides - virtual void device_post_load() override; virtual void device_start() override; virtual void device_reset() override; - virtual void device_stop() override; virtual void device_clock_changed() override; virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; @@ -81,20 +88,12 @@ private: void compute_freq_step(YMF278BSlot *slot); void compute_envelope(YMF278BSlot *slot); void irq_check(); - void A_w(uint8_t reg, uint8_t data); - void B_w(uint8_t reg, uint8_t data); void retrigger_sample(YMF278BSlot *slot); void C_w(uint8_t reg, uint8_t data); void timer_busy_start(int is_pcm); void precompute_rate_tables(); void register_save_state(); - void update_request() { m_stream->update(); } - - static void static_irq_handler(device_t *param, int irq) { } - static void static_timer_handler(device_t *param, int c, const attotime &period) { } - static void static_update_request(device_t *param, int interval) { downcast(param)->update_request(); } - // internal state uint8_t m_pcmregs[256]; YMF278BSlot m_slots[24]; @@ -102,20 +101,16 @@ private: int8_t m_memmode; int32_t m_memadr; - uint8_t m_status_busy, m_status_ld; emu_timer *m_timer_busy; emu_timer *m_timer_ld; - uint8_t m_exp; int32_t m_fm_l, m_fm_r; int32_t m_pcm_l, m_pcm_r; - attotime m_timer_base; - uint8_t m_timer_a_count, m_timer_b_count; - uint8_t m_enable, m_current_irq; - int m_irq_line; + uint32_t m_fm_pos; uint8_t m_port_C, m_port_AB, m_lastport; + bool m_next_status_id; // precomputed tables uint32_t m_lut_ar[64]; // attack rate @@ -124,17 +119,14 @@ private: int m_pan_left[16],m_pan_right[16]; // pan volume offsets int32_t m_mix_level[8]; - emu_timer *m_timer_a, *m_timer_b; int m_clock; int m_rate; sound_stream * m_stream; std::vector m_mix_buffer; - devcb_write_line m_irq_handler; - uint8_t m_last_fm_data; // ymf262 - void *m_ymf262; + fm_engine m_fm; }; DECLARE_DEVICE_TYPE(YMF278B, ymf278b_device) diff --git a/src/devices/sound/ymfm.cpp b/src/devices/sound/ymfm.cpp index 52250dfefc5..4fa7480683d 100644 --- a/src/devices/sound/ymfm.cpp +++ b/src/devices/sound/ymfm.cpp @@ -9,95 +9,322 @@ #include "logmacro.h" // -// This emulator is written from the ground-up based on analysis and deduction -// by Nemesis, particularly in this thread: +// ONE FM CORE TO RULE THEM ALL +// +// This emulator is 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 OPN variants unless +// 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, as well as Alexey Khokholov's -// "Nuked" implementation based off die shots. +// "Nuked" implementations based off die shots. // // Operator and channel summing/mixing code is largely based off of research // done by David Viens and Hubert Lamontagne. // // Search for QUESTION to find areas where I am unsure. // -// =================================================================================== // -// OPN pedigree: -// -// +--------++-----------------++------------------++--------------------------+ -// broad catgeory: | OPN || OPNA || OPNB || OPN2 | -// +--------++--------+--------++--------+---------++--------+--------+--------+ -// chip ID: | YM2203 || YM2608 | YMF288 || YM2610 | YM2610B || YM2612 | YM3438 | YMF276 | -// +--------++--------+--------++--------+---------++--------+--------+--------+ -// aka: | OPN || OPNA | OPN3 || OPNB | OPNB2 || OPN2 | OPN2C | OPN2L | -// FM channels: | 3 || 6 | 6 || 4 | 6 || 6 | 6 | 6 | -//AY-3-8910 channels: | 3 || 3 | 3 || 3 | 3 || - | - | - | -// ADPCM-A channels: | - || 6 int | 6 int || 6 ext | 6 ext || - | - | - | -// ADPCM-B channels: | - || 1 ext | - || 1 ext | 1 ext || - | - | - | -// Channel 6 "DAC": | no || no | no || no | no || yes | yes | yes | -// Clock divider: | 6/3/2 || 6/3/2 | 6/3/2 || 6 | 6 || 6 | 6 | 6 | -// Stereo: | no || yes | yes || yes | yes || yes | yes | yes | -// DAC: | 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 | -// LFO: | no || yes | yes || yes | yes || yes | yes | yes | -// +--------++--------+--------++--------+---------++--------+--------+--------+ +// FAMILIES // -// =================================================================================== +// The Yamaha FM chips can be broadly categoried into families: +// +// OPM (YM2151) +// 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, YMF278) +// +// All of these families are very closely related, and the ymfm engine +// implemented below 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 helper functions +// +// +// FAMILY HISTORY +// +// 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 implementations will use +// "holes" in the register space to store additional values that may be +// needed. +// +// +// 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 after any +// operation to call set_busy_end() with the attotime of when the busy +// signal should be released. // -// From OPM to OPN: -// - FM Channels reduced from 8 to 3 -// - Stereo removed, Hardware LFO removed, Channel 8 noise removed -// - Hardware pitch table removed, coarse detune removed, pitch calculation is different -// - 3 square wave channels added (GI AY-3–8910 compatible) -// - SSG-EG envelope mode added (lets you do AY style looping envelopes on FM ops) -// - Channel 3 can have different frequency for each op -// - CSM only applies to channel 3 -// - Register map is different -// - Operator timing is different. Channel 1 and 2 have very different timing. -// - OPN’s hardware FM clock divider can be changed from /6 (default) to /2 or /3 -// -// From OPN to OPNA: -// - Channels doubled from 3 to 6 -// - Added hardware LFO (different from OPM) -// - OPNA is stereo -// - OPNA uses a full 16bit dac instead of a 10:3bit dac. -// - 6 ADPCM-A drum channels added (play from built-in rom only) -// and 1 variable rate ADPCM-B channel (streaming from a small RAM). -// - Operator timing is different. All channels have the same timing on OPNA -// (roughly the same timing as Channel 3 on OPN), except for Channel 6 when -// set to algorithm 8. -// - Frequency calculation is 1 bit less precise and can wrap. -// - All carrier output values / 2 (this makes carrier output 13 bits instead -// of 14 bits) -// -// OPNB/OPNB2 is a OPNA that uses external ROM for the 6 ADPCM-A channels and the -// ADPCM-B channel. ADPCM-A and ADPCM-B use different buses and different ADPCM -// encodings. OPNB(2) doesn’t have a changeable divider (always /6). OPNB has 4 FM -// channels only (ch. 1 and 4 removed), OPNB2 has 6 channels. -// -// From OPNA to OPN2: -// - Removed GI AY-3–8910 channels and drums and streaming ADPCM -// - Operator timing is different. All channels have the same timing on OPN2. -// - Removed changeable divider (always /6) -// - Carrier output values / 32 instead of / 2 (carriers output 9 bits, down -// from 13 bits) -// - Built-in 9bit dac, uses analog mixing (time division multiplexing). The -// dac has a large gap between values 0 and -1 (resulting in the ladder effect). -// - Ch6 “DAC” mode. -// -// From OPN2 to OPN2C: -// - The DAC is more linear (no gap between 0 and -1). -// -// From OPN2C to OPN2L: -// - Carrier output is different (full 14 bits instead of 9 bits, narrowed to -// 13 on ch. mix) -// - Uses external DAC (16bit stereo), no analog mixing +// +// 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, 10, or 11-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. +// +// Interally, 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 +// +// OPN: [3-bit block]:[11-bit fnum] = 14 bits total +// OPL: [3-bit block]:[10-bit fnum]:0 = 14 bits total +// OPLL: [3-bit block]:[ 9-bit fnum]:00 = 14 bits total +// +// Template specialization in functions that interpret the 'block_freq' value +// is used to deconstruct it appropriately (specifically, see clock_phase). +// +// +// 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 +// 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: +// +// +--------++-----------------++-----------------------------------+ +// family: | OPM || OPN || OPL | +// +--------++--------+--------++--------+--------+--------+--------+ +// 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 | OPN3 || OPNB | OPNB2 || OPN2 | OPN2C | OPN2L | +// FM: | 3 || 6 | 6 || 4 | 6 || 6 | 6 | 6 | +// AY-8910: | 3 || 3 | 3 || 3 | 3 || - | - | - | +// 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 channels that are built in. +// 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 || YMF278B | +// +--------+---------++--------++--------++--------++---------+ +// aka: | OPL |MSX-AUDIO|| OPL2 || OPLL || OPL3 || OPL4 | +// FM: | 9 | 9 || 9 || 9 || 18 || 18 | +// ADPCM-B: | - | 1 ext || - || - || - || - | +// wavetable: | - | - || - || - || - || 24 | +// instruments: | no | no || no || yes || no || no | +// output: | 10.3fp | 10.3fp || 10.3fp || 9-bit || 16-bit || 16-bit | +// summing: | adder | adder || adder || muxer || 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). // // =================================================================================== // @@ -137,7 +364,7 @@ // attenuation value, in 4.8 fixed point format //------------------------------------------------- -inline u16 abs_sin_attenuation(u16 input) +inline u32 abs_sin_attenuation(u32 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 @@ -177,32 +404,54 @@ inline u16 abs_sin_attenuation(u16 input) // linear volume //------------------------------------------------- -inline u16 attenuation_to_volume(u16 input) +inline u32 attenuation_to_volume(u32 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 u16 const s_power_table[256] = { - 0x000,0x003,0x006,0x008,0x00b,0x00e,0x011,0x014,0x016,0x019,0x01c,0x01f,0x022,0x025,0x028,0x02a, - 0x02d,0x030,0x033,0x036,0x039,0x03c,0x03f,0x042,0x045,0x048,0x04b,0x04e,0x051,0x054,0x057,0x05a, - 0x05d,0x060,0x063,0x066,0x069,0x06c,0x06f,0x072,0x075,0x078,0x07b,0x07e,0x082,0x085,0x088,0x08b, - 0x08e,0x091,0x094,0x098,0x09b,0x09e,0x0a1,0x0a4,0x0a8,0x0ab,0x0ae,0x0b1,0x0b5,0x0b8,0x0bb,0x0be, - 0x0c2,0x0c5,0x0c8,0x0cc,0x0cf,0x0d2,0x0d6,0x0d9,0x0dc,0x0e0,0x0e3,0x0e7,0x0ea,0x0ed,0x0f1,0x0f4, - 0x0f8,0x0fb,0x0ff,0x102,0x106,0x109,0x10c,0x110,0x114,0x117,0x11b,0x11e,0x122,0x125,0x129,0x12c, - 0x130,0x134,0x137,0x13b,0x13e,0x142,0x146,0x149,0x14d,0x151,0x154,0x158,0x15c,0x160,0x163,0x167, - 0x16b,0x16f,0x172,0x176,0x17a,0x17e,0x181,0x185,0x189,0x18d,0x191,0x195,0x199,0x19c,0x1a0,0x1a4, - 0x1a8,0x1ac,0x1b0,0x1b4,0x1b8,0x1bc,0x1c0,0x1c4,0x1c8,0x1cc,0x1d0,0x1d4,0x1d8,0x1dc,0x1e0,0x1e4, - 0x1e8,0x1ec,0x1f0,0x1f5,0x1f9,0x1fd,0x201,0x205,0x209,0x20e,0x212,0x216,0x21a,0x21e,0x223,0x227, - 0x22b,0x230,0x234,0x238,0x23c,0x241,0x245,0x249,0x24e,0x252,0x257,0x25b,0x25f,0x264,0x268,0x26d, - 0x271,0x276,0x27a,0x27f,0x283,0x288,0x28c,0x291,0x295,0x29a,0x29e,0x2a3,0x2a8,0x2ac,0x2b1,0x2b5, - 0x2ba,0x2bf,0x2c4,0x2c8,0x2cd,0x2d2,0x2d6,0x2db,0x2e0,0x2e5,0x2e9,0x2ee,0x2f3,0x2f8,0x2fd,0x302, - 0x306,0x30b,0x310,0x315,0x31a,0x31f,0x324,0x329,0x32e,0x333,0x338,0x33d,0x342,0x347,0x34c,0x351, - 0x356,0x35b,0x360,0x365,0x36a,0x370,0x375,0x37a,0x37f,0x384,0x38a,0x38f,0x394,0x399,0x39f,0x3a4, - 0x3a9,0x3ae,0x3b4,0x3b9,0x3bf,0x3c4,0x3c9,0x3cf,0x3d4,0x3da,0x3df,0x3e4,0x3ea,0x3ef,0x3f5,0x3fa + 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] | 0x400) << 2) >> (input >> 8); + return s_power_table[input & 0xff] >> (input >> 8); } @@ -214,7 +463,7 @@ inline u16 attenuation_to_volume(u16 input) // fractional scale factor to decrease by) //------------------------------------------------- -inline u8 attenuation_increment(u8 rate, u8 index) +inline u32 attenuation_increment(u32 rate, u32 index) { static u32 const s_increment_table[64] = { @@ -248,7 +497,7 @@ inline u8 attenuation_increment(u8 rate, u8 index) // we'll keep the simplicity of the table //------------------------------------------------- -inline s8 detune_adjustment(u8 detune, u8 keycode) +inline s32 detune_adjustment(u32 detune, u32 keycode) { static u8 const s_detune_adjustment[32][4] = { @@ -261,7 +510,7 @@ inline s8 detune_adjustment(u8 detune, u8 keycode) { 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 } }; - s8 result = s_detune_adjustment[keycode][detune & 3]; + s32 result = s_detune_adjustment[keycode][detune & 3]; return BIT(detune, 2) ? -result : result; } @@ -274,7 +523,7 @@ inline s8 detune_adjustment(u8 detune, u8 keycode) // algorithm written to match Nuked behavior //------------------------------------------------- -inline s16 opn_lfo_pm_phase_adjustment(u8 fnum_bits, u8 pm_sensitivity, s8 lfo_raw_pm) +inline s32 opn_lfo_pm_phase_adjustment(u32 fnum_bits, u32 pm_sensitivity, s32 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 @@ -292,11 +541,11 @@ inline s16 opn_lfo_pm_phase_adjustment(u8 fnum_bits, u8 pm_sensitivity, s8 lfo_r }; // look up the relevant shifts - s8 abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm; - u8 const shifts = s_lfo_pm_shifts[pm_sensitivity][BIT(abs_pm, 0, 3)]; + s32 abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm; + u32 const shifts = s_lfo_pm_shifts[pm_sensitivity][BIT(abs_pm, 0, 3)]; // compute the adjustment - s16 adjust = (fnum_bits >> BIT(shifts, 0, 4)) + (fnum_bits >> BIT(shifts, 4, 4)); + s32 adjust = (fnum_bits >> BIT(shifts, 0, 4)) + (fnum_bits >> BIT(shifts, 4, 4)); if (pm_sensitivity > 5) adjust <<= pm_sensitivity - 5; adjust >>= 2; @@ -307,13 +556,13 @@ inline s16 opn_lfo_pm_phase_adjustment(u8 fnum_bits, u8 pm_sensitivity, s8 lfo_r //------------------------------------------------- -// opm_keycode_to_phase_step - converts an +// 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 //------------------------------------------------- -inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta) +inline u32 opm_key_code_to_phase_step(u32 block_freq, s32 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 @@ -323,11 +572,11 @@ inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta) // However, the YM2608 manual describes everything in terms of a nominal 8MHz // clock, which produces an FM clock of: // - // 8000000 / 6(channels) / 4(operators) / 6(prescale) = 55555Hz FM clock + // 8000000 / 24(operators) / 6(prescale) = 55555Hz FM clock // // Whereas the descriptions for the YM2151 use a nominal 3.579545MHz clock: // - // 3579545 / 8(channels) / 4(operators) / 2(prescale) = 55930Hz FM 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: @@ -390,22 +639,22 @@ inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta) }; // extract the block (octave) first - u8 block = BIT(block_freq, 10, 3); + u32 block = BIT(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 - u8 adjusted_code = BIT(block_freq, 6, 4) - BIT(block_freq, 8, 2); + u32 adjusted_code = BIT(block_freq, 6, 4) - BIT(block_freq, 8, 2); // now re-insert the 6-bit fraction - s16 eff_freq = (adjusted_code << 6) | BIT(block_freq, 0, 6); + s32 eff_freq = (adjusted_code << 6) | BIT(block_freq, 0, 6); // now that the gaps are removed, add the delta eff_freq += delta; // handle over/underflow by adjusting the block: - if (u16(eff_freq) >= 768) + if (u32(eff_freq) >= 768) { // minimum delta is -512 (PM), so we can only underflow by 1 octave if (eff_freq < 0) @@ -431,26 +680,67 @@ inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta) } +//------------------------------------------------- +// 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 u32 opl_key_scale_atten(u32 block, u32 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 u8 const fnum_to_atten[16] = { 0,24,32,37,40,43,45,47,48,50,51,52,53,54,55,56 }; + s32 result = fnum_to_atten[fnum_4msb] - 8 * (block ^ 7); + return std::max(0, result); +} + + //********************************************************* -// YMFM OPERATOR +// OPM SPECIFICS //********************************************************* //------------------------------------------------- -// ymfm_operator - constructor +// ymopm_registers - constructor //------------------------------------------------- -template -ymfm_operator::ymfm_operator(RegisterType regs) : - m_phase(0), - m_env_attenuation(0x3ff), - m_env_state(ENV_RELEASE), - m_ssg_inverted(false), - m_key_state(0), - m_keyon(0), - m_csm_triggered(0), - m_regs(regs) +ymopm_registers::ymopm_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 (int index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[0][index] = abs_sin_attenuation(index) | (BIT(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 (int index = 0; index < LFO_WAVEFORM_LENGTH; index++) + { + // waveform 0 is a sawtooth + u8 am = index ^ 0xff; + s8 pm = s8(index); + m_lfo_waveform[0][index] = am | (pm << 8); + + // waveform 1 is a square wave + am = BIT(index, 7) ? 0 : 0xff; + pm = s8(am ^ 0x80); + m_lfo_waveform[1][index] = am | (pm << 8); + + // waveform 2 is a triangle wave + am = BIT(index, 7) ? (index << 1) : ((index ^ 0xff) << 1); + pm = s8(BIT(index, 6) ? am : ~am); + m_lfo_waveform[2][index] = am | (pm << 8); + + // waveform 3 is noise; it is filled in dynamically + } } @@ -458,275 +748,1588 @@ ymfm_operator::ymfm_operator(RegisterType regs) : // save - register for save states //------------------------------------------------- -ALLOW_SAVE_TYPE(ymfm_operator::envelope_state); -ALLOW_SAVE_TYPE(ymfm_operator::envelope_state); -ALLOW_SAVE_TYPE(ymfm_operator::envelope_state); - -template -void ymfm_operator::save(device_t &device, u8 index) +void ymopm_registers::save(device_t &device) { - // save our data - device.save_item(YMFM_NAME(m_phase), index); - device.save_item(YMFM_NAME(m_env_attenuation), index); - device.save_item(YMFM_NAME(m_env_state), index); - device.save_item(YMFM_NAME(m_ssg_inverted), index); - device.save_item(YMFM_NAME(m_key_state), index); - device.save_item(YMFM_NAME(m_keyon), index); - device.save_item(YMFM_NAME(m_csm_triggered), index); + device.save_item(YMFM_NAME(m_lfo_counter)); + device.save_item(YMFM_NAME(m_lfo_am)); + device.save_item(YMFM_NAME(m_noise_lfsr)); + device.save_item(YMFM_NAME(m_noise_counter)); + device.save_item(YMFM_NAME(m_noise_state)); + device.save_item(YMFM_NAME(m_noise_lfo)); + device.save_item(YMFM_NAME(m_regdata)); } //------------------------------------------------- -// reset - reset the channel state +// reset - reset to initial state //------------------------------------------------- -template -void ymfm_operator::reset() +void ymopm_registers::reset() { - // reset our data - m_phase = 0; - m_env_attenuation = 0x3ff; - m_env_state = ENV_RELEASE; - m_ssg_inverted = 0; - m_key_state = 0; - m_keyon = 0; - m_csm_triggered = 0; + 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; } //------------------------------------------------- -// clock - master clocking function +// operator_map - return an array of operator +// indices for each channel; for OPM this is fixed //------------------------------------------------- -template -void ymfm_operator::clock(u32 env_counter, s8 lfo_raw_pm, u16 block_freq) +void ymopm_registers::operator_map(operator_mapping &dest) const { - // clock the key state - u8 keycode = block_freq_to_keycode(block_freq); - clock_keystate(m_keyon | m_csm_triggered, keycode); - m_csm_triggered = 0; + // 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; +} - // clock the SSG-EG state (OPN/OPNA) - if (m_regs.ssg_eg_enabled()) - clock_ssg_eg_state(keycode); - // clock the envelope if on an envelope cycle - if (BIT(env_counter, 0, 2) == 0) - clock_envelope(env_counter >> 2, keycode); +//------------------------------------------------- +// write - handle writes to the register array +//------------------------------------------------- - // clock the phase - clock_phase(lfo_raw_pm, block_freq); +bool ymopm_registers::write(u16 index, u8 data, u32 &channel, u32 &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 + BIT(data, 7)] = data; + else if (index != 0x1a) + m_regdata[index] = data; + + // handle writes to the key on index + if (index == 0x08) + { + channel = BIT(data, 0, 3); + opmask = BIT(data, 3, 4); + return true; + } + return false; } //------------------------------------------------- -// compute_volume - compute the 14-bit signed -// volume of this operator, given a phase -// modulation and an AM LFO offset +// clock_noise_and_lfo - clock the noise and LFO, +// handling clock division, depth, and waveform +// computations //------------------------------------------------- -template -s16 ymfm_operator::compute_volume(u16 modulation, u16 am_offset) const +s32 ymopm_registers::clock_noise_and_lfo() { - // early out if the envelope is effectively off - if (m_env_attenuation > ENV_QUIET) - return 0; + // base noise frequency is measured at 2x 1/2 FM frequency; this + // means each tick counts as two steps against the noise counter + u32 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 |= BIT(m_noise_lfsr, 17) ^ BIT(m_noise_lfsr, 14) ^ 1; - // start with the upper 10 bits of the phase value plus modulation - // the low 10 bits of this result represents a full 2*PI period over - // the full sin wave - u16 phase = (m_phase >> 10) + modulation; + // compare against the frequency and latch when we exceed it + if (m_noise_counter++ >= freq) + { + m_noise_counter = 0; + m_noise_state = BIT(m_noise_lfsr, 17); + } + } - // get the absolute value of the sin, as attenuation, as a 4.8 fixed point value - u16 sin_attenuation = abs_sin_attenuation(phase); + // 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 + u32 rate = lfo_rate(); + m_lfo_counter += (0x10 | BIT(rate, 0, 4)) << BIT(rate, 4, 4); + u32 lfo = BIT(m_lfo_counter, 22, 8); - // get the attenuation from the evelope generator as a 4.6 value, shifted up to 4.8 - u16 env_attenuation = envelope_attenuation(am_offset) << 2; + // 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 + u32 lfo_noise = BIT(m_noise_lfsr, 17, 8); + m_lfo_waveform[3][(lfo + 1) & 0xff] = lfo_noise | (lfo_noise << 8); - // combine into a 5.8 value, then convert from attenuation to 13-bit linear volume - s16 result = attenuation_to_volume(sin_attenuation + env_attenuation); + // 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 + s32 ampm = m_lfo_waveform[lfo_waveform()][lfo]; - // negate if in the negative part of the sin wave (sign bit gives 14 bits) - return BIT(phase, 9) ? -result : result; + // 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) * s32(lfo_pm_depth())) >> 7; } //------------------------------------------------- -// compute_noise_volume - compute the 14-bit -// signed noise volume of this operator, given a -// noise input value and an AM offset +// lfo_am_offset - return the AM offset from LFO +// for the given channel //------------------------------------------------- -template -s16 ymfm_operator::compute_noise_volume(u8 noise_state, u16 am_offset) const +u32 ymopm_registers::lfo_am_offset(u32 choffs) 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 - u16 result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1; + // OPM maps AM quite differently from OPN - // QUESTION: is AM applied still? + // shift value for AM sensitivity is [*, 0, 1, 2], + // mapping to values of [0, 23.9, 47.8, and 95.6dB] + u32 am_sensitivity = ch_lfo_am_sens(choffs); + if (am_sensitivity == 0) + return 0; - // negate based on the noise state - return BIT(noise_state, 0) ? -result : result; + // 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); } //------------------------------------------------- -// block_freq_to_keycode - given a concatenated -// block+frequency value, return the 5-bit keycode +// cache_operator_data - fill the operator cache +// with prefetched data //------------------------------------------------- -// OPM version -template<> -u8 ymfm_operator::block_freq_to_keycode(u16 block_freq) +void ymopm_registers::cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache) { - // block_freq is block(3b):keycode(4b):keyfrac(6b); the 5-bit keycode - // we want is just the top 5 bits here - return BIT(block_freq, 8, 5); -} + // set up the easy stuff + cache.waveform = &m_waveform[0][0]; -// OPN/OPNA version -template -u8 ymfm_operator::block_freq_to_keycode(u16 block_freq) -{ - // block_freq is block(3b):fnum(11b); the 5-bit keycode uses the top - // 4 bits plus a magic formula for the final bit - u8 keycode = BIT(block_freq, 10, 4) << 1; + // get frequency from the channel + u32 block_freq = cache.block_freq = ch_block_freq(choffs); - // lowest bit is determined by a mix of next lower FNUM bits - // according to this equation from the YM2608 manual: + // compute the keycode: block_freq is: // - // (F11 & (F10 | F9 | F8)) | (!F11 & F10 & F9 & F8) + // BBBCCCCFFFFFF + // ^^^^^ // - // for speed, we just look it up in a 16-bit constant - return keycode | BIT(0xfe80, BIT(block_freq, 7, 4)); + // the 5-bit keycode is just the top 5 bits (block + top 2 bits + // of the key code) + u32 keycode = BIT(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 = ymfm_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 + u32 ksrval = keycode >> (op_ksr(opoffs) ^ 3); + cache.eg_rate[YMFM_ENV_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval); + cache.eg_rate[YMFM_ENV_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval); + cache.eg_rate[YMFM_ENV_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval); + cache.eg_rate[YMFM_ENV_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval); + cache.eg_rate[YMFM_ENV_DEPRESS] = 0x3f; } //------------------------------------------------- -// effective_rate - return the effective 6-bit -// ADSR rate value after adjusting for keycode +// compute_phase_step - compute the phase step //------------------------------------------------- -template -u8 ymfm_operator::effective_rate(u8 rawrate, u8 keycode) +u32 ymopm_registers::compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm) { - if (rawrate == 0) - return 0; - u8 rate = rawrate * 2 + (keycode >> (m_regs.ksr() ^ 3)); - return (rate < 64) ? rate : 63; + // 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 s16 s_detune2_delta[4] = { 0, (600*64+50)/100, (781*64+50)/100, (950*64+50)/100 }; + s32 delta = s_detune2_delta[op_detune2(opoffs)]; + + // add in the PM delta + u32 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 += lfo_raw_pm << (pm_sensitivity - 5); + } + + // apply delta and convert to a frequency number + u32 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; } //------------------------------------------------- -// start_attack - start the attack phase; called -// when a keyon happens or when an SSG-EG cycle -// is complete and restarts +// log_keyon - log a key-on event //------------------------------------------------- -template -void ymfm_operator::start_attack(u8 keycode) +void ymopm_registers::log_keyon(u32 choffs, u32 opoffs) { - // don't change anything if already in attack state - if (m_env_state == ENV_ATTACK) - return; - m_env_state = ENV_ATTACK; + u32 chnum = choffs; + u32 opnum = opoffs; + + LOG("%d.%02d freq=%04X dt2=%d dt=%d fb=%d alg=%X mul=%X tl=%02X ksr=%d 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) + LOG(" am=%d/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0); + if (pm) + LOG(" pm=%d/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + if (am || pm) + LOG(" lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); + if (noise_enable() && opoffs == 31) + LOG(" noise=1"); +} - // generally not inverted at start, except if SSG-EG is - // enabled and one of the inverted modes is specified - m_ssg_inverted = m_regs.ssg_eg_enabled() & BIT(m_regs.ssg_eg_mode(), 2); - // reset the phase when we start an attack - m_phase = 0; +//********************************************************* +// OPN/OPNA SPECIFICS +//********************************************************* - // if the attack rate >= 62 then immediately go to max attenuation - if (effective_rate(m_regs.attack_rate(), keycode) >= 62) - m_env_attenuation = 0; +//------------------------------------------------- +// ymopn_registers_base - constructor +//------------------------------------------------- - // log key on events under certain conditions - if (m_regs.lfo_waveform() == 3 && m_regs.lfo_enabled() && ((m_regs.lfo_am_enabled() && m_regs.lfo_am_sensitivity() != 0) || m_regs.lfo_pm_sensitivity() != 0)) - { - LOG("KeyOn %d.%d: freq=%04X dt2=%d fb=%d alg=%d dt=%d mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X pan=%c%c", - m_regs.chnum(), m_regs.opnum(), - m_regs.block_freq(), - m_regs.detune2(), - m_regs.feedback(), - m_regs.algorithm(), - m_regs.detune(), - m_regs.multiple(), - m_regs.total_level(), - m_regs.ksr(), - m_regs.attack_rate(), - m_regs.decay_rate(), - m_regs.sustain_rate(), - m_regs.release_rate(), - m_regs.sustain_level(), - m_regs.pan_left() ? 'L' : '-', - m_regs.pan_right() ? 'R' : '-'); - if (m_regs.ssg_eg_enabled()) - LOG(" ssg=%X", m_regs.ssg_eg_mode()); - if (m_regs.lfo_enabled() && ((m_regs.lfo_am_enabled() && m_regs.lfo_am_sensitivity() != 0) || m_regs.lfo_pm_sensitivity() != 0)) - LOG(" am=%d pm=%d w=%d", m_regs.lfo_am_enabled() ? m_regs.lfo_am_sensitivity() : 0, m_regs.lfo_pm_sensitivity(), m_regs.lfo_waveform()); - if (m_regs.noise_enabled() && m_regs.opnum() == 3 && m_regs.chnum() == 7) - LOG(" noise=1"); - LOG("\n"); - } +template +ymopn_registers_base::ymopn_registers_base() : + m_lfo_counter(0), + m_lfo_am(0) +{ + // create the waveforms + for (int index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[0][index] = abs_sin_attenuation(index) | (BIT(index, 9) << 15); } //------------------------------------------------- -// start_release - start the release phase; -// called when a keyoff happens +// save - register for save states //------------------------------------------------- -template -void ymfm_operator::start_release() +template +void ymopn_registers_base::save(device_t &device) { - // don't change anything if already in release state - if (m_env_state == ENV_RELEASE) - return; - m_env_state = ENV_RELEASE; - - // adjust attenuation if inverted due to SSG-EG - if (m_ssg_inverted) - m_env_attenuation = 0x200 - m_env_attenuation; + if (IsOpnA) + { + device.save_item(YMFM_NAME(m_lfo_counter)); + device.save_item(YMFM_NAME(m_lfo_am)); + } + device.save_item(YMFM_NAME(m_regdata)); } //------------------------------------------------- -// clock_keystate - clock the keystate to match -// the incoming keystate +// reset - reset to initial state //------------------------------------------------- -template -void ymfm_operator::clock_keystate(u8 keystate, u8 keycode) +template +void ymopn_registers_base::reset() { - assert(keystate == 0 || keystate == 1); - - // has the key changed? - if ((keystate ^ m_key_state) != 0) + std::fill_n(&m_regdata[0], REGISTERS, 0); + if (IsOpnA) { - m_key_state = keystate; - - // if the key has turned on, start the attack - if (keystate != 0) - start_attack(keycode); - - // otherwise, start the release - else - start_release(); + // 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; } } //------------------------------------------------- -// clock_ssg_eg_state - clock the SSG-EG state; -// should only be called if SSG-EG is enabled +// operator_map - return an array of operator +// indices for each channel; for OPN this is fixed //------------------------------------------------- -template -void ymfm_operator::clock_ssg_eg_state(u8 keycode) +template<> +void ymopn_registers_base::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 ymopn_registers_base::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 ymopn_registers_base::write(u16 index, u8 data, u32 &channel, u32 &opmask) +{ + assert(index < REGISTERS); + + // writes in the 0xa0-af/0x1a0-af region are handled as latched pairs + // borrow unused registers 0xb8-bf/0x1b8-bf as temporary holding locations + if ((index & 0xf0) == 0xa0) + { + u32 latchindex = 0xb8 | (BIT(index, 3) << 2) | BIT(index, 0, 2); + if (IsOpnA) + latchindex |= index & 0x100; + + // writes to the upper half just latch (only low 6 bits matter) + if (BIT(index, 2)) + m_regdata[latchindex] = data | 0x80; + + // writes to the lower half only commit if the latch is there + else if (BIT(m_regdata[latchindex], 7)) + { + m_regdata[index | 4] = m_regdata[latchindex] & 0x3f; + m_regdata[latchindex] = 0; + } + } + + // everything else is normal + m_regdata[index] = data; + + // handle writes to the key on index + if (index == 0x28) + { + channel = BIT(data, 0, 2); + if (channel == 3) + return false; + if (IsOpnA) + channel += BIT(data, 2, 1) * 3; + opmask = BIT(data, 4, 4); + return true; + } + return false; +} + + +//------------------------------------------------- +// clock_noise_and_lfo - clock the noise and LFO, +// handling clock division, depth, and waveform +// computations +//------------------------------------------------- + +template +s32 ymopn_registers_base::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; + 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 u8 const lfo_max_count[8] = { 109, 78, 72, 68, 63, 45, 9, 6 }; + u32 subcount = u8(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 += subcount ^ 0xff; + + // AM value is 7 bits, staring at bit 8; grab the low 6 directly + m_lfo_am = BIT(m_lfo_counter, 8, 6); + + // first half of the AM period (bit 6 == 0) is inverted + if (BIT(m_lfo_counter, 8+6) == 0) + m_lfo_am ^= 0x3f; + + // PM value is 5 bits, starting at bit 10; grab the low 3 directly + s32 pm = BIT(m_lfo_counter, 10, 3); + + // PM is reflected based on bit 3 + if (BIT(m_lfo_counter, 10+3)) + pm ^= 7; + + // PM is negated based on bit 4 + return BIT(m_lfo_counter, 10+4) ? -pm : pm; +} + + +//------------------------------------------------- +// lfo_am_offset - return the AM offset from LFO +// for the given channel +//------------------------------------------------- + +template +u32 ymopn_registers_base::lfo_am_offset(u32 choffs) const +{ + // shift value for AM sensitivity is [7, 3, 1, 0], + // mapping to values of [0, 1.4, 5.9, and 11.8dB] + u32 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 +void ymopn_registers_base::cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache) +{ + // set up the easy stuff + cache.waveform = &m_waveform[0][0]; + + // get frequency from the channel + u32 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 + u32 keycode = BIT(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 |= BIT(0xfe80, BIT(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 = ymfm_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 + u32 ksrval = keycode >> (op_ksr(opoffs) ^ 3); + cache.eg_rate[YMFM_ENV_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval); + cache.eg_rate[YMFM_ENV_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval); + cache.eg_rate[YMFM_ENV_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval); + cache.eg_rate[YMFM_ENV_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval); + cache.eg_rate[YMFM_ENV_DEPRESS] = 0x3f; +} + + +//------------------------------------------------- +// compute_phase_step - compute the phase step +//------------------------------------------------- + +template +u32 ymopn_registers_base::compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 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) + u32 fnum = BIT(cache.block_freq, 0, 11) << 1; + + // if there's a non-zero PM sensitivity, compute the adjustment + u32 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(BIT(cache.block_freq, 4, 7), pm_sensitivity, lfo_raw_pm); + + // keep fnum to 12 bits + fnum &= 0xfff; + } + + // apply block shift to compute phase step + u32 block = BIT(cache.block_freq, 11, 3); + u32 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 +void ymopn_registers_base::log_keyon(u32 choffs, u32 opoffs) +{ + u32 chnum = (choffs & 3) + 3 * BIT(choffs, 8); + u32 opnum = (opoffs & 15) - ((opoffs & 15) / 4) + 12 * BIT(opoffs, 8); + + u32 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); + } + + LOG("%d.%02d freq=%04X dt=%d fb=%d alg=%X mul=%X tl=%02X ksr=%d 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) + LOG(" out=%c%c", + ch_output_0(choffs) ? 'L' : '-', + ch_output_1(choffs) ? 'R' : '-'); + if (op_ssg_eg_enable(opoffs)) + LOG(" ssg=%X", op_ssg_eg_mode(opoffs)); + bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); + if (am) + LOG(" am=%d", ch_lfo_am_sens(choffs)); + bool pm = (lfo_enable() && ch_lfo_pm_sens(choffs) != 0); + if (pm) + LOG(" pm=%d", ch_lfo_pm_sens(choffs)); + if (am || pm) + LOG(" lfo=%02X", lfo_rate()); + if (multi_freq() && choffs == 2) + LOG(" multi=1"); +} + + +//********************************************************* +// OPL SPECIFICS +//********************************************************* + +//------------------------------------------------- +// ymopl_registers_base - constructor +//------------------------------------------------- + +template +ymopl_registers_base::ymopl_registers_base() : + m_lfo_am_counter(0), + m_lfo_pm_counter(0), + m_noise_lfsr(1), + m_lfo_am(0) +{ + // create the waveforms + for (int index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[0][index] = abs_sin_attenuation(index) | (BIT(index, 9) << 15); + + if (WAVEFORMS >= 4) + { + u16 zeroval = m_waveform[0][0]; + for (int index = 0; index < WAVEFORM_LENGTH; index++) + { + m_waveform[1][index] = BIT(index, 9) ? zeroval : m_waveform[0][index]; + m_waveform[2][index] = m_waveform[0][index] & 0x7fff; + m_waveform[3][index] = BIT(index, 8) ? zeroval : (m_waveform[0][index] & 0x7fff); + if (WAVEFORMS >= 8) + { + m_waveform[4][index] = BIT(index, 9) ? zeroval : m_waveform[0][index * 2]; + m_waveform[5][index] = BIT(index, 9) ? zeroval : m_waveform[0][(index * 2) & 0x1ff]; + m_waveform[6][index] = BIT(index, 9) << 15; + m_waveform[7][index] = (zeroval - m_waveform[0][(index / 2)]) | (BIT(index, 9) << 15); + } + } + } +} + + +//------------------------------------------------- +// save - register for save states +//------------------------------------------------- + +template +void ymopl_registers_base::save(device_t &device) +{ + device.save_item(YMFM_NAME(m_lfo_am_counter)); + device.save_item(YMFM_NAME(m_lfo_pm_counter)); + device.save_item(YMFM_NAME(m_lfo_am)); + device.save_item(YMFM_NAME(m_noise_lfsr)); + device.save_item(YMFM_NAME(m_regdata)); +} + + +//------------------------------------------------- +// reset - reset to initial state +//------------------------------------------------- + +template +void ymopl_registers_base::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + +//------------------------------------------------- +// operator_map - return an array of operator +// indices for each channel; for OPL this is fixed +//------------------------------------------------- + +template +void ymopl_registers_base::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 + u32 fourop = fourop_enable(); + + dest.chan[ 0] = BIT(fourop, 0) ? operator_list( 0, 3, 6, 9 ) : operator_list( 0, 3 ); + dest.chan[ 1] = BIT(fourop, 1) ? operator_list( 1, 4, 7, 10 ) : operator_list( 1, 4 ); + dest.chan[ 2] = BIT(fourop, 2) ? operator_list( 2, 5, 8, 11 ) : operator_list( 2, 5 ); + dest.chan[ 3] = BIT(fourop, 0) ? operator_list() : operator_list( 6, 9 ); + dest.chan[ 4] = BIT(fourop, 1) ? operator_list() : operator_list( 7, 10 ); + dest.chan[ 5] = BIT(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] = BIT(fourop, 3) ? operator_list( 18, 21, 24, 27 ) : operator_list( 18, 21 ); + dest.chan[10] = BIT(fourop, 4) ? operator_list( 19, 22, 25, 28 ) : operator_list( 19, 22 ); + dest.chan[11] = BIT(fourop, 5) ? operator_list( 20, 23, 26, 29 ) : operator_list( 20, 23 ); + dest.chan[12] = BIT(fourop, 3) ? operator_list() : operator_list( 24, 27 ); + dest.chan[13] = BIT(fourop, 4) ? operator_list() : operator_list( 25, 28 ); + dest.chan[14] = BIT(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 +bool ymopl_registers_base::write(u16 index, u8 data, u32 &channel, u32 &opmask) +{ + assert(index < REGISTERS); + + // writes to the mode register with high bit set ignore the low bits + if (index == REG_MODE && BIT(data, 7) != 0) + m_regdata[index] |= 0x80; + else + m_regdata[index] = data; + + // handle writes to the rhythm keyons + if (index == 0xbd) + { + channel = YMFM_RHYTHM_CHANNEL; + opmask = BIT(data, 5) ? BIT(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 * BIT(index, 8); + opmask = BIT(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 s32 opl_clock_noise_and_lfo(u32 &noise_lfsr, u16 &lfo_am_counter, u16 &lfo_pm_counter, u8 &lfo_am, u32 am_depth, u32 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 |= BIT(noise_lfsr, 23) ^ BIT(noise_lfsr, 9) ^ BIT(noise_lfsr, 8) ^ BIT(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 + u32 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 + u32 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 s8 const pm_scale[8] = { 8, 4, 0, -4, -8, -4, 0, 4 }; + return pm_scale[BIT(pm_counter, 10, 3)] >> (pm_depth ^ 1); +} + +template +s32 ymopl_registers_base::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 +void ymopl_registers_base::cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache) +{ + // set up the easy stuff + cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0]; + + // get frequency from the channel + u32 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 + u32 keycode = BIT(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 |= BIT(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 + u32 multiple = op_multiple(opoffs); + cache.multiple = ((multiple & 0xe) | BIT(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 = ymfm_opdata_cache::PHASE_STEP_DYNAMIC; + + // total level, scaled by 8 + cache.total_level = op_total_level(opoffs) << 3; + + // pre-add key scale level + u32 ksl = op_ksl(opoffs); + if (ksl != 0) + cache.total_level += opl_key_scale_atten(BIT(block_freq, 10, 3), BIT(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 + u32 ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1)); + cache.eg_rate[YMFM_ENV_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval); + cache.eg_rate[YMFM_ENV_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval); + cache.eg_rate[YMFM_ENV_SUSTAIN] = op_eg_sustain(opoffs) ? 0 : effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[YMFM_ENV_RELEASE] = effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[YMFM_ENV_DEPRESS] = 0x3f; +} + + +//------------------------------------------------- +// compute_phase_step - compute the phase step +//------------------------------------------------- + +static u32 opl_compute_phase_step(u32 block_freq, u32 multiple, s32 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 + u32 fnum = BIT(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 * BIT(block_freq, 7, 3)) >> 1; + + // keep fnum to 12 bits + fnum &= 0xfff; + + // apply block shift to compute phase step + u32 block = BIT(block_freq, 10, 3); + u32 phase_step = (fnum << block) >> 2; + + // apply frequency multiplier (which is cached as an x.1 value) + return (phase_step * multiple) >> 1; +} + +template +u32 ymopl_registers_base::compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 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 +void ymopl_registers_base::log_keyon(u32 choffs, u32 opoffs) +{ + u32 chnum = (choffs & 15) + 9 * BIT(choffs, 8); + u32 opnum = (opoffs & 31) - 2 * ((opoffs & 31) / 8) + 18 * BIT(opoffs, 8); + + LOG("%2d.%02d freq=%04X fb=%d alg=%X mul=%X tl=%02X ksr=%d ns=%d ksl=%d adr=%X/%X/%X sl=%X sus=%d", + 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) + LOG(" 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) + LOG(" am=%d", lfo_am_depth()); + if (op_lfo_pm_enable(opoffs) != 0) + LOG(" pm=%d", lfo_pm_depth()); + if (waveform_enable() && op_waveform(opoffs) != 0) + LOG(" wf=%d", op_waveform(opoffs)); + if (is_rhythm(choffs)) + LOG(" rhy=1"); + if (DYNAMIC_OPS) + { + operator_mapping map; + operator_map(map); + if (BIT(map.chan[chnum], 16, 8) != 0xff) + LOG(" 4op"); + } +} + + +//********************************************************* +// OPLL SPECIFICS +//********************************************************* + +//------------------------------------------------- +// ymopll_registers - constructor +//------------------------------------------------- + +ymopll_registers::ymopll_registers() : + m_lfo_am_counter(0), + m_lfo_pm_counter(0), + m_noise_lfsr(1), + m_lfo_am(0) +{ + // create the waveforms + for (int index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[0][index] = abs_sin_attenuation(index) | (BIT(index, 9) << 15); + + u16 zeroval = m_waveform[0][0]; + for (int index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[1][index] = BIT(index, 9) ? zeroval : m_waveform[0][index]; + + // initialize the instruments to something sane + for (int choffs = 0; choffs < CHANNELS; choffs++) + m_chinst[choffs] = &m_regdata[0]; + for (int opoffs = 0; opoffs < OPERATORS; opoffs++) + m_opinst[opoffs] = &m_regdata[BIT(opoffs, 0)]; +} + + +//------------------------------------------------- +// save - register for save states +//------------------------------------------------- + +void ymopll_registers::save(device_t &device) +{ + device.save_item(YMFM_NAME(m_lfo_am_counter)); + device.save_item(YMFM_NAME(m_lfo_pm_counter)); + device.save_item(YMFM_NAME(m_lfo_am)); + device.save_item(YMFM_NAME(m_noise_lfsr)); + device.save_item(YMFM_NAME(m_regdata)); +} + + +//------------------------------------------------- +// reset - reset to initial state +//------------------------------------------------- + +void ymopll_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + +//------------------------------------------------- +// operator_map - return an array of operator +// indices for each channel; for OPLL this is fixed +//------------------------------------------------- + +void ymopll_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 ymopll_registers::write(u16 index, u8 data, u32 &channel, u32 &opmask) +{ + assert(index < REGISTERS); + + // write the new data + m_regdata[index] = data; + + // handle writes to the rhythm keyons + if (index == 0x0e) + { + channel = YMFM_RHYTHM_CHANNEL; + opmask = BIT(data, 5) ? BIT(data, 0, 5) : 0; + return true; + } + + // handle writes to the channel keyons + if ((index & 0xf0) == 0x20) + { + channel = index & 0x0f; + if (channel < CHANNELS) + { + opmask = BIT(data, 4) ? 3 : 0; + return true; + } + } + return false; +} + + +//------------------------------------------------- +// clock_noise_and_lfo - clock the noise and LFO, +// handling clock division, depth, and waveform +// computations +//------------------------------------------------- + +s32 ymopll_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 ymopll_registers::cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache) +{ + // first set up the instrument data + u32 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] + BIT(opoffs, 0); + + // set up the easy stuff + cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0]; + + // get frequency from the channel + u32 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 + u32 keycode = BIT(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 + u32 multiple = op_multiple(opoffs); + cache.multiple = ((multiple & 0xe) | BIT(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 = ymfm_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 (BIT(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 + u32 ksl = op_ksl(opoffs); + if (ksl != 0) + cache.total_level += opl_key_scale_atten(BIT(block_freq, 9, 3), BIT(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 u8 DP = 12 * 4; + constexpr u8 RR = 7 * 4; + constexpr u8 RS = 5 * 4; + + // determine KSR adjustment for envelope rates + u32 ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1)); + cache.eg_rate[YMFM_ENV_DEPRESS] = DP; + cache.eg_rate[YMFM_ENV_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval); + cache.eg_rate[YMFM_ENV_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval); + if (op_eg_sustain(opoffs)) + { + cache.eg_rate[YMFM_ENV_SUSTAIN] = 0; + cache.eg_rate[YMFM_ENV_RELEASE] = ch_sustain(choffs) ? RS : effective_rate(op_release_rate(opoffs) * 4, ksrval); + } + else + { + cache.eg_rate[YMFM_ENV_SUSTAIN] = effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[YMFM_ENV_RELEASE] = ch_sustain(choffs) ? RS : RR; + } +} + + +//------------------------------------------------- +// compute_phase_step - compute the phase step +//------------------------------------------------- + +u32 ymopll_registers::compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 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 +//------------------------------------------------- + +void ymopll_registers::log_keyon(u32 choffs, u32 opoffs) +{ + u32 chnum = choffs; + u32 opnum = opoffs; + + LOG("%d.%02d freq=%04X inst=%X fb=%d mul=%X", + chnum, opnum, + ch_block_freq(choffs), + ch_instrument(choffs), + ch_feedback(choffs), + op_multiple(opoffs)); + + if (BIT(opoffs, 0) == 1 || (is_rhythm(choffs) && choffs >= 6)) + LOG(" vol=%X", op_volume(opoffs)); + else + LOG(" tl=%02X", ch_total_level(choffs)); + + LOG(" ksr=%d ksl=%d adr=%X/%X/%X sl=%X sus=%d/%d", + 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)) + LOG(" am=1"); + if (op_lfo_pm_enable(opoffs)) + LOG(" pm=1"); + if (op_waveform(opoffs) != 0) + LOG(" wf=1"); + if (is_rhythm(choffs)) + LOG(" rhy=1"); +} + + +//********************************************************* +// YMFM OPERATOR +//********************************************************* + +//------------------------------------------------- +// ymfm_operator - constructor +//------------------------------------------------- + +template +ymfm_operator::ymfm_operator(ymfm_engine_base &owner, u32 opoffs) : + m_choffs(0), + m_opoffs(opoffs), + m_phase(0), + m_env_attenuation(0x3ff), + m_env_state(YMFM_ENV_RELEASE), + m_ssg_inverted(false), + m_key_state(0), + m_keyon_live(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// save - register for save states +//------------------------------------------------- + +ALLOW_SAVE_TYPE(ymfm_envelope_state); + +template +void ymfm_operator::save(device_t &device, u32 index) +{ + // save our data + device.save_item(YMFM_NAME(m_phase), index); + device.save_item(YMFM_NAME(m_env_attenuation), index); + device.save_item(YMFM_NAME(m_env_state), index); + device.save_item(YMFM_NAME(m_ssg_inverted), index); + device.save_item(YMFM_NAME(m_key_state), index); + device.save_item(YMFM_NAME(m_keyon_live), index); +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +template +void ymfm_operator::reset() +{ + // reset our data + m_phase = 0; + m_env_attenuation = 0x3ff; + m_env_state = YMFM_ENV_RELEASE; + m_ssg_inverted = 0; + m_key_state = 0; + m_keyon_live = 0; +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +template +bool ymfm_operator::prepare() +{ + // cache the data + m_regs.cache_operator_data(m_choffs, m_opoffs, m_cache); + + // clock the key state + clock_keystate(u32(m_keyon_live != 0)); + m_keyon_live &= ~(1 << YMFM_KEYON_CSM); + + // we're active until we're quiet after the release + return (m_env_state != YMFM_ENV_RELEASE || m_env_attenuation < ENV_QUIET); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +template +void ymfm_operator::clock(u32 env_counter, s32 lfo_raw_pm) +{ + // clock the SSG-EG state (OPN/OPNA) + if (m_regs.op_ssg_eg_enable(m_opoffs)) + clock_ssg_eg_state(); + + // clock the envelope if on an envelope cycle; env_counter is a x.2 value + if (BIT(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 +s32 ymfm_operator::compute_volume(u32 phase, u32 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 > ENV_QUIET) + return 0; + + // get the absolute value of the sin, as attenuation, as a 4.8 fixed point value + u32 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 + u32 env_attenuation = envelope_attenuation(am_offset) << 2; + + // combine into a 5.8 value, then convert from attenuation to 13-bit linear volume + s32 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 BIT(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 +s32 ymfm_operator::compute_noise_volume(u32 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 + u32 result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1; + + // QUESTION: is AM applied still? + + // negate based on the noise state + return BIT(m_regs.noise_state(), 0) ? -result : result; +} + + +//------------------------------------------------- +// keyonoff - signal a key on/off event +//------------------------------------------------- + +template +void ymfm_operator::keyonoff(u32 on, ymfm_keyon_type type) +{ + m_keyon_live = (m_keyon_live & ~(1 << int(type))) | (BIT(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 +void ymfm_operator::start_attack() +{ + // don't change anything if already in attack state + if (m_env_state == YMFM_ENV_ATTACK) + return; + m_env_state = YMFM_ENV_ATTACK; + + // generally not inverted at start, except if SSG-EG is + // enabled and one of the inverted modes is specified + if (RegisterType::EG_HAS_SSG) + m_ssg_inverted = m_regs.op_ssg_eg_enable(m_opoffs) & BIT(m_regs.op_ssg_eg_mode(m_opoffs), 2); + + // reset the phase when we start an attack + m_phase = 0; + + // if the attack rate >= 62 then immediately go to max attenuation + if (m_cache.eg_rate[YMFM_ENV_ATTACK] >= 62) + m_env_attenuation = 0; +} + + +//------------------------------------------------- +// start_release - start the release phase; +// called when a keyoff happens +//------------------------------------------------- + +template +void ymfm_operator::start_release() +{ + // don't change anything if already in release state + if (m_env_state == YMFM_ENV_RELEASE) + return; + m_env_state = YMFM_ENV_RELEASE; + + // adjust attenuation if inverted due to SSG-EG + if (RegisterType::EG_HAS_SSG && m_ssg_inverted) + m_env_attenuation = 0x200 - m_env_attenuation; +} + + +//------------------------------------------------- +// clock_keystate - clock the keystate to match +// the incoming keystate +//------------------------------------------------- + +template +void ymfm_operator::clock_keystate(u32 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) + { + // log key on events under certain conditions + // if (m_regs.lfo_waveform() == 3 && m_regs.lfo_enable() && ((m_regs.lfo_am_enable() && m_regs.lfo_am_sensitivity() != 0) || m_regs.lfo_pm_sensitivity() != 0)) + // if ((m_regs.rhythm_enable() && m_regs.chnum() >= 6) || + // (m_regs.waveform_enable() && m_regs.waveform() != 0)) + { + LOG("%s: ", m_owner.device().tag(), m_opoffs); + m_regs.log_keyon(m_choffs, m_opoffs); + LOG("\n"); + } + + // 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 = YMFM_ENV_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 +void ymfm_operator::clock_ssg_eg_state() { // work only happens once the attenuation crosses above 0x200 if (!BIT(m_env_attenuation, 9)) @@ -741,7 +2344,7 @@ void ymfm_operator::clock_ssg_eg_state(u8 keycode) // 101: inverted run once, hold low // 110: inverted repeat, alternating between inverted/non-inverted // 111: inverted run once, hold high - u8 mode = m_regs.ssg_eg_mode(); + u32 mode = m_regs.op_ssg_eg_mode(m_opoffs); // hold modes (1/3/5/7) if (BIT(mode, 0)) @@ -751,7 +2354,7 @@ void ymfm_operator::clock_ssg_eg_state(u8 keycode) // if holding low (modes 1/5), force the attenuation to maximum // once we're past the attack phase - if (m_env_state != ENV_ATTACK && BIT(mode, 1) == 0) + if (m_env_state != YMFM_ENV_ATTACK && BIT(mode, 1) == 0) m_env_attenuation = 0x3ff; } @@ -762,8 +2365,8 @@ void ymfm_operator::clock_ssg_eg_state(u8 keycode) m_ssg_inverted ^= BIT(mode, 1); // restart attack if in decay/sustain states - if (m_env_state == ENV_DECAY || m_env_state == ENV_SUSTAIN) - start_attack(keycode); + if (m_env_state == YMFM_ENV_DECAY || m_env_state == YMFM_ENV_SUSTAIN) + start_attack(); // phase is reset to 0 regardless in modes 0/4 if (BIT(mode, 1) == 0) @@ -771,7 +2374,7 @@ void ymfm_operator::clock_ssg_eg_state(u8 keycode) } // in all modes, once we hit release state, attenuation is forced to maximum - if (m_env_state == ENV_RELEASE) + if (m_env_state == YMFM_ENV_RELEASE) m_env_attenuation = 0x3ff; } @@ -782,31 +2385,21 @@ void ymfm_operator::clock_ssg_eg_state(u8 keycode) //------------------------------------------------- template -void ymfm_operator::clock_envelope(u16 env_counter, u8 keycode) +void ymfm_operator::clock_envelope(u32 env_counter) { - // if in attack state, see if we hit minimum attenuation - if (m_env_state == ENV_ATTACK && m_env_attenuation == 0) - m_env_state = ENV_DECAY; - - // if in decay state, see if we hit the sustain level - else if (m_env_state == ENV_DECAY) - { - // 4-bit sustain level, but 15 means 31 so effectively 5 bits - u8 target = m_regs.sustain_level(); - target |= (target + 1) & 0x10; - - // bring current attenuation down to 5 bits and compare - if ((m_env_attenuation >> 5) >= target) - m_env_state = ENV_SUSTAIN; - } + // handle attack->decay and decay->sustain transitions + if (m_env_state == YMFM_ENV_ATTACK && m_env_attenuation == 0) + m_env_state = YMFM_ENV_DECAY; + else if (m_env_state == YMFM_ENV_DECAY && m_env_attenuation >= m_cache.eg_sustain) + m_env_state = YMFM_ENV_SUSTAIN; - // determine our raw 5-bit rate value - u8 rate = effective_rate(m_regs.adsr_rate(m_env_state), keycode); + // fetch the appropriate 6-bit rate value from the cache + u32 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 - u8 rate_shift = rate >> 2; + u32 rate_shift = rate >> 2; env_counter <<= rate_shift; // see if the fractional part is 0; if not, it's not time to clock @@ -814,10 +2407,10 @@ void ymfm_operator::clock_envelope(u16 env_counter, u8 keycode) return; // determine the increment based on the non-fractional part of env_counter - u8 increment = attenuation_increment(rate, BIT(env_counter, 11, 3)); + u32 increment = attenuation_increment(rate, BIT(env_counter, 11, 3)); // attack is the only one that increases - if (m_env_state == ENV_ATTACK) + if (m_env_state == YMFM_ENV_ATTACK) { // glitch means that attack rates of 62/63 don't increment if // changed after the initial key on (where they are handled @@ -826,112 +2419,45 @@ void ymfm_operator::clock_envelope(u16 env_counter, u8 keycode) // QUESTION: this check affects one of the operators on the gng credit sound // is it correct? // QUESTION: does this apply only to YM2612? - 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.ssg_eg_enabled()) - 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; - } -} - - -//------------------------------------------------- -// clock_phase - clock the 10.10 phase value; the -// OPN version of the logic has been verified -// against the Nuked phase generator -//------------------------------------------------- - -// OPM version -template<> -void ymfm_operator::clock_phase(s8 lfo_raw_pm, u16 block_freq) -{ - // start with coarse detune delta; table uses cents value from - // manual, converted into 1/64ths - static const s16 s_detune2_delta[4] = { 0, (600*64+50)/100, (781*64+50)/100, (950*64+50)/100 }; - s16 delta = s_detune2_delta[m_regs.detune2()]; - - // add in the PM delta - u8 pm_sensitivity = m_regs.lfo_pm_sensitivity(); - 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 += lfo_raw_pm << (pm_sensitivity - 5); - } - - // apply delta and convert to a frequency number - u32 phase_step = opm_keycode_to_phase_step(block_freq, delta); - - // apply detune based on the keycode - phase_step += detune_adjustment(m_regs.detune(), block_freq_to_keycode(block_freq)); - - // QUESTION: do we clamp to 17 bits like YM2612? - - // apply frequency multiplier (0 means 0.5, other values are as-is) - u8 multiple = m_regs.multiple(); - if (multiple == 0) - phase_step >>= 1; - else - phase_step *= multiple; - - // finally apply the step to the current phase value - m_phase += phase_step; -} - -template -void ymfm_operator::clock_phase(s8 lfo_raw_pm, u16 block_freq) -{ - // extract frequency number (low 11 bits of block_freq) - u16 fnum = BIT(block_freq, 0, 11) << 1; + if (rate < 62) + m_env_attenuation += (~m_env_attenuation * increment) >> 4; + } - // if there's a non-zero PM sensitivity, compute the adjustment - u8 pm_sensitivity = m_regs.lfo_pm_sensitivity(); - if (pm_sensitivity != 0) + // all other cases are similar + else { - // apply the phase adjustment based on the upper 7 bits - // of FNUM and the PM depth parameters - fnum += opn_lfo_pm_phase_adjustment(BIT(block_freq, 4, 7), pm_sensitivity, lfo_raw_pm); + // non-SSG-EG cases just apply the increment + if (!m_regs.op_ssg_eg_enable(m_opoffs)) + m_env_attenuation += increment; - // keep fnum to 12 bits - fnum &= 0xfff; - } + // SSG-EG only applies if less than mid-point, and then at 4x + else if (m_env_attenuation < 0x200) + m_env_attenuation += 4 * increment; - // apply block shift to compute phase step - u8 block = BIT(block_freq, 11, 3); - u32 phase_step = (fnum << block) >> 2; + // clamp the final attenuation + if (m_env_attenuation >= 0x400) + m_env_attenuation = 0x3ff; - // apply detune based on the keycode - phase_step += detune_adjustment(m_regs.detune(), block_freq_to_keycode(block_freq)); + // transition from depress to attack + if (RegisterType::EG_HAS_DEPRESS && m_env_state == YMFM_ENV_DEPRESS && m_env_attenuation >= 0x200) + start_attack(); + } +} - // clamp to 17 bits in case detune overflows - // QUESTION: is this specific to the YM2612/3438? - phase_step &= 0x1ffff; - // apply frequency multiplier (0 means 0.5, other values are as-is) - u8 multiple = m_regs.multiple(); - if (multiple == 0) - phase_step >>= 1; - else - phase_step *= multiple; +//------------------------------------------------- +// clock_phase - clock the 10.10 phase value; the +// OPN version of the logic has been verified +// against the Nuked phase generator +//------------------------------------------------- + +template +void ymfm_operator::clock_phase(s32 lfo_raw_pm) +{ + // read from the cache, or recalculate if PM active + u32 phase_step = m_cache.phase_step; + if (phase_step == ymfm_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; @@ -944,20 +2470,20 @@ void ymfm_operator::clock_phase(s8 lfo_raw_pm, u16 block_freq) //------------------------------------------------- template -u16 ymfm_operator::envelope_attenuation(u8 am_offset) const +u32 ymfm_operator::envelope_attenuation(u32 am_offset) const { - u16 result = m_env_attenuation; + u32 result = m_env_attenuation; // invert if necessary due to SSG-EG - if (m_ssg_inverted) + if (RegisterType::EG_HAS_SSG && m_ssg_inverted) result = (0x200 - result) & 0x3ff; // add in LFO AM modulation - if (m_regs.lfo_am_enabled()) + if (m_regs.op_lfo_am_enable(m_opoffs)) result += am_offset; - // add in total level - result += m_regs.total_level() << 3; + // add in total level and KSL from the cache + result += m_cache.total_level; // clamp to max and return return (result < 0x400) ? result : 0x3ff; @@ -974,14 +2500,13 @@ u16 ymfm_operator::envelope_attenuation(u8 am_offset) const //------------------------------------------------- template -ymfm_channel::ymfm_channel(RegisterType regs) : +ymfm_channel::ymfm_channel(ymfm_engine_base &owner, u32 choffs) : + m_choffs(choffs), m_feedback{ 0, 0 }, m_feedback_in(0), - m_op1(regs.operator_registers(0)), - m_op2(regs.operator_registers(1)), - m_op3(regs.operator_registers(2)), - m_op4(regs.operator_registers(3)), - m_regs(regs) + m_op{ nullptr, nullptr, nullptr, nullptr }, + m_regs(owner.regs()), + m_owner(owner) { } @@ -991,17 +2516,11 @@ ymfm_channel::ymfm_channel(RegisterType regs) : //------------------------------------------------- template -void ymfm_channel::save(device_t &device, u8 index) +void ymfm_channel::save(device_t &device, u32 index) { // save our data device.save_item(YMFM_NAME(m_feedback), index); device.save_item(YMFM_NAME(m_feedback_in), index); - - // save operator data - m_op1.save(device, index * 4 + 0); - m_op2.save(device, index * 4 + 1); - m_op3.save(device, index * 4 + 2); - m_op4.save(device, index * 4 + 3); } @@ -1015,12 +2534,6 @@ void ymfm_channel::reset() // reset our data m_feedback[0] = m_feedback[1] = 0; m_feedback_in = 0; - - // reset the operators - m_op1.reset(); - m_op2.reset(); - m_op3.reset(); - m_op4.reset(); } @@ -1029,26 +2542,30 @@ void ymfm_channel::reset() //------------------------------------------------- template -void ymfm_channel::keyonoff(u8 states) +void ymfm_channel::keyonoff(u32 states, ymfm_keyon_type type) { - m_op1.keyonoff(BIT(states, 0)); - m_op2.keyonoff(BIT(states, 1)); - m_op3.keyonoff(BIT(states, 2)); - m_op4.keyonoff(BIT(states, 3)); + for (int opnum = 0; opnum < std::size(m_op); opnum++) + if (m_op[opnum] != nullptr) + m_op[opnum]->keyonoff(BIT(states, opnum), type); } //------------------------------------------------- -// keyon_csm - signal CSM key on to our operators +// prepare - prepare for clocking //------------------------------------------------- template -void ymfm_channel::keyon_csm() +bool ymfm_channel::prepare() { - m_op1.keyon_csm(); - m_op2.keyon_csm(); - m_op3.keyon_csm(); - m_op4.keyon_csm(); + u32 active_mask = 0; + + // prepare all operators and determine if they are active + for (int opnum = 0; opnum < std::size(m_op); opnum++) + if (m_op[opnum] != nullptr) + if (m_op[opnum]->prepare()) + active_mask |= 1 << opnum; + + return (active_mask != 0); } @@ -1057,192 +2574,278 @@ void ymfm_channel::keyon_csm() //------------------------------------------------- template -void ymfm_channel::clock(u32 env_counter, s8 lfo_raw_pm, bool is_multi_freq) +void ymfm_channel::clock(u32 env_counter, s32 lfo_raw_pm) { - // grab common block/fnum values - u16 block_freq = m_regs.block_freq(); - // clock the feedback through m_feedback[0] = m_feedback[1]; m_feedback[1] = m_feedback_in; - // in multi-frequency mode, the first 3 channels use independent block/fnum values - if (is_multi_freq) + for (int opnum = 0; opnum < std::size(m_op); opnum++) + if (m_op[opnum] != nullptr) + m_op[opnum]->clock(env_counter, lfo_raw_pm); +} + + +//------------------------------------------------- +// 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 +void ymfm_channel::output_2op(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 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 + u32 am_offset = m_regs.lfo_am_offset(m_choffs); + + // operator 1 has optional self-feedback + s32 opmod = 0; + u32 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 + s32 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 + s32 result; + if (BIT(m_regs.ch_algorithm(m_choffs), 0) == 0) { - m_op1.clock(env_counter, lfo_raw_pm, m_regs.multi_block_freq1()); - m_op2.clock(env_counter, lfo_raw_pm, m_regs.multi_block_freq2()); - m_op3.clock(env_counter, lfo_raw_pm, m_regs.multi_block_freq0()); + // 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; } - - // otherwise, all channels use the common block/fnum else { - m_op1.clock(env_counter, lfo_raw_pm, block_freq); - m_op2.clock(env_counter, lfo_raw_pm, block_freq); - m_op3.clock(env_counter, lfo_raw_pm, block_freq); + result = op1value + (m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift); + s32 clipmin = -clipmax - 1; + result = std::clamp(result, clipmin, clipmax); } - // operator 3 uses the common values in all cases - m_op4.clock(env_counter, lfo_raw_pm, block_freq); + // add to the output + add_to_output(m_choffs, outputs, result); } //------------------------------------------------- -// output - combine the operators according to the -// specified algorithm, returning a sum according -// to the rshift and clipmax parameters, which -// vary between different OPN implementations +// 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 -void ymfm_channel::output(u8 lfo_raw_am, u8 noise_state, s32 &lsum, s32 &rsum, u8 rshift, s16 clipmax) const +void ymfm_channel::output_4op(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 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 - u16 am_offset = lfo_am_offset(lfo_raw_am); - - // Algorithms: - // 0: O1 -> O2 -> O3 -> O4 -> out - // 1: (O1 + O2) -> O3 -> O4 -> out - // 2: (O1 + (O2 -> O3)) -> O4 -> out - // 3: ((O1 -> O2) + O3) -> O4 -> out - // 4: ((O1 -> O2) + (O3 -> O4)) -> out - // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out - // 6: ((O1 -> O2) + O3 + O4) -> out - // 7: (O1 + O2 + O3 + O4) -> out + u32 am_offset = m_regs.lfo_am_offset(m_choffs); + + // operator 1 has optional self-feedback + s32 opmod = 0; + u32 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 + s32 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 that is populated as we go: + // an array of values (opout) that is populated as we go: // 0 = 0 // 1 = O1 // 2 = O2 // 3 = O3 - // 4 = O4 + // 4 = (O4) // 5 = O1+O2 // 6 = O1+O3 // 7 = O2+O3 // - // This table encodes for operators 2-4 which of the 8 input values - // above is used: 1 bit for O2 and 3 bits for O3 and O4 - static u8 const s_algorithm_inputs[8] = + // 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 u16 const s_algorithm_ops[8+4] = { - // OP2 OP3 OP4 - 1 | (2 << 1) | (3 << 4), - 0 | (5 << 1) | (3 << 4), - 0 | (2 << 1) | (6 << 4), - 1 | (0 << 1) | (7 << 4), - 1 | (0 << 1) | (3 << 4), - 1 | (1 << 1) | (1 << 4), - 1 | (0 << 1) | (0 << 4), - 0 | (0 << 1) | (0 << 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] }; - u8 algorithm = m_regs.algorithm(); - u8 algorithm_inputs = s_algorithm_inputs[algorithm]; + u32 algorithm_ops = s_algorithm_ops[m_regs.ch_algorithm(m_choffs)]; + + // populate the opout table s16 opout[8]; opout[0] = 0; - - // operator 1 has optional self-feedback - s16 modulation = 0; - u8 feedback = m_regs.feedback(); - if (feedback != 0) - modulation = (m_feedback[0] + m_feedback[1]) >> (10 - feedback); - - // compute the 14-bit volume/value of operator 1 and update the feedback - opout[1] = m_feedback_in = m_op1.compute_volume(modulation, am_offset); - - // no that the feedback has been computed, skip the rest if both pans are clear; - // no need to do all this work for nothing - if (m_regs.pan_left() == 0 && m_regs.pan_right() == 0) - return; + opout[1] = op1value; // compute the 14-bit volume/value of operator 2 - opout[2] = m_op2.compute_volume(opout[BIT(algorithm_inputs, 0, 1)] >> 1, am_offset); + opmod = opout[BIT(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 - opout[3] = m_op3.compute_volume(opout[BIT(algorithm_inputs, 1, 3)] >> 1, am_offset); + opmod = opout[BIT(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 - if (noise_state != 0) - opout[4] = m_op4.compute_noise_volume(noise_state, am_offset); + // value on the OPM; all algorithms consume OP4 output at a minimum + s32 result; + if (m_regs.noise_enable() && m_choffs == 7) + result = m_op[3]->compute_noise_volume(am_offset); else - opout[4] = m_op4.compute_volume(opout[BIT(algorithm_inputs, 4, 3)] >> 1, am_offset); + { + opmod = opout[BIT(algorithm_ops, 4, 3)] >> 1; + result = m_op[3]->compute_volume(m_op[3]->phase() + opmod, am_offset); + } + result >>= rshift; - // all algorithms consume OP4 output - s16 result = opout[4] >> rshift; + // optionally add OP1, OP2, OP3 + s32 clipmin = -clipmax - 1; + if (BIT(algorithm_ops, 7) != 0) + result = std::clamp(result + (opout[1] >> rshift), clipmin, clipmax); + if (BIT(algorithm_ops, 8) != 0) + result = std::clamp(result + (opout[2] >> rshift), clipmin, clipmax); + if (BIT(algorithm_ops, 9) != 0) + result = std::clamp(result + (opout[3] >> rshift), clipmin, clipmax); - // algorithms 4-7 add in OP2 output - if (algorithm >= 4) - { - s16 clipmin = -clipmax - 1; - result += opout[2] >> rshift; - result = std::clamp(result, clipmin, clipmax); + // add to the output + add_to_output(m_choffs, outputs, result); +} - // agorithms 5-7 add in OP3 output - if (algorithm >= 5) - { - result += opout[3] >> rshift; - result = std::clamp(result, clipmin, clipmax); - // algorithm 7 adds in OP1 output - if (algorithm == 7) - { - result += opout[1] >> rshift; - result = std::clamp(result, clipmin, clipmax); - } - } - } +//------------------------------------------------- +// output_rhythm_ch6 - special case output +// computation for OPL channel 6 in rhythm mode, +// which outputs a Bass Drum instrument +//------------------------------------------------- + +template +void ymfm_channel::output_rhythm_ch6(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const +{ + // AM amount is the same across all operators; compute it once + u32 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 + s32 opmod = 0; + u32 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 + s32 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 = BIT(m_regs.ch_algorithm(m_choffs), 0) ? 0 : (opout1 >> 1); + s32 result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift; // add to the output - if (m_regs.pan_left()) - lsum += result; - if (m_regs.pan_right()) - rsum += result; + add_to_output(m_choffs, outputs, result * 2); } //------------------------------------------------- -// lfo_am_offset - convert a 6/8-bit raw AM value -// into an amplitude offset based on sensitivity +// output_rhythm_ch7 - special case output +// computation for OPL channel 7 in rhythm mode, +// which outputs High Hat and Snare Drum +// instruments //------------------------------------------------- -// OPM version -template<> -u16 ymfm_channel::lfo_am_offset(u8 lfo_raw_am) const +template +void ymfm_channel::output_rhythm_ch7(u32 phase_select, s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const { - // shift value for AM sensitivity is [*, 0, 1, 2], - // mapping to values of [0, 23.9, 47.8, and 95.6dB] - u8 am_sensitivity = m_regs.lfo_am_sensitivity(); - if (am_sensitivity == 0) - return 0; - - // QUESTION: see OPN note below for the dB range mapping; it applies - // here as well + // AM amount is the same across all operators; compute it once + u32 am_offset = m_regs.lfo_am_offset(m_choffs); + u32 noise_state = BIT(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 + u32 phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select))); + s32 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 + u32 op13phase = m_op[0]->phase(); + phase = (0x100 << BIT(op13phase, 8)) ^ (noise_state << 8); + result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result = std::clamp(result, -clipmax - 1, clipmax); - // 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 lfo_raw_am << (am_sensitivity - 1); + // add to the output + add_to_output(m_choffs, outputs, result * 2); } -// OPN/OPNA version + +//------------------------------------------------- +// output_rhythm_ch8 - special case output +// computation for OPL channel 8 in rhythm mode, +// which outputs Tom Tom and Top Cymbal instruments +//------------------------------------------------- + template -u16 ymfm_channel::lfo_am_offset(u8 lfo_raw_am) const +void ymfm_channel::output_rhythm_ch8(u32 phase_select, s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const { - // shift value for AM sensitivity is [7, 3, 1, 0], - // mapping to values of [0, 1.4, 5.9, and 11.8dB] - u8 am_shift = (1 << (m_regs.lfo_am_sensitivity() ^ 3)) - 1; + // AM amount is the same across all operators; compute it once + u32 am_offset = m_regs.lfo_am_offset(m_choffs); - // 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. + // Tom Tom: this is just a single operator processed normally + s32 result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift; - // 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 (lfo_raw_am << 1) >> am_shift; + // Top Cymbal: this uses the envelope from operator 17 (channel 8), + // and the operator 13/17 phase select to compute the phase + u32 phase = 0x100 | (phase_select << 9); + result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result = std::clamp(result, -clipmax - 1, clipmax); + + // add to the output + add_to_output(m_choffs, outputs, result * 2); } @@ -1259,28 +2862,27 @@ template ymfm_engine_base::ymfm_engine_base(device_t &device) : m_device(device), m_env_counter(0), - m_lfo_counter(0), - m_noise_lfsr(0), - m_noise_counter(0), - m_noise_state(0), - m_noise_lfo(0), - m_lfo_am(0), m_status(0), m_clock_prescale(RegisterType::DEFAULT_PRESCALE), m_irq_mask(STATUS_TIMERA | STATUS_TIMERB), m_irq_state(0), - m_active_channels(0xffffffff), - m_modified_channels(0xffffffff), + m_active_channels(ALL_CHANNELS), + m_modified_channels(ALL_CHANNELS), m_prepare_count(0), m_busy_end(attotime::zero), m_timer{ nullptr, nullptr }, - m_irq_handler(device), - m_regdata(RegisterType::REGISTERS), - m_regs(m_regdata) + m_irq_handler(device) { // create the channels - for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++) - m_channel[chnum] = std::make_unique>(m_regs.channel_registers(chnum)); + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique>(*this, RegisterType::channel_offset(chnum)); + + // create the operators + for (int opnum = 0; opnum < OPERATORS; opnum++) + m_operator[opnum] = std::make_unique>(*this, RegisterType::operator_offset(opnum)); + + // do the initial operator assignment + assign_operators(); } @@ -1300,22 +2902,22 @@ void ymfm_engine_base::save(device_t &device) // save our data device.save_item(YMFM_NAME(m_env_counter)); - device.save_item(YMFM_NAME(m_lfo_counter)); - device.save_item(YMFM_NAME(m_noise_lfsr)); - device.save_item(YMFM_NAME(m_noise_counter)); - device.save_item(YMFM_NAME(m_noise_state)); - device.save_item(YMFM_NAME(m_noise_lfo)); - device.save_item(YMFM_NAME(m_lfo_am)); device.save_item(YMFM_NAME(m_status)); device.save_item(YMFM_NAME(m_clock_prescale)); device.save_item(YMFM_NAME(m_irq_mask)); device.save_item(YMFM_NAME(m_irq_state)); device.save_item(YMFM_NAME(m_busy_end)); - device.save_item(YMFM_NAME(m_regdata)); + + // save the register/family data + m_regs.save(device); // save channel data - for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++) + for (int chnum = 0; chnum < CHANNELS; chnum++) m_channel[chnum]->save(device, chnum); + + // save operator data + for (int opnum = 0; opnum < OPERATORS; opnum++) + m_operator[opnum]->save(device, opnum); } @@ -1329,19 +2931,20 @@ void ymfm_engine_base::reset() // reset all status bits set_reset_status(0, 0xff); - // clear all registers - std::fill_n(&m_regdata[0], m_regdata.size(), 0); + // 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); - // register type-specific initialization - m_regs.reset(); - // reset the channels for (auto &chan : m_channel) chan->reset(); + + // reset the operators + for (auto &op : m_operator) + op->reset(); } @@ -1351,39 +2954,41 @@ void ymfm_engine_base::reset() //------------------------------------------------- template -u32 ymfm_engine_base::clock(u8 chanmask) +u32 ymfm_engine_base::clock(u32 chanmask) { // 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 (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++) + for (int chnum = 0; chnum < CHANNELS; chnum++) if (BIT(chanmask, chnum)) - if (m_channel[chnum]->active()) + 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 count; low two bits are the subcount, which - // only counts to 3, so if it reaches 3, count one more time - m_env_counter++; - if (BIT(m_env_counter, 0, 2) == 3) - m_env_counter++; + // 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 (BIT(++m_env_counter, 0, 2) == RegisterType::EG_CLOCK_DIVIDER) + m_env_counter += 4 - RegisterType::EG_CLOCK_DIVIDER; // clock the noise generator - clock_noise(); - - // clock the LFO - s8 lfo_raw_pm = clock_lfo(); + s32 lfo_raw_pm = m_regs.clock_noise_and_lfo(); // now update the state of all the channels and operators - for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++) + for (int chnum = 0; chnum < CHANNELS; chnum++) if (BIT(chanmask, chnum)) - m_channel[chnum]->clock(m_env_counter, lfo_raw_pm, chnum == 2 && m_regs.multi_freq()); + 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; @@ -1396,20 +3001,51 @@ u32 ymfm_engine_base::clock(u8 chanmask) //------------------------------------------------- template -void ymfm_engine_base::output(s32 &lsum, s32 &rsum, u8 rshift, s16 clipmax, u8 chanmask) const +void ymfm_engine_base::output(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax, u32 chanmask) const { // mask out inactive channels chanmask &= m_active_channels; - // sum over all the desired channels - for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++) - if (BIT(chanmask, chnum)) - { - // noise must be non-zero to use noise on OP4, so if it is enabled, - // OR with 2 (since only the LSB is actually checked for the noise state) - u8 noise = (chnum == 7 && m_regs.noise_enabled()) ? (m_noise_state | 2) : 0; - m_channel[chnum]->output(m_lfo_am, noise, lsum, rsum, rshift, clipmax); - } + // 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 + u32 op13phase = m_operator[13]->phase(); + u32 op17phase = m_operator[17]->phase(); + u32 phase_select = (BIT(op13phase, 2) ^ BIT(op13phase, 7)) | BIT(op13phase, 3) | (BIT(op17phase, 5) ^ BIT(op17phase, 3)); + + // sum over all the desired channels + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (BIT(chanmask, chnum)) + { + if (chnum == 6) + m_channel[chnum]->output_rhythm_ch6(outputs, rshift, clipmax); + else if (chnum == 7) + m_channel[chnum]->output_rhythm_ch7(phase_select, outputs, rshift, clipmax); + else if (chnum == 8) + m_channel[chnum]->output_rhythm_ch8(phase_select, outputs, rshift, clipmax); + else if (m_channel[chnum]->is4op()) + m_channel[chnum]->output_4op(outputs, rshift, clipmax); + else + m_channel[chnum]->output_2op(outputs, rshift, clipmax); + } + } + else + { + // sum over all the desired channels + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (BIT(chanmask, chnum)) + { + if (m_channel[chnum]->is4op()) + m_channel[chnum]->output_4op(outputs, rshift, clipmax); + else + m_channel[chnum]->output_2op(outputs, rshift, clipmax); + } + } } @@ -1428,18 +3064,27 @@ void ymfm_engine_base::write(u16 regnum, u8 data) return; } - // most writes are passive, consumed only when needed - m_regs.write(regnum, data); - // for now just mark all channels as modified - m_modified_channels = 0xffffffff; + m_modified_channels = ALL_CHANNELS; - // handle writes to the keyon registers - if (regnum == RegisterType::REG_KEYON) + // most writes are passive, consumed only when needed + u32 keyon_channel; + u32 keyon_opmask; + if (m_regs.write(regnum, data, keyon_channel, keyon_opmask)) { - u8 chnum = m_regs.keyon_channel(); - if (chnum < RegisterType::CHANNELS) - m_channel[chnum]->keyonoff(m_regs.keyon_states()); + // handle writes to the keyon register(s) + if (keyon_channel < CHANNELS) + { + // normal channel on/off + m_channel[keyon_channel]->keyonoff(keyon_opmask, YMFM_KEYON_NORMAL); + } + else if (CHANNELS >= 9 && keyon_channel == RegisterType::YMFM_RHYTHM_CHANNEL) + { + // special case for the OPL rhythm channels + m_channel[6]->keyonoff(BIT(keyon_opmask, 4) ? 3 : 0, YMFM_KEYON_RHYTHM); + m_channel[7]->keyonoff(BIT(keyon_opmask, 0) | (BIT(keyon_opmask, 3) << 1), YMFM_KEYON_RHYTHM); + m_channel[8]->keyonoff(BIT(keyon_opmask, 2) | (BIT(keyon_opmask, 1) << 1), YMFM_KEYON_RHYTHM); + } } } @@ -1453,8 +3098,6 @@ template u8 ymfm_engine_base::status() const { u8 result = m_status & ~STATUS_BUSY; - - // synthesize the busy flag if we're still busy if (m_device.machine().time() < m_busy_end) result |= STATUS_BUSY; return result; @@ -1462,142 +3105,22 @@ u8 ymfm_engine_base::status() const //------------------------------------------------- -// clock_lfo - clock the LFO, handling clock -// division, depth, and waveform computations +// assign_operators - get the current mapping of +// operators to channels and assign them all //------------------------------------------------- -// OPM implementation -template<> -s8 ymfm_engine_base::clock_lfo() -{ - // 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 - u8 rate = m_regs.lfo_rate(); - u32 prev_counter = m_lfo_counter; - m_lfo_counter += (0x10 | BIT(rate, 0, 4)) << BIT(rate, 4, 4); - u8 lfo = BIT(m_lfo_counter, 22, 8); - - // compute the AM and PM values based on the waveform - // AM is 8-bit unsigned; PM is 8-bit signed; waveforms are adjusted - // to match the pictures in the application manual - u8 am; - s8 pm; - switch (m_regs.lfo_waveform()) - { - // sawtooth - default: - case 0: - am = lfo ^ 0xff; - pm = lfo; - break; - - // square wave - case 1: - am = BIT(lfo, 7) ? 0 : 0xff; - pm = am ^ 0x80; - break; - - // triangle wave - case 2: - am = BIT(lfo, 7) ? (lfo << 1) : (~lfo << 1); - pm = BIT(lfo, 6) ? am : ~am; - break; - - // noise: - case 3: - // QUESTION: this behavior is surmised but not yet verified: - // LFO noise value is accumulated over 8 bits of LFSR and - // clocked as the LFO value transitions - if (BIT(m_lfo_counter ^ prev_counter, 22, 8) != 0) - m_noise_lfo = m_noise_lfsr & 0xff; - am = m_noise_lfo; - pm = am ^ 0x80; - break; - } - - // apply depth to the AM value and store for later - m_lfo_am = (am * m_regs.lfo_am_depth()) >> 7; - - // apply depth to the PM value and return it - return (pm * m_regs.lfo_pm_depth()) >> 7; -} - -// OPN/OPNA implementation template -s8 ymfm_engine_base::clock_lfo() -{ - // if not enabled, quick exit with 0s - if (!m_regs.lfo_enabled()) - { - 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 u8 const lfo_max_count[8] = { 109, 78, 72, 68, 63, 45, 9, 6 }; - u8 subcount = u8(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[m_regs.lfo_rate()]) - m_lfo_counter += subcount ^ 0xff; - - // AM value is 7 bits, staring at bit 8; grab the low 6 directly - m_lfo_am = BIT(m_lfo_counter, 8, 6); - - // first half of the AM period (bit 6 == 0) is inverted - if (BIT(m_lfo_counter, 8+6) == 0) - m_lfo_am ^= 0x3f; - - // PM value is 5 bits, starting at bit 10; grab the low 3 directly - s8 pm = BIT(m_lfo_counter, 10, 3); - - // PM is reflected based on bit 3 - if (BIT(m_lfo_counter, 10+3)) - pm ^= 7; - - // PM is negated based on bit 4 - return BIT(m_lfo_counter, 10+4) ? -pm : pm; -} - - -//------------------------------------------------- -// clock_noise - clock the noise generator -//------------------------------------------------- - -// OPM implementation -template<> -void ymfm_engine_base::clock_noise() +void ymfm_engine_base::assign_operators() { - // base noise frequency is measured at 2x 1/2 FM frequency; this means - // each tick counts as two steps against the noise counter - u8 freq = m_regs.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; clock it here - // twice; 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 |= (BIT(m_noise_lfsr, 7) ^ BIT(m_noise_lfsr, 10) ^ 1) << 24; + typename RegisterType::operator_mapping map; + m_regs.operator_map(map); - // compare against the frequency and latch when we exceed it - if (m_noise_counter++ >= freq) + for (int chnum = 0; chnum < CHANNELS; chnum++) + for (int index = 0; index < 4; index++) { - m_noise_counter = 0; - m_noise_state = BIT(m_noise_lfsr, 7); + u32 opnum = BIT(map.chan[chnum], 8 * index, 8); + m_channel[chnum]->assign(index, (opnum == 0xff) ? nullptr : m_operator[opnum].get()); } - } -} - -// OPN/OPNA implementation -template -void ymfm_engine_base::clock_noise() -{ - // OPN does not have a noise generator } @@ -1607,13 +3130,13 @@ void ymfm_engine_base::clock_noise() //------------------------------------------------- template -void ymfm_engine_base::update_timer(u8 tnum, u8 enable) +void ymfm_engine_base::update_timer(u32 tnum, u32 enable) { // if the timer is live, but not currently enabled, set the timer - if (enable && !m_timer[tnum]->enabled()) + if (enable && !m_timer[tnum]->enable()) { - // each timer clock is n channels * 4 operators * prescale factor (2/3/6) - u32 clockscale = RegisterType::CHANNELS * 4 * m_clock_prescale; + // each timer clock is n operators * prescale factor (2/3/6) + u32 clockscale = OPERATORS * m_clock_prescale; // period comes from the registers, and is different for each u32 period = (tnum == 0) ? (1024 - m_regs.timer_a_value()) : 16 * (256 - m_regs.timer_b_value()); @@ -1644,9 +3167,9 @@ TIMER_CALLBACK_MEMBER(ymfm_engine_base::timer_handler) // if timer A fired in CSM mode, trigger CSM on all relevant channels if (param == 0 && m_regs.csm()) - for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++) + for (int chnum = 0; chnum < CHANNELS; chnum++) if (BIT(RegisterType::CSM_TRIGGER_MASK, chnum)) - m_channel[chnum]->keyon_csm(); + m_channel[chnum]->keyonoff(1, YMFM_KEYON_CSM); // reset update_timer(param, 1); @@ -1683,6 +3206,12 @@ TIMER_CALLBACK_MEMBER(ymfm_engine_base::check_interrupts) u8 old_state = m_irq_state; m_irq_state = ((m_status & m_irq_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_irq_handler.isnull()) m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE); @@ -1698,21 +3227,41 @@ template TIMER_CALLBACK_MEMBER(ymfm_engine_base::synced_mode_w) { // actually write the mode register now - m_regs.write(RegisterType::REG_MODE, param); + u32 dummy1, dummy2; + m_regs.write(RegisterType::REG_MODE, param, 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 + u8 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 + update_timer(1, m_regs.load_timer_b()); + update_timer(0, m_regs.load_timer_a()); + } +} - // reset timer status - if (m_regs.reset_timer_b()) - set_reset_status(0, STATUS_TIMERB); - if (m_regs.reset_timer_a()) - set_reset_status(0, STATUS_TIMERA); - // load timers - update_timer(1, m_regs.load_timer_b()); - update_timer(0, m_regs.load_timer_a()); -} +//********************************************************* +// EXPLICIT TEMPLATE INSTANTIATION +//********************************************************* -// Explicit template instantiation template class ymfm_engine_base; template class ymfm_engine_base; template class ymfm_engine_base; +template class ymfm_engine_base; +template class ymfm_engine_base; +template class ymfm_engine_base; +template class ymfm_engine_base; +template class ymfm_engine_base; diff --git a/src/devices/sound/ymfm.h b/src/devices/sound/ymfm.h index 06bde003c9b..b452e15b944 100644 --- a/src/devices/sound/ymfm.h +++ b/src/devices/sound/ymfm.h @@ -6,71 +6,6 @@ #pragma once -// -// Implementation notes: -// -// -// REGISTER CLASSES -// -// OPM and OPN are very closely related, and thus share a common engine -// and implementation. Differentiation is provided by the various registers -// classes, which are specified as template parameters to the shared -// implementation. -// -// There are currently three register classes: -// -// ymopm_registers: OPM (YM2151) -// ymopn_registers: OPN (YM2203) -// ymopna_registers: OPNA (YM2608) / OPNB (YM2610/B) / OPN2 (YM2612/YM3438) -// -// -// FREQUENCIES -// -// One major difference between OPM and OPN 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. -// -// OPN, on the other hand, specifies frequencies via a 3-bit 'block' -// just as on OPM, but combined with an 11-bit 'frequency number' or -// 'fnum', which is directly shifted by the block to produce the step -// value. So essentially, OPN makes the user do the conversion from -// note value to phase increment, while OPM is programmed in a more -// 'musical' way, specifying notes and cents. -// -// Interally, 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 -// -// OPN: [3-bit block]:[11-bit fnum] = 14 bits total -// -// Template specialization in functions that interpret the 'block_freq' -// value is used to deconstruct it appropriately (specifically, see -// clock_phase). -// -// -// 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 depth control. It did bring back the channel-level -// sensitivity controls and the operator-level on/off control. -// - //********************************************************* // MACROS @@ -83,70 +18,210 @@ #define YMFM_NAME(x) x, "ymfm." #x +//********************************************************* +// GLOBAL ENUMERATORS +//********************************************************* + +enum ymfm_envelope_state : u32 +{ + YMFM_ENV_DEPRESS = 0, + YMFM_ENV_ATTACK = 1, + YMFM_ENV_DECAY = 2, + YMFM_ENV_SUSTAIN = 3, + YMFM_ENV_RELEASE = 4, + YMFM_ENV_STATES = 5 +}; + + +//********************************************************* +// GLOBAL HELPERS +//********************************************************* + +// 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 + +//------------------------------------------------- +// ymfm_encode_fp - given a 32-bit signed input +// value, convert it to a signed 3.10 floating- +// point value +//------------------------------------------------- + +inline s16 ymfm_encode_fp(s32 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 + s32 scanvalue = value ^ (s32(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 + s32 mantissa = value >> (exponent - 1); + + // assemble into final form, inverting the sign + return ((exponent << 10) | (mantissa & 0x3ff)) ^ 0x200; +} + + +//------------------------------------------------- +// ymfm_decode_fp - given a 3.10 floating-point +// value, convert it to a signed 16-bit value +//------------------------------------------------- + +inline s16 ymfm_decode_fp(s16 value) +{ + // invert the sign and the exponent + value ^= 0x1e00; + + // shift mantissa up to 16 bits then apply inverted exponent + return s16(value << 6) >> BIT(value, 10, 3); +} + + +//------------------------------------------------- +// ymfm_roundtrip_fp - compute the result of a +// round trip through the encode/decode process +// above +//------------------------------------------------- + +inline s16 ymfm_roundtrip_fp(s32 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 + s32 scanvalue = value ^ (s32(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; + return (value >> exponent) << exponent; +} + //********************************************************* // REGISTER CLASSES //********************************************************* +// ======================> ymfm_opdata_cache + +// this class holds data that is computed once at the start of clocking +// and remains static during subsequent sound generation +struct ymfm_opdata_cache +{ + // set phase_step to this value to recalculate it each sample; needed + // in the case of PM LFO changes + static constexpr u32 PHASE_STEP_DYNAMIC = 1; + + u16 const *waveform; // base of sine table + u32 phase_step; // phase step, or PHASE_STEP_DYNAMIC if PM is active + u32 total_level; // total level * 8 + KSL + u32 block_freq; // raw block frequency value (used to compute phase_step) + s32 detune; // detuning value (used to compute phase_step) + u32 multiple; // multiple value (x.1, used to compute phase_step) + u32 eg_sustain; // sustain level, shifted up to envelope values + u8 eg_rate[YMFM_ENV_STATES]; // envelope rate, including KSR +}; + + // ======================> ymfm_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 ymfm_registers_base { -protected: - // constructor - ymfm_registers_base(std::vector ®data, u16 chbase = 0, u16 opbase = 0) : - m_chbase(chbase), - m_opbase(opbase), - m_regdata(regdata) - { - } - public: - // system-wide registers that aren't universally supported - u8 noise_frequency() const /* 5 bits */ { return 0; } // not on OPN,OPNA - u8 noise_enabled() const /* 1 bit */ { return 0; } // not on OPN,OPNA - u8 lfo_enabled() const /* 1 bit */ { return 0; } // not on OPM,OPN - u8 lfo_rate() const /*3-8 bits */ { return 0; } // not on OPN - u8 lfo_waveform() const /* 2 bits */ { return 0; } // not on OPN,OPNA - u8 lfo_pm_depth() const /* 7 bits */ { return 0; } // not on OPN,OPNA - u8 lfo_am_depth() const /* 7 bits */ { return 0; } // not on OPN,OPNA - u8 multi_freq() const /* 1 bit */ { return 0; } // not on OPM - u16 multi_block_freq0() const /* 14 bits */ { return 0; } // not on OPM - u16 multi_block_freq1() const /* 14 bits */ { return 0; } // not on OPM - u16 multi_block_freq2() const /* 14 bits */ { return 0; } // not on OPM - - // per-channel registers that aren't universally supported - u8 pan_right() const /* 1 bit */ { return 1; } // not on OPN - u8 pan_left() const /* 1 bit */ { return 1; } // not on OPN - u8 lfo_pm_sensitivity() const /* 3 bits */ { return 0; } // not on OPN - u8 lfo_am_sensitivity() const /* 2 bits */ { return 0; } // not on OPN - - // per-operator registers that aren't universally supported - u8 lfo_am_enabled() const /* 1 bit */ { return 0; } // not on OPN - u8 detune2() const /* 2 bits */ { return 0; } // not on OPN,OPN2 - u8 ssg_eg_enabled() const /* 1 bit */ { return 0; } // not on OPM - u8 ssg_eg_mode() const /* 1 bit */ { return 0; } // not on OPM + // this value is returned from the write() function for rhythm channels + static constexpr u32 YMFM_RHYTHM_CHANNEL = 0xff; + + // this is the size of a full sin waveform + static constexpr u32 WAVEFORM_LENGTH = 0x400; + + // + // the following constants need to be defined per family: + // u32 OUTPUTS: The number of outputs exposed (1-4) + // u32 CHANNELS: The number of channels on the chip + // u32 ALL_CHANNELS: A bitmask of all channels + // u32 OPERATORS: The number of operators on the chip + // bool DYNAMIC_OPS: True if ops/channel can be changed at runtime + // u32 WAVEFORMS: The number of waveforms offered + // u32 REGISTERS: The number of 8-bit registers allocated + // u32 REG_MODE: The address of the "mode" register controlling timers + // u32 DEFAULT_PRESCALE: The starting clock prescale + // u32 EG_CLOCK_DIVIDER: The clock divider of the envelope generator + // bool EG_HAS_DEPRESS: True if the chip has a DP ("depress"?) envelope stage + // bool EG_HAS_SSG: True if the chip has SSG envelope support + // bool MODULATOR_DELAY: True if the modulator is delayed by 1 sample (OPL pre-OPL3) + // u32 CSM_TRIGGER_MASK: Mask of channels to trigger in CSM mode + // u8 STATUS_TIMERA: Status bit to set when timer A fires + // u8 STATUS_TIMERB: Status bit to set when tiemr B fires + // u8 STATUS_BUSY: Status bit to set when the chip is busy + // u8 STATUS_IRQ: Status bit to set when an IRQ is signalled + // + + // system-wide register defaults + u32 status_mask() const { return 0; } // OPL only + u32 irq_reset() const { return 0; } // OPL only + u32 noise_enable() const { return 0; } // OPM only + u32 rhythm_enable() const { return 0; } // OPL only + + // per-operator register defaults + u32 op_ssg_eg_enable(u32 opoffs) const { return 0; } // OPN(A) only + u32 op_ssg_eg_mode(u32 opoffs) const { return 0; } // OPN(A) only protected: - // return a bitfield extracted from a byte - u8 sysbyte(u16 offset, u8 start, u8 count) const + // helper to encode four operator numbers into a 32-bit value in the + // operator maps for each register class + static constexpr u32 operator_list(u8 o1 = 0xff, u8 o2 = 0xff, u8 o3 = 0xff, u8 o4 = 0xff) { - return BIT(m_regdata[offset], start, count); + return o1 | (o2 << 8) | (o3 << 16) | (o4 << 24); } - u8 chbyte(u16 offset, u8 start, u8 count) const { return sysbyte(offset + m_chbase, start, count); } - u8 opbyte(u16 offset, u8 start, u8 count) const { return sysbyte(offset + m_opbase, start, count); } - // return a bitfield extracted from a pair of bytes, MSBs listed first - u16 sysword(u16 offset1, u8 start1, u8 count1, u16 offset2, u8 start2, u8 count2) const + // helper to apply KSR to the raw ADSR rate, ignoring ksr if the + // raw value is 0, and clamping to 63 + static constexpr u32 effective_rate(u32 rawrate, u32 ksr) { - return (sysbyte(offset1, start1, count1) << count2) | sysbyte(offset2, start2, count2); + return (rawrate == 0) ? 0 : std::min(rawrate + ksr, 63); } - u16 chword(u16 offset1, u8 start1, u8 count1, u16 offset2, u8 start2, u8 count2) const { return sysword(offset1 + m_chbase, start1, count1, offset2 + m_chbase, start2, count2); } - - // internal state - u16 m_chbase; // base offset for channel-specific data - u16 m_opbase; // base offset for operator-specific data - std::vector &m_regdata; // reference to the raw data }; @@ -180,7 +255,7 @@ protected: // ------xx W // // Per-channel registers (channel in address bits 0-2) -// 20-27 xx------ Pan right +// 20-27 x------- Pan right // -x------ Pan left // --xxx--- Feedback level for operator 1 (0-7) // -----xxx Operator connection algorithm (0-7) @@ -202,124 +277,166 @@ protected: // E0-FF xxxx---- Sustain level (0-15) // ----xxxx Release rate (0-15) // +// Internal (fake) registers: +// 19 -xxxxxxx AM depth +// 1A -xxxxxxx PM depth +// class ymopm_registers : public ymfm_registers_base { + // LFO waveforms are 256 entries long + static constexpr u32 LFO_WAVEFORM_LENGTH = 256; + public: // constants - static constexpr u8 DEFAULT_PRESCALE = 2; - static constexpr u8 CHANNELS = 8; - static constexpr u8 CSM_TRIGGER_MASK = 0xff; - static constexpr u16 REGISTERS = 0x100; - static constexpr u16 REG_MODE = 0x14; - static constexpr u16 REG_KEYON = 0x08; + static constexpr u32 OUTPUTS = 2; + static constexpr u32 CHANNELS = 8; + static constexpr u32 ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr u32 OPERATORS = CHANNELS * 4; + static constexpr bool DYNAMIC_OPS = false; + static constexpr u32 WAVEFORMS = 1; + static constexpr u32 REGISTERS = 0x100; + static constexpr u32 REG_MODE = 0x14; + static constexpr u32 DEFAULT_PRESCALE = 2; + static constexpr u32 EG_CLOCK_DIVIDER = 3; + static constexpr bool EG_HAS_DEPRESS = false; + static constexpr bool EG_HAS_SSG = false; + static constexpr bool MODULATOR_DELAY = false; + static constexpr u32 CSM_TRIGGER_MASK = ALL_CHANNELS; + static constexpr u8 STATUS_TIMERA = 0x01; + static constexpr u8 STATUS_TIMERB = 0x02; + static constexpr u8 STATUS_BUSY = 0x80; + static constexpr u8 STATUS_IRQ = 0; // constructor - ymopm_registers(std::vector ®data, u16 chbase = 0, u16 opbase = 0) : - ymfm_registers_base(regdata, chbase, opbase) - { - } + ymopm_registers(); - // return channel/operator number - u8 chnum() const { return BIT(m_chbase, 0, 3); } - u8 opnum() const { return BIT(m_opbase, 4) | (BIT(m_opbase, 3) << 1); } + // register for save states + void save(device_t &device); - // reset state to default values - void reset() + // reset to initial state + void reset(); + + // map channel number to register offset + static constexpr u32 channel_offset(u32 chnum) { - // 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; + assert(chnum < CHANNELS); + return chnum; } - // write access - void write(u16 index, u8 data) + // map operator number to register offset + static constexpr u32 operator_offset(u32 opnum) { - // 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 + BIT(data, 7)] = data; - else if (index != 0x1a) - m_regdata[index] = data; + assert(opnum < OPERATORS); + return opnum; } - // create a new version of ourself with a different channel/operator base - ymopm_registers channel_registers(u8 chnum) { return ymopm_registers(m_regdata, channel_offset(chnum)); } - ymopm_registers operator_registers(u8 opnum) { return ymopm_registers(m_regdata, m_chbase, m_chbase + operator_offset(opnum)); } + // return an array of operator indices for each channel + struct operator_mapping { u32 chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // handle writes to the register array + bool write(u16 index, u8 data, u32 &chan, u32 &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + s32 clock_noise_and_lfo(); + + // reset the LFO + void reset_lfo() { m_lfo_counter = 0; } + + // return the AM offset from LFO for the given channel + u32 lfo_am_offset(u32 choffs) const; + + // return the current noise state, gated by the noise clock + u32 noise_state() const { return m_noise_state; } + + // caching helpers + void cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache); + + // compute the phase step, given a PM value + u32 compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm); + + // log a key-on event + void log_keyon(u32 choffs, u32 opoffs); // system-wide registers - u8 test() const /* 8 bits */ { return sysbyte(0x01, 0, 8); } - u8 keyon_states() const /* 4 bits */ { return sysbyte(0x08, 3, 4); } - u8 keyon_channel() const /* 3 bits */ { return sysbyte(0x08, 0, 3); } - u8 noise_frequency() const /* 5 bits */ { return sysbyte(0x0f, 0, 5); } - u8 noise_enabled() const /* 1 bit */ { return sysbyte(0x0f, 7, 1); } - u16 timer_a_value() const /* 10 bits */ { return sysword(0x10, 0, 8, 0x11, 0, 2); } - u8 timer_b_value() const /* 8 bits */ { return sysbyte(0x12, 0, 8); } - u8 csm() const /* 1 bit */ { return sysbyte(0x14, 7, 1); } - u8 reset_timer_b() const /* 1 bit */ { return sysbyte(0x14, 5, 1); } - u8 reset_timer_a() const /* 1 bit */ { return sysbyte(0x14, 4, 1); } - u8 enable_timer_b() const /* 1 bit */ { return sysbyte(0x14, 3, 1); } - u8 enable_timer_a() const /* 1 bit */ { return sysbyte(0x14, 2, 1); } - u8 load_timer_b() const /* 1 bit */ { return sysbyte(0x14, 1, 1); } - u8 load_timer_a() const /* 1 bit */ { return sysbyte(0x14, 0, 1); } - u8 lfo_rate() const /* 8 bits */ { return sysbyte(0x18, 0, 8); } - u8 lfo_am_depth() const /* 7 bits */ { return sysbyte(0x19, 0, 7); } - u8 lfo_pm_depth() const /* 7 bits */ { return sysbyte(0x1a, 0, 7); } - u8 lfo_waveform() const /* 2 bits */ { return sysbyte(0x1b, 0, 2); } + u32 test() const { return byte(0x01, 0, 8); } + u32 noise_frequency() const { return byte(0x0f, 0, 5); } + u32 noise_enable() const { return byte(0x0f, 7, 1); } + u32 timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); } + u32 timer_b_value() const { return byte(0x12, 0, 8); } + u32 csm() const { return byte(0x14, 7, 1); } + u32 reset_timer_b() const { return byte(0x14, 5, 1); } + u32 reset_timer_a() const { return byte(0x14, 4, 1); } + u32 enable_timer_b() const { return byte(0x14, 3, 1); } + u32 enable_timer_a() const { return byte(0x14, 2, 1); } + u32 load_timer_b() const { return byte(0x14, 1, 1); } + u32 load_timer_a() const { return byte(0x14, 0, 1); } + u32 lfo_rate() const { return byte(0x18, 0, 8); } + u32 lfo_am_depth() const { return byte(0x19, 0, 7); } + u32 lfo_pm_depth() const { return byte(0x1a, 0, 7); } + u32 lfo_waveform() const { return byte(0x1b, 0, 2); } // per-channel registers - u8 pan_right() const /* 1 bit */ { return chbyte(0x20, 7, 1); } - u8 pan_left() const /* 1 bit */ { return chbyte(0x20, 6, 1); } - u8 feedback() const /* 3 bits */ { return chbyte(0x20, 3, 3); } - u8 algorithm() const /* 3 bits */ { return chbyte(0x20, 0, 3); } - u16 block_freq() const /* 13 bits */ { return chword(0x28, 0, 7, 0x30, 2, 6); } - u8 lfo_pm_sensitivity() const /* 3 bits */ { return chbyte(0x38, 4, 3); } - u8 lfo_am_sensitivity() const /* 2 bits */ { return chbyte(0x38, 0, 2); } + u32 ch_output_any(u32 choffs) const { return byte(0x20, 6, 2, choffs); } + u32 ch_output_0(u32 choffs) const { return byte(0x20, 6, 1, choffs); } + u32 ch_output_1(u32 choffs) const { return byte(0x20, 7, 1, choffs); } + u32 ch_output_2(u32 choffs) const { return 0; } + u32 ch_output_3(u32 choffs) const { return 0; } + u32 ch_feedback(u32 choffs) const { return byte(0x20, 3, 3, choffs); } + u32 ch_algorithm(u32 choffs) const { return byte(0x20, 0, 3, choffs); } + u32 ch_block_freq(u32 choffs) const { return word(0x28, 0, 7, 0x30, 2, 6, choffs); } + u32 ch_lfo_pm_sens(u32 choffs) const { return byte(0x38, 4, 3, choffs); } + u32 ch_lfo_am_sens(u32 choffs) const { return byte(0x38, 0, 2, choffs); } // per-operator registers - u8 detune() const /* 3 bits */ { return opbyte(0x40, 4, 3); } - u8 multiple() const /* 4 bits */ { return opbyte(0x40, 0, 4); } - u8 total_level() const /* 7 bits */ { return opbyte(0x60, 0, 7); } - u8 ksr() const /* 2 bits */ { return opbyte(0x80, 6, 2); } - u8 attack_rate() const /* 5 bits */ { return opbyte(0x80, 0, 5); } - u8 lfo_am_enabled() const /* 1 bit */ { return opbyte(0xa0, 7, 1); } - u8 decay_rate() const /* 5 bits */ { return opbyte(0xa0, 0, 5); } - u8 detune2() const /* 2 bits */ { return opbyte(0xc0, 6, 2); } - u8 sustain_rate() const /* 5 bits */ { return opbyte(0xc0, 0, 5); } - u8 sustain_level() const /* 4 bits */ { return opbyte(0xe0, 4, 4); } - u8 release_rate() const /* 4 bits */ { return opbyte(0xe0, 0, 4); } - - // LFO is always enabled - u8 lfo_enabled() const { return 1; } - - // special helper for generically getting the attack/decay/statain/release rates - u8 adsr_rate(u8 state) const - { - // attack/decay/sustain are identical - if (state < 3) - return opbyte(0x80 + (state << 5), 0, 5); + u32 op_detune(u32 opoffs) const { return byte(0x40, 4, 3, opoffs); } + u32 op_multiple(u32 opoffs) const { return byte(0x40, 0, 4, opoffs); } + u32 op_total_level(u32 opoffs) const { return byte(0x60, 0, 7, opoffs); } + u32 op_ksr(u32 opoffs) const { return byte(0x80, 6, 2, opoffs); } + u32 op_attack_rate(u32 opoffs) const { return byte(0x80, 0, 5, opoffs); } + u32 op_lfo_am_enable(u32 opoffs) const { return byte(0xa0, 7, 1, opoffs); } + u32 op_decay_rate(u32 opoffs) const { return byte(0xa0, 0, 5, opoffs); } + u32 op_detune2(u32 opoffs) const { return byte(0xc0, 6, 2, opoffs); } + u32 op_sustain_rate(u32 opoffs) const { return byte(0xc0, 0, 5, opoffs); } + u32 op_sustain_level(u32 opoffs) const { return byte(0xe0, 4, 4, opoffs); } + u32 op_release_rate(u32 opoffs) const { return byte(0xe0, 0, 4, opoffs); } - // release encodes 4 bits and expands them - else - return opbyte(0xe0, 0, 4) * 2 + 1; +protected: + // return a bitfield extracted from a byte + u32 byte(u32 offset, u32 start, u32 count, u32 extra_offset = 0) const + { + return BIT(m_regdata[offset + extra_offset], start, count); } -protected: - // convert a channel number into a register offset; channel goes into the low 3 bits - static constexpr u8 channel_offset(u8 chnum) { return BIT(chnum, 0, 3); } + // return a bitfield extracted from a pair of bytes, MSBs listed first + u32 word(u32 offset1, u32 start1, u32 count1, u32 offset2, u32 start2, u32 count2, u32 extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } - // convert an operator number into a register offset; operator goes into bits 3-4 - static constexpr u8 operator_offset(u8 opnum) { return (BIT(opnum, 0) << 4) | (BIT(opnum, 1) << 3); } + // internal state + u32 m_lfo_counter; // LFO counter + u32 m_noise_lfsr; // noise LFSR state + u8 m_noise_counter; // noise counter + u8 m_noise_state; // latched noise state + u8 m_noise_lfo; // latched LFO noise value + u8 m_lfo_am; // current LFO AM value + u8 m_regdata[REGISTERS]; // register data + s16 m_lfo_waveform[4][LFO_WAVEFORM_LENGTH]; // LFO waveforms; AM in low 8, PM in upper 8 + u16 m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms }; -// ======================> ymopn_registers +// ======================> ymopn_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 @@ -336,403 +453,832 @@ protected: // ---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 ---xxxxx Decay 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) // -// Per-channel registers (channel in address bits 0-1) -// 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) -// // 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) +// -class ymopn_registers : public ymfm_registers_base +template +class ymopn_registers_base : public ymfm_registers_base { public: // constants - static constexpr u8 DEFAULT_PRESCALE = 6; - static constexpr u8 CHANNELS = 3; - static constexpr u8 CSM_TRIGGER_MASK = 1 << 2; - static constexpr u16 REGISTERS = 0x100; - static constexpr u16 REG_MODE = 0x27; - static constexpr u16 REG_KEYON = 0x28; + static constexpr u32 OUTPUTS = IsOpnA ? 2 : 1; + static constexpr u32 CHANNELS = IsOpnA ? 6 : 3; + static constexpr u32 ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr u32 OPERATORS = CHANNELS * 4; + static constexpr bool DYNAMIC_OPS = false; + static constexpr u32 WAVEFORMS = 1; + static constexpr u32 REGISTERS = IsOpnA ? 0x200 : 0x100; + static constexpr u32 REG_MODE = 0x27; + static constexpr u32 DEFAULT_PRESCALE = 6; + static constexpr u32 EG_CLOCK_DIVIDER = 3; + static constexpr bool EG_HAS_DEPRESS = false; + static constexpr bool EG_HAS_SSG = true; + static constexpr bool MODULATOR_DELAY = false; + static constexpr u32 CSM_TRIGGER_MASK = 1 << 2; + static constexpr u8 STATUS_TIMERA = 0x01; + static constexpr u8 STATUS_TIMERB = 0x02; + static constexpr u8 STATUS_BUSY = 0x80; + static constexpr u8 STATUS_IRQ = 0; // constructor - ymopn_registers(std::vector ®data, u16 chbase = 0, u16 opbase = 0) : - ymfm_registers_base(regdata, chbase, opbase) - { - } + ymopn_registers_base(); - // return channel/operator number - u8 chnum() const { return BIT(m_chbase, 0, 2); } - u8 opnum() const { return BIT(m_opbase, 3) | (BIT(m_opbase, 2) << 1); } + // register for save states + void save(device_t &device); - // reset state to default values - void reset() + // reset to initial state + void reset(); + + // map channel number to register offset + static constexpr u32 channel_offset(u32 chnum) { + assert(chnum < CHANNELS); + if (!IsOpnA) + return chnum; + else + return (chnum % 3) + 0x100 * (chnum / 3); } - // write access - void write(u16 index, u8 data) + // map operator number to register offset + static constexpr u32 operator_offset(u32 opnum) { - // writes in the 0xa0-af/0x1a0-af region are handled as latched pairs - // borrow unused registers 0xb8-bf/0x1b8-bf as temporary holding locations - if ((index & 0xf0) == 0xa0) - { - u16 latchindex = (index & 0x100) | 0xb8 | (BIT(index, 3) << 2) | BIT(index, 0, 2); - - // writes to the upper half just latch (only low 6 bits matter) - if (BIT(index, 2)) - m_regdata[latchindex] = data | 0x80; - - // writes to the lower half only commit if the latch is there - else if (BIT(m_regdata[latchindex], 7)) - { - m_regdata[index | 4] = m_regdata[latchindex] & 0x3f; - m_regdata[latchindex] = 0; - } - } - - // everything else is normal - m_regdata[index] = data; + assert(opnum < OPERATORS); + if (!IsOpnA) + return opnum + opnum / 3; + else + return (opnum % 12) + ((opnum % 12) / 3) + 0x100 * (opnum / 12); } - // create a new version of ourself with a different channel/operator base - ymopn_registers channel_registers(u8 chnum) { return ymopn_registers(m_regdata, channel_offset(chnum)); } - ymopn_registers operator_registers(u8 opnum) { return ymopn_registers(m_regdata, m_chbase, m_chbase + operator_offset(opnum)); } + // return an array of operator indices for each channel + struct operator_mapping { u32 chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // handle writes to the register array + bool write(u16 index, u8 data, u32 &chan, u32 &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + s32 clock_noise_and_lfo(); + + // reset the LFO + void reset_lfo() { m_lfo_counter = 0; } + + // return the AM offset from LFO for the given channel + u32 lfo_am_offset(u32 choffs) const; + + // return LFO/noise states + u32 noise_state() const { return 0; } + + // caching helpers + void cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache); + + // compute the phase step, given a PM value + u32 compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm); + + // log a key-on event + void log_keyon(u32 choffs, u32 opoffs); // system-wide registers - u8 test() const /* 8 bits */ { return sysbyte(0x21, 0, 8); } - u16 timer_a_value() const /* 10 bits */ { return sysword(0x24, 0, 8, 0x25, 0, 2); } - u8 timer_b_value() const /* 8 bits */ { return sysbyte(0x26, 0, 8); } - u8 csm() const /* 2 bits */ { return (sysbyte(0x27, 6, 2) == 2); } - u8 multi_freq() const /* 2 bits */ { return (sysbyte(0x27, 6, 2) != 0); } - u8 reset_timer_b() const /* 1 bit */ { return sysbyte(0x27, 5, 1); } - u8 reset_timer_a() const /* 1 bit */ { return sysbyte(0x27, 4, 1); } - u8 enable_timer_b() const /* 1 bit */ { return sysbyte(0x27, 3, 1); } - u8 enable_timer_a() const /* 1 bit */ { return sysbyte(0x27, 2, 1); } - u8 load_timer_b() const /* 1 bit */ { return sysbyte(0x27, 1, 1); } - u8 load_timer_a() const /* 1 bit */ { return sysbyte(0x27, 0, 1); } - u8 keyon_states() const /* 4 bits */ { return sysbyte(0x28, 4, 4); } - u8 keyon_channel() const /* 2 bits */ { return sysbyte(0x28, 0, 2); } - u16 multi_block_freq0() const /* 14 bits */ { return sysword(0xac, 0, 6, 0xa8, 0, 8); } - u16 multi_block_freq1() const /* 14 bits */ { return sysword(0xad, 0, 6, 0xa9, 0, 8); } - u16 multi_block_freq2() const /* 14 bits */ { return sysword(0xae, 0, 6, 0xaa, 0, 8); } + u32 test() const { return byte(0x21, 0, 8); } + u32 lfo_enable() const { return IsOpnA ? byte(0x22, 3, 1) : 0; } + u32 lfo_rate() const { return IsOpnA ? byte(0x22, 0, 3) : 0; } + u32 timer_a_value() const { return word(0x24, 0, 8, 0x25, 0, 2); } + u32 timer_b_value() const { return byte(0x26, 0, 8); } + u32 csm() const { return (byte(0x27, 6, 2) == 2); } + u32 multi_freq() const { return (byte(0x27, 6, 2) != 0); } + u32 reset_timer_b() const { return byte(0x27, 5, 1); } + u32 reset_timer_a() const { return byte(0x27, 4, 1); } + u32 enable_timer_b() const { return byte(0x27, 3, 1); } + u32 enable_timer_a() const { return byte(0x27, 2, 1); } + u32 load_timer_b() const { return byte(0x27, 1, 1); } + u32 load_timer_a() const { return byte(0x27, 0, 1); } + u32 multi_block_freq(u32 num) const { return word(0xac, 0, 6, 0xa8, 0, 8, num); } // per-channel registers - u16 block_freq() const /* 14 bits */ { return chword(0xa4, 0, 6, 0xa0, 0, 8); } - u8 feedback() const /* 3 bits */ { return chbyte(0xb0, 3, 3); } - u8 algorithm() const /* 3 bits */ { return chbyte(0xb0, 0, 3); } + u32 ch_block_freq(u32 choffs) const { return word(0xa4, 0, 6, 0xa0, 0, 8, choffs); } + u32 ch_feedback(u32 choffs) const { return byte(0xb0, 3, 3, choffs); } + u32 ch_algorithm(u32 choffs) const { return byte(0xb0, 0, 3, choffs); } + u32 ch_output_any(u32 choffs) const { return IsOpnA ? byte(0xb4, 6, 2, choffs) : 1; } + u32 ch_output_0(u32 choffs) const { return IsOpnA ? byte(0xb4, 7, 1, choffs) : 1; } + u32 ch_output_1(u32 choffs) const { return IsOpnA ? byte(0xb4, 6, 1, choffs) : 0; } + u32 ch_output_2(u32 choffs) const { return 0; } + u32 ch_output_3(u32 choffs) const { return 0; } + u32 ch_lfo_am_sens(u32 choffs) const { return IsOpnA ? byte(0xb4, 4, 2, choffs) : 0; } + u32 ch_lfo_pm_sens(u32 choffs) const { return IsOpnA ? byte(0xb4, 0, 3, choffs) : 0; } // per-operator registers - u8 detune() const /* 3 bits */ { return opbyte(0x30, 4, 3); } - u8 multiple() const /* 4 bits */ { return opbyte(0x30, 0, 4); } - u8 total_level() const /* 8 bits */ { return opbyte(0x40, 0, 7); } - u8 ksr() const /* 2 bits */ { return opbyte(0x50, 6, 2); } - u8 attack_rate() const /* 5 bits */ { return opbyte(0x50, 0, 5); } - u8 decay_rate() const /* 5 bits */ { return opbyte(0x60, 0, 5); } - u8 sustain_rate() const /* 5 bits */ { return opbyte(0x70, 0, 5); } - u8 sustain_level() const /* 4 bits */ { return opbyte(0x80, 4, 4); } - u8 release_rate() const /* 4 bits */ { return opbyte(0x80, 0, 4); } - u8 ssg_eg_enabled() const /* 1 bit */ { return opbyte(0x90, 3, 1); } - u8 ssg_eg_mode() const /* 3 bits */ { return opbyte(0x90, 0, 3); } - - // special helper for generically getting the attack/decay/statain/release rates - u8 adsr_rate(u8 state) const - { - // attack/decay/sustain are identical - if (state < 3) - return opbyte(0x50 + (state << 4), 0, 5); + u32 op_detune(u32 opoffs) const { return byte(0x30, 4, 3, opoffs); } + u32 op_multiple(u32 opoffs) const { return byte(0x30, 0, 4, opoffs); } + u32 op_total_level(u32 opoffs) const { return byte(0x40, 0, 7, opoffs); } + u32 op_ksr(u32 opoffs) const { return byte(0x50, 6, 2, opoffs); } + u32 op_attack_rate(u32 opoffs) const { return byte(0x50, 0, 5, opoffs); } + u32 op_decay_rate(u32 opoffs) const { return byte(0x60, 0, 5, opoffs); } + u32 op_lfo_am_enable(u32 opoffs) const { return IsOpnA ? byte(0x60, 7, 1, opoffs) : 0; } + u32 op_sustain_rate(u32 opoffs) const { return byte(0x70, 0, 5, opoffs); } + u32 op_sustain_level(u32 opoffs) const { return byte(0x80, 4, 4, opoffs); } + u32 op_release_rate(u32 opoffs) const { return byte(0x80, 0, 4, opoffs); } + u32 op_ssg_eg_enable(u32 opoffs) const { return byte(0x90, 3, 1, opoffs); } + u32 op_ssg_eg_mode(u32 opoffs) const { return byte(0x90, 0, 3, opoffs); } - // release encodes 4 bits and expands them - else - return opbyte(0x80, 0, 4) * 2 + 1; +protected: + // return a bitfield extracted from a byte + u32 byte(u32 offset, u32 start, u32 count, u32 extra_offset = 0) const + { + return BIT(m_regdata[offset + extra_offset], start, count); } -protected: - // convert a channel number into a register offset; channel goes in low 2 bits - static constexpr u16 channel_offset(u8 chnum) { return BIT(chnum, 0, 2); } + // return a bitfield extracted from a pair of bytes, MSBs listed first + u32 word(u32 offset1, u32 start1, u32 count1, u32 offset2, u32 start2, u32 count2, u32 extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } - // convert an operator number into a register offset; operator goes into bits 2-3 - static constexpr u8 operator_offset(u8 opnum) { return (BIT(opnum, 0) << 3) | (BIT(opnum, 1) << 2); } + // internal state + u32 m_lfo_counter; // LFO counter + u8 m_lfo_am; // current LFO AM value + u8 m_regdata[REGISTERS]; // register data + u16 m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms }; +using ymopn_registers = ymopn_registers_base; +using ymopna_registers = ymopn_registers_base; + -// ======================> ymopna_registers +// ======================> ymopl_registers_base // -// OPNA/OPNB/OPNB2/OPN2 register map: +// OPL/OPL2/OPL3/OPL4 register map: // // System-wide registers: -// 21 xxxxxxxx Test register -// 22 ----x--- LFO enable (new for OPNA) -// -----xxx LFO rate (new for OPNA) -// 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 +// 01 xxxxxxxx Test register +// --x----- Enable OPL compatibility mode [OPL2 only] (1 = 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 -// 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 -// -----x-- Upper channel select (new for OPNA) -// ------xx Channel select +// 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-operator registers (channel in address bits 0-1, operator in bits 2-3) -// 30-3F -xxx---- Detune value (0-7) +// 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-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 (new for OPNA) -// ---xxxxx Decay rate (0-31) -// 70-7F ---xxxxx Sustain rate (0-31) -// 80-8F xxxx---- Sustain level (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) -// 90-9F ----x--- SSG-EG enable -// -----xxx SSG-EG envelope (0-7) +// E0-F5 ------xx Wave select (0-3) [OPL2 only] +// -----xxx Wave select (0-7) [OPL3+ only] // -// Per-channel registers (channel in address bits 0-1) -// 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 (new for OPNA) -// -x------ Pan right (new for OPNA) -// --xx---- LFO AM shift (0-3) (new for OPNA) -// -----xxx LFO PM depth (0-7) (new for OPNA) + +template +class ymopl_registers_base : public ymfm_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 u32 OUTPUTS = IsOpl3Plus ? 4 : 1; + static constexpr u32 CHANNELS = IsOpl3Plus ? 18 : 9; + static constexpr u32 ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr u32 OPERATORS = CHANNELS * 2; + static constexpr bool DYNAMIC_OPS = IsOpl3Plus; + static constexpr u32 WAVEFORMS = IsOpl3Plus ? 8 : (IsOpl2Plus ? 4 : 1); + static constexpr u32 REGISTERS = IsOpl3Plus ? 0x200 : 0x100; + static constexpr u32 REG_MODE = 0x04; + static constexpr u32 DEFAULT_PRESCALE = IsOpl4Plus ? 19 : (IsOpl3Plus ? 8 : 4); + static constexpr u32 EG_CLOCK_DIVIDER = 1; + static constexpr bool EG_HAS_DEPRESS = false; + static constexpr bool EG_HAS_SSG = false; + static constexpr bool MODULATOR_DELAY = !IsOpl3Plus; + static constexpr u32 CSM_TRIGGER_MASK = ALL_CHANNELS; + static constexpr u8 STATUS_TIMERA = 0x40; + static constexpr u8 STATUS_TIMERB = 0x20; + static constexpr u8 STATUS_BUSY = 0; + static constexpr u8 STATUS_IRQ = 0x80; + + // constructor + ymopl_registers_base(); + + // register for save states + void save(device_t &device); + + // reset to initial state + void reset(); + + // map channel number to register offset + static constexpr u32 channel_offset(u32 chnum) + { + assert(chnum < CHANNELS); + if (!IsOpl3Plus) + return chnum; + else + return (chnum % 9) + 0x100 * (chnum / 9); + } + + // map operator number to register offset + static constexpr u32 operator_offset(u32 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 { u32 chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // OPL4 apparently can read back FM registers? + u8 read(u16 index) { return m_regdata[index]; } + + // handle writes to the register array + bool write(u16 index, u8 data, u32 &chan, u32 &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + s32 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 + u32 lfo_am_offset(u32 choffs) const { return m_lfo_am; } + + // return LFO/noise states + u32 noise_state() const { return m_noise_lfsr >> 23; } + + // caching helpers + void cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache); + + // compute the phase step, given a PM value + u32 compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm); + + // log a key-on event + void log_keyon(u32 choffs, u32 opoffs); + + // system-wide registers + u32 test() const { return byte(0x01, 0, 8); } + u32 waveform_enable() const { return IsOpl2 ? byte(0x01, 5, 1) : (IsOpl3Plus ? 1 : 0); } + u32 timer_a_value() const { return byte(0x02, 0, 8) * 4; } // 8->10 bits + u32 timer_b_value() const { return byte(0x03, 0, 8); } + u32 status_mask() const { return byte(0x04, 0, 8) & 0x78; } + u32 irq_reset() const { return byte(0x04, 7, 1); } + u32 reset_timer_b() const { return byte(0x04, 7, 1) | byte(0x04, 5, 1); } + u32 reset_timer_a() const { return byte(0x04, 7, 1) | byte(0x04, 6, 1); } + u32 enable_timer_b() const { return byte(0x04, 5, 1) ^ 1; } + u32 enable_timer_a() const { return byte(0x04, 6, 1) ^ 1; } + u32 load_timer_b() const { return byte(0x04, 1, 1); } + u32 load_timer_a() const { return byte(0x04, 0, 1); } + u32 csm() const { return IsOpl3Plus ? 0 : byte(0x08, 7, 1); } + u32 note_select() const { return byte(0x08, 6, 1); } + u32 lfo_am_depth() const { return byte(0xbd, 7, 1); } + u32 lfo_pm_depth() const { return byte(0xbd, 6, 1); } + u32 rhythm_enable() const { return byte(0xbd, 5, 1); } + u32 rhythm_keyon() const { return byte(0xbd, 4, 0); } + u32 newflag() const { return IsOpl3Plus ? byte(0x105, 0, 1) : 0; } + u32 new2flag() const { return IsOpl4Plus ? byte(0x105, 1, 1) : 0; } + u32 fourop_enable() const { return IsOpl3Plus ? byte(0x104, 0, 6) : 0; } + + // per-channel registers + u32 ch_block_freq(u32 choffs) const { return word(0xb0, 0, 5, 0xa0, 0, 8, choffs); } + u32 ch_feedback(u32 choffs) const { return byte(0xc0, 1, 3, choffs); } + u32 ch_algorithm(u32 choffs) const { return byte(0xc0, 0, 1, choffs) | (IsOpl3Plus ? (8 | (byte(0xc3, 0, 1, choffs) << 1)) : 0); } + u32 ch_output_any(u32 choffs) const { return IsOpl3Plus ? byte(0xc0 + choffs, 4, 4) : 1; } + u32 ch_output_0(u32 choffs) const { return IsOpl3Plus ? byte(0xc0 + choffs, 4, 1) : 1; } + u32 ch_output_1(u32 choffs) const { return IsOpl3Plus ? byte(0xc0 + choffs, 5, 1) : 0; } + u32 ch_output_2(u32 choffs) const { return IsOpl3Plus ? byte(0xc0 + choffs, 6, 1) : 0; } + u32 ch_output_3(u32 choffs) const { return IsOpl3Plus ? byte(0xc0 + choffs, 7, 1) : 0; } + + // per-operator registers + u32 op_lfo_am_enable(u32 opoffs) const { return byte(0x20, 7, 1, opoffs); } + u32 op_lfo_pm_enable(u32 opoffs) const { return byte(0x20, 6, 1, opoffs); } + u32 op_eg_sustain(u32 opoffs) const { return byte(0x20, 5, 1, opoffs); } + u32 op_ksr(u32 opoffs) const { return byte(0x20, 4, 1, opoffs); } + u32 op_multiple(u32 opoffs) const { return byte(0x20, 0, 4, opoffs); } + u32 op_ksl(u32 opoffs) const { return bitswap<2>(byte(0x40, 6, 2, opoffs), 0, 1); } + u32 op_total_level(u32 opoffs) const { return byte(0x40, 0, 6, opoffs); } + u32 op_attack_rate(u32 opoffs) const { return byte(0x60, 4, 4, opoffs); } + u32 op_decay_rate(u32 opoffs) const { return byte(0x60, 0, 4, opoffs); } + u32 op_sustain_level(u32 opoffs) const { return byte(0x80, 4, 4, opoffs); } + u32 op_release_rate(u32 opoffs) const { return byte(0x80, 0, 4, opoffs); } + u32 op_waveform(u32 opoffs) const { return IsOpl2Plus ? byte(0xe0, 0, IsOpl3Plus ? 3 : 2, opoffs) : 0; } + +protected: + // return a bitfield extracted from a byte + u32 byte(u32 offset, u32 start, u32 count, u32 extra_offset = 0) const + { + return BIT(m_regdata[offset + extra_offset], start, count); + } + + // return a bitfield extracted from a pair of bytes, MSBs listed first + u32 word(u32 offset1, u32 start1, u32 count1, u32 offset2, u32 start2, u32 count2, u32 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(u32 choffs) const + { + return rhythm_enable() && (choffs >= 6 && choffs <= 8); + } + + // internal state + u16 m_lfo_am_counter; // LFO AM counter + u16 m_lfo_pm_counter; // LFO PM counter + u32 m_noise_lfsr; // noise LFSR state + u8 m_lfo_am; // current LFO AM value + u8 m_regdata[REGISTERS]; // register data + u16 m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms +}; + +using ymopl_registers = ymopl_registers_base<1>; +using ymopl2_registers = ymopl_registers_base<2>; +using ymopl3_registers = ymopl_registers_base<3>; +using ymopl4_registers = ymopl_registers_base<4>; + + +// ======================> ymopll_registers + // -// 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 +// 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 ymopna_registers : public ymopn_registers +class ymopll_registers : public ymfm_registers_base { public: - // constants - static constexpr u8 CHANNELS = 6; - static constexpr u16 REGISTERS = 0x200; + static constexpr u32 OUTPUTS = 2; + static constexpr u32 CHANNELS = 9; + static constexpr u32 ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr u32 OPERATORS = CHANNELS * 2; + static constexpr bool DYNAMIC_OPS = false; + static constexpr u32 WAVEFORMS = 2; + static constexpr u32 REGISTERS = 0x40; + static constexpr u32 REG_MODE = 0x3f; + static constexpr u32 DEFAULT_PRESCALE = 4; + static constexpr u32 EG_CLOCK_DIVIDER = 1; + static constexpr bool EG_HAS_DEPRESS = true; + static constexpr bool EG_HAS_SSG = false; + static constexpr bool MODULATOR_DELAY = true; + static constexpr u32 CSM_TRIGGER_MASK = 0; + static constexpr u8 STATUS_TIMERA = 0; + static constexpr u8 STATUS_TIMERB = 0; + static constexpr u8 STATUS_BUSY = 0; + static constexpr u8 STATUS_IRQ = 0; + + // OPLL-specific constants + static constexpr u32 INSTDATA_SIZE = 0x90; // constructor - ymopna_registers(std::vector ®data, u16 chbase = 0, u16 opbase = 0) : - ymopn_registers(regdata, chbase, opbase) + ymopll_registers(); + + // register for save states + void save(device_t &device); + + // reset to initial state + void reset(); + + // map channel number to register offset + static constexpr u32 channel_offset(u32 chnum) + { + assert(chnum < CHANNELS); + return chnum; + } + + // map operator number to register offset + static constexpr u32 operator_offset(u32 opnum) { + assert(opnum < OPERATORS); + return opnum; } - // return channel/operator number - u8 chnum() const { return BIT(m_chbase, 0, 2) + 3 * BIT(m_chbase, 8); } + // return an array of operator indices for each channel + struct operator_mapping { u32 chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // handle writes to the register array + bool write(u16 index, u8 data, u32 &chan, u32 &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + s32 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 + u32 lfo_am_offset(u32 choffs) const { return m_lfo_am; } - // reset state to default values - void reset() + // return LFO/noise states + u32 noise_state() const { return m_noise_lfsr >> 23; } + + // caching helpers + void cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache); + + // compute the phase step, given a PM value + u32 compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm); + + // log a key-on event + void log_keyon(u32 choffs, u32 opoffs); + + // set the instrument data + void set_instrument_data(u8 const *data) { - // 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; + memcpy(&m_instdata[0], data, INSTDATA_SIZE); } - // create a new version of ourself with a different channel/operator base - ymopna_registers channel_registers(u8 chnum) { return ymopna_registers(m_regdata, channel_offset(chnum)); } - ymopna_registers operator_registers(u8 opnum) { return ymopna_registers(m_regdata, m_chbase, m_chbase + operator_offset(opnum)); } + // system-wide registers + u32 rhythm_enable() const { return byte(0x0e, 5, 1); } + u32 rhythm_keyon() const { return byte(0x0e, 4, 0); } + u32 test() const { return byte(0x0f, 0, 8); } + u32 waveform_enable() const { return 1; } + u32 timer_a_value() const { return 0; } + u32 timer_b_value() const { return 0; } + u32 status_mask() const { return 0; } + u32 irq_reset() const { return 0; } + u32 reset_timer_b() const { return 0; } + u32 reset_timer_a() const { return 0; } + u32 enable_timer_b() const { return 0; } + u32 enable_timer_a() const { return 0; } + u32 load_timer_b() const { return 0; } + u32 load_timer_a() const { return 0; } + u32 csm() const { return 0; } - // OPNA-specific system-wide registers - u8 lfo_enabled() const /* 3 bits */ { return sysbyte(0x22, 3, 1); } - u8 lfo_rate() const /* 3 bits */ { return sysbyte(0x22, 0, 3); } - u8 keyon_channel() const /* 3 bits */ + // per-channel registers + u32 ch_block_freq(u32 choffs) const { return word(0x20, 0, 4, 0x10, 0, 8, choffs); } + u32 ch_sustain(u32 choffs) const { return byte(0x20, 5, 1, choffs); } + u32 ch_total_level(u32 choffs) const { return instchbyte(0x02, 0, 6, choffs); } + u32 ch_feedback(u32 choffs) const { return instchbyte(0x03, 0, 3, choffs); } + u32 ch_algorithm(u32 choffs) const { return 0; } + u32 ch_instrument(u32 choffs) const { return byte(0x30, 4, 4, choffs); } + u32 ch_output_any(u32 choffs) const { return 1; } + u32 ch_output_0(u32 choffs) const { return !is_rhythm(choffs); } + u32 ch_output_1(u32 choffs) const { return is_rhythm(choffs); } + u32 ch_output_2(u32 choffs) const { return 0; } + u32 ch_output_3(u32 choffs) const { return 0; } + + // per-operator registers + u32 op_lfo_am_enable(u32 opoffs) const { return instopbyte(0x00, 7, 1, opoffs); } + u32 op_lfo_pm_enable(u32 opoffs) const { return instopbyte(0x00, 6, 1, opoffs); } + u32 op_eg_sustain(u32 opoffs) const { return instopbyte(0x00, 5, 1, opoffs); } + u32 op_ksr(u32 opoffs) const { return instopbyte(0x00, 4, 1, opoffs); } + u32 op_multiple(u32 opoffs) const { return instopbyte(0x00, 0, 4, opoffs); } + u32 op_ksl(u32 opoffs) const { return instopbyte(0x02, 6, 2, opoffs); } + u32 op_waveform(u32 opoffs) const { return instchbyte(0x03, 3 + BIT(opoffs, 0), 1, opoffs >> 1); } + u32 op_attack_rate(u32 opoffs) const { return instopbyte(0x04, 4, 4, opoffs); } + u32 op_decay_rate(u32 opoffs) const { return instopbyte(0x04, 0, 4, opoffs); } + u32 op_sustain_level(u32 opoffs) const { return instopbyte(0x06, 4, 4, opoffs); } + u32 op_release_rate(u32 opoffs) const { return instopbyte(0x06, 0, 4, opoffs); } + u32 op_volume(u32 opoffs) const { return byte(0x30, 4 * BIT(~opoffs, 0), 4, opoffs >> 1); } + +private: + // return a bitfield extracted from a byte + u32 byte(u32 offset, u32 start, u32 count, u32 extra_offset = 0) const { - // ensure that both 3 and 7 return out-of-range values - u8 temp = sysbyte(0x28, 0, 3); - return (temp == 3) ? 6 : temp - BIT(temp, 2); + return BIT(m_regdata[offset + extra_offset], start, count); } - // OPNA-specific per-channel registers - u8 pan_left() const /* 1 bit */ { return chbyte(0xb4, 7, 1); } - u8 pan_right() const /* 1 bit */ { return chbyte(0xb4, 6, 1); } - u8 lfo_am_sensitivity() const /* 2 bits */ { return chbyte(0xb4, 4, 2); } - u8 lfo_pm_sensitivity() const /* 3 bits */ { return chbyte(0xb4, 0, 3); } + // return a bitfield extracted from a pair of bytes, MSBs listed first + u32 word(u32 offset1, u32 start1, u32 count1, u32 offset2, u32 start2, u32 count2, u32 extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } - // OPNA-specific per-operator registers - u8 lfo_am_enabled() const /* 1 bit */ { return opbyte(0x60, 7, 1); } + // helpers to read from instrument channel/operator data + u32 instchbyte(u32 offset, u32 start, u32 count, u32 choffs) const { return BIT(m_chinst[choffs][offset], start, count); } + u32 instopbyte(u32 offset, u32 start, u32 count, u32 opoffs) const { return BIT(m_opinst[opoffs][offset], start, count); } -protected: - // convert a channel number into a register offset - static constexpr u16 channel_offset(u8 chnum) { return chnum % 3 + ((chnum / 3) << 8); } -}; + // helper to determine if the this channel is an active rhythm channel + bool is_rhythm(u32 choffs) const + { + return rhythm_enable() && choffs >= 6; + } + // internal state + u16 m_lfo_am_counter; // LFO AM counter + u16 m_lfo_pm_counter; // LFO PM counter + u32 m_noise_lfsr; // noise LFSR state + u8 m_lfo_am; // current LFO AM value + u8 const *m_chinst[CHANNELS]; // pointer to instrument data for each channel + u8 const *m_opinst[OPERATORS]; // pointer to instrument data for each operator + u8 m_regdata[REGISTERS]; // register data + u8 m_instdata[INSTDATA_SIZE]; // instrument data + u16 m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms +}; //********************************************************* // CORE ENGINE CLASSES //********************************************************* +// forward declarations +template class ymfm_engine_base; + +// three different keyon sources; actual keyon is an OR over all of these +enum ymfm_keyon_type : u32 +{ + YMFM_KEYON_NORMAL = 0, + YMFM_KEYON_RHYTHM = 1, + YMFM_KEYON_CSM = 2 +}; + + // ======================> ymfm_operator +// ymfm_operator represents an FM operator (or "slot" in FM parlance), which +// produces an output sine wave modulated by an envelope template class ymfm_operator { - enum envelope_state : u8 - { - ENV_ATTACK = 0, - ENV_DECAY = 1, - ENV_SUSTAIN = 2, - ENV_RELEASE = 3 - }; - static constexpr u16 ENV_QUIET = 0x200; + // "quiet" value, used to optimize when we can skip doing working + static constexpr u32 ENV_QUIET = 0x200; public: // constructor - ymfm_operator(RegisterType regs); + ymfm_operator(ymfm_engine_base &owner, u32 opoffs); // register for save states - void save(device_t &device, u8 index); + void save(device_t &device, u32 index); // reset the operator state void reset(); + // set the current channel + void set_choffs(u32 choffs) { m_choffs = choffs; } + + // prepare prior to clocking + bool prepare(); + // master clocking function - void clock(u32 env_counter, s8 lfo_raw_pm, u16 block_freq); + void clock(u32 env_counter, s32 lfo_raw_pm); + + // return the current phase value + u32 phase() const { return m_phase >> 10; } // compute operator volume - s16 compute_volume(u16 modulation, u16 am_offset) const; + s32 compute_volume(u32 phase, u32 am_offset) const; // compute volume for the OPM noise channel - s16 compute_noise_volume(u8 noise_state, u16 am_offset) const; + s32 compute_noise_volume(u32 am_offset) const; // key state control - void keyonoff(u8 on) { m_keyon = on; } - void keyon_csm() { m_csm_triggered = 1; } + void keyonoff(u32 on, ymfm_keyon_type type); - // are we active? - bool active() const { return (m_env_state != ENV_RELEASE || m_env_attenuation < ENV_QUIET); } + // return a reference to our registers + RegisterType ®s() { return m_regs; } private: - // convert the generic block_freq into a 5-bit keycode - u8 block_freq_to_keycode(u16 block_freq); - - // return the effective 6-bit ADSR rate after adjustments - u8 effective_rate(u8 rawrate, u8 keycode); - // start the attack phase - void start_attack(u8 keycode); + void start_attack(); // start the release phase void start_release(); // clock phases - void clock_keystate(u8 keystate, u8 keycode); - void clock_ssg_eg_state(u8 keycode); - void clock_envelope(u16 env_counter, u8 keycode); - void clock_phase(s8 lfo_raw_pm, u16 block_freq); + void clock_keystate(u32 keystate); + void clock_ssg_eg_state(); + void clock_envelope(u32 env_counter); + void clock_phase(s32 lfo_raw_pm); // return effective attenuation of the envelope - u16 envelope_attenuation(u8 am_offset) const; + u32 envelope_attenuation(u32 am_offset) const; // internal state + u32 m_choffs; // channel offset in registers + u32 m_opoffs; // operator offset in registers u32 m_phase; // current phase value (10.10 format) u16 m_env_attenuation; // computed envelope attenuation (4.6 format) - envelope_state m_env_state; // current envelope state + ymfm_envelope_state m_env_state; // current envelope state u8 m_ssg_inverted; // non-zero if the output should be inverted (bit 0) u8 m_key_state; // current key state: on or off (bit 0) - u8 m_keyon; // live key on state (bit 0) - u8 m_csm_triggered; // true if a CSM key on has been triggered (bit 0) - RegisterType m_regs; // operator-specific registers + u8 m_keyon_live; // live key on state (bit 0 = direct, bit 1 = rhythm, bit 2 = CSM) + ymfm_opdata_cache m_cache; // cached values for performance + RegisterType &m_regs; // direct reference to registers + ymfm_engine_base &m_owner; // reference to the owning engine }; -template<> -u8 ymfm_operator::block_freq_to_keycode(u16 block_freq); - -template<> -void ymfm_operator::clock_phase(s8 lfo_raw_pm, u16 block_freq); - // ======================> ymfm_channel +// ymfm_channel represents an FM channel which combines the output of 2 or 4 +// operators into a final result template class ymfm_channel { public: // constructor - ymfm_channel(RegisterType regs); + ymfm_channel(ymfm_engine_base &owner, u32 choffs); // register for save states - void save(device_t &device, u8 index); + void save(device_t &device, u32 index); // reset the channel state void reset(); + // assign operators + void assign(int index, ymfm_operator *op) + { + assert(index < std::size(m_op)); + m_op[index] = op; + if (op != nullptr) + op->set_choffs(m_choffs); + } + // signal key on/off to our operators - void keyonoff(u8 states); + void keyonoff(u32 states, ymfm_keyon_type type); - // signal CSM key on to our operators - void keyon_csm(); + // prepare prior to clocking + bool prepare(); // master clocking function - void clock(u32 env_counter, s8 lfo_raw_pm, bool is_multi_freq); + void clock(u32 env_counter, s32 lfo_raw_pm); + + // specific 2-operator and 4-operator output handlers + void output_2op(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const; + void output_4op(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const; - // compute the channel output and add to the left/right output sums - void output(u8 lfo_raw_am, u8 noise_state, s32 &lsum, s32 &rsum, u8 rshift, s16 clipmax) const; + // compute the special OPL rhythm channel outputs + void output_rhythm_ch6(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const; + void output_rhythm_ch7(u32 phase_select, s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const; + void output_rhythm_ch8(u32 phase_select, s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 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); + } - // is this channel active? - bool active() const { return m_op1.active() || m_op2.active() || m_op3.active() || m_op4.active(); } + // return a reference to our registers + RegisterType ®s() { return m_regs; } private: - // convert a 6/8-bit raw AM value into an amplitude offset based on sensitivity - u16 lfo_am_offset(u8 am_value) const; + // helper to add values to the outputs based on channel enables + void add_to_output(u32 choffs, s32 *outputs, s32 value) const + { + if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs)) + outputs[0] += value; + if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs)) + outputs[1] += value; + if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs)) + outputs[2] += value; + if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs)) + outputs[3] += value; + } // internal state + u32 m_choffs; // channel offset in registers s16 m_feedback[2]; // feedback memory for operator 1 mutable s16 m_feedback_in; // next input value for op 1 feedback (set in output) - ymfm_operator m_op1; // operator 1 - ymfm_operator m_op2; // operator 2 - ymfm_operator m_op3; // operator 3 - ymfm_operator m_op4; // operator 4 - RegisterType m_regs; // channel-specific registers + ymfm_operator *m_op[4]; // up to 4 operators + RegisterType &m_regs; // direct reference to registers + ymfm_engine_base &m_owner; // reference to the owning engine }; -template<> -u16 ymfm_channel::lfo_am_offset(u8 lfo_raw_am) const; - // ======================> ymfm_engine_base +// ymfm_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 ymfm_engine_base { public: - enum : u8 - { - STATUS_TIMERA = 0x01, - STATUS_TIMERB = 0x02, - STATUS_BUSY = 0x80 - }; + // expose some constants from the registers + static constexpr u32 OUTPUTS = RegisterType::OUTPUTS; + static constexpr u32 CHANNELS = RegisterType::CHANNELS; + static constexpr u32 ALL_CHANNELS = RegisterType::ALL_CHANNELS; + static constexpr u32 OPERATORS = RegisterType::OPERATORS; + + // also expose status flags for consumers that inject additional bits + static constexpr u8 STATUS_TIMERA = RegisterType::STATUS_TIMERA; + static constexpr u8 STATUS_TIMERB = RegisterType::STATUS_TIMERB; + static constexpr u8 STATUS_BUSY = RegisterType::STATUS_BUSY; + static constexpr u8 STATUS_IRQ = RegisterType::STATUS_IRQ; // constructor ymfm_engine_base(device_t &device); + // configuration helpers + auto irq_handler() { return m_irq_handler.bind(); } + // register for save states void save(device_t &device); @@ -740,10 +1286,10 @@ public: void reset(); // master clocking function - u32 clock(u8 chanmask); + u32 clock(u32 chanmask); // compute sum of channel outputs - void output(s32 &lsum, s32 &rsum, u8 rshift, s16 clipmax, u8 chanmask) const; + void output(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax, u32 chanmask) const; // write to the OPN registers void write(u16 regnum, u8 data); @@ -752,7 +1298,12 @@ public: u8 status() const; // set/reset bits in the status register, updating the IRQ status - void set_reset_status(u8 set, u8 reset) { m_status = (m_status | set) & ~reset; schedule_check_interrupts(); } + u8 set_reset_status(u8 set, u8 reset) + { + m_status = (m_status | set) & ~reset & ~m_regs.status_mask(); + schedule_check_interrupts(); + return m_status; + } // set the IRQ mask void set_irq_mask(u8 mask) { m_irq_mask = mask; schedule_check_interrupts(); } @@ -767,26 +1318,29 @@ public: void set_busy_end(attotime end) { m_busy_end = end; } // return the current clock prescale - u8 clock_prescale() const { return m_clock_prescale; } + u32 clock_prescale() const { return m_clock_prescale; } // set prescale factor (2/3/6) - void set_clock_prescale(u8 prescale) { m_clock_prescale = prescale; } + void set_clock_prescale(u32 prescale) { m_clock_prescale = prescale; } - // configuration helpers - auto irq_handler() { return m_irq_handler.bind(); } + // compute sample rate + u32 sample_rate(u32 baseclock) const { return baseclock / (m_clock_prescale * OPERATORS); } // reset the LFO state - void reset_lfo() { m_lfo_counter = 0; } + void reset_lfo() { m_regs.reset_lfo(); } -private: - // clock the LFO, updating m_lfo_am and return the signed PM value - s8 clock_lfo(); + // return the owning device + device_t &device() const { return m_device; } - // clock the noise generator - void clock_noise(); + // return a reference to our registers + RegisterType ®s() { return m_regs; } + +protected: + // assign the current set of operators to channels + void assign_operators(); // update the state of the given timer - void update_timer(u8 which, u8 enable); + void update_timer(u32 which, u32 enable); // timer callback TIMER_CALLBACK_MEMBER(timer_handler); @@ -803,12 +1357,6 @@ private: // internal state device_t &m_device; // reference to the owning device u32 m_env_counter; // envelope counter; low 2 bits are sub-counter - u32 m_lfo_counter; // LFO counter - u32 m_noise_lfsr; // noise LFSR state - u8 m_noise_counter; // noise counter - u8 m_noise_state; // latched noise state - u8 m_noise_lfo; // latched LFO noise value - u8 m_lfo_am; // current LFO AM value u8 m_status; // current status register u8 m_clock_prescale; // prescale factor (2/3/6) u8 m_irq_mask; // mask of which bits signal IRQs @@ -819,26 +1367,49 @@ private: attotime m_busy_end; // end of the busy time emu_timer *m_timer[2]; // our two timers devcb_write_line m_irq_handler; // IRQ callback - std::unique_ptr> m_channel[RegisterType::CHANNELS]; // channel pointers - std::vector m_regdata; // raw register data RegisterType m_regs; // register accessor + std::unique_ptr> m_channel[CHANNELS]; // channel pointers + std::unique_ptr> m_operator[OPERATORS]; // operator pointers }; -template<> -s8 ymfm_engine_base::clock_lfo(); - -template<> -void ymfm_engine_base::clock_noise(); - // ======================> template instantiations extern template class ymfm_engine_base; extern template class ymfm_engine_base; extern template class ymfm_engine_base; +extern template class ymfm_engine_base; +extern template class ymfm_engine_base; +extern template class ymfm_engine_base; using ymopm_engine = ymfm_engine_base; using ymopn_engine = ymfm_engine_base; using ymopna_engine = ymfm_engine_base; +using ymopl_engine = ymfm_engine_base; +using ymopl2_engine = ymfm_engine_base; +using ymopl3_engine = ymfm_engine_base; +using ymopl4_engine = ymfm_engine_base; + + +// ======================> ymopll_engine + +// ymopll_engine is a special case because instrument data needs to be +// provided from an external source +class ymopll_engine : public ymfm_engine_base +{ +public: + // constructor + ymopll_engine(device_t &device) : + ymfm_engine_base(device) + { + } + + // set the instrument data + void set_instrument_data(u8 const *data) + { + m_regs.set_instrument_data(data); + } +}; + #endif // MAME_SOUND_YMFM_H diff --git a/src/mame/audio/nichisnd.h b/src/mame/audio/nichisnd.h index 897ed87ac6f..22a04a38497 100644 --- a/src/mame/audio/nichisnd.h +++ b/src/mame/audio/nichisnd.h @@ -12,8 +12,8 @@ #pragma once #include "cpu/z80/tmpz84c011.h" -#include "sound/3812intf.h" #include "sound/dac.h" +#include "sound/ym3812.h" #include "speaker.h" #include "machine/gen_latch.h" diff --git a/src/mame/drivers/actfancr.cpp b/src/mame/drivers/actfancr.cpp index 16acf6cc680..5b32998b852 100644 --- a/src/mame/drivers/actfancr.cpp +++ b/src/mame/drivers/actfancr.cpp @@ -21,9 +21,9 @@ #include "includes/actfancr.h" #include "cpu/m6502/m6502.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "emupal.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/aerofgt.cpp b/src/mame/drivers/aerofgt.cpp index 2c08d3ab25d..ac51e0a9baf 100644 --- a/src/mame/drivers/aerofgt.cpp +++ b/src/mame/drivers/aerofgt.cpp @@ -67,9 +67,9 @@ Verification still needed for the other PCBs. #include "cpu/z80/z80.h" #include "machine/mb3773.h" #include "machine/vs9209.h" -#include "sound/3812intf.h" #include "sound/ym2151.h" #include "sound/ym2610.h" +#include "sound/ym3812.h" #include "video/vsystem_gga.h" #include "screen.h" #include "speaker.h" @@ -525,8 +525,8 @@ void aerofgt_state::karatblzbl_sound_map(address_map &map) void aerofgt_state::karatblzbl_sound_portmap(address_map &map) { map.global_mask(0xff); - map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::write_port_w)); + map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::data_w)); map(0x40, 0x40).w(FUNC(aerofgt_state::karatblzbl_d7759_write_port_0_w)); map(0x80, 0x80).w(FUNC(aerofgt_state::karatblzbl_d7759_reset_w)); } @@ -541,8 +541,8 @@ void aerofgt_state::kickball_sound_map(address_map &map) void aerofgt_state::kickball_sound_portmap(address_map &map) { map.global_mask(0xff); - map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::write_port_w)); + map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::data_w)); map(0x40, 0x40).rw(m_oki, FUNC(okim6295_device::read), FUNC(okim6295_device::write)); map(0xc0, 0xc0).w(m_soundlatch, FUNC(generic_latch_8_device::acknowledge_w)); } diff --git a/src/mame/drivers/alpha68k_i.cpp b/src/mame/drivers/alpha68k_i.cpp index 855f9f19675..d5f4ec2b81c 100644 --- a/src/mame/drivers/alpha68k_i.cpp +++ b/src/mame/drivers/alpha68k_i.cpp @@ -152,8 +152,8 @@ void paddlemania_state::sound_map(address_map &map) { map(0x0000, 0x9fff).rom(); map(0xe000, 0xe000).rw(m_soundlatch, FUNC(generic_latch_8_device::read), FUNC(generic_latch_8_device::clear_w)); - map(0xe800, 0xe800).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0xec00, 0xec00).w("ymsnd", FUNC(ym3812_device::write_port_w)); + map(0xe800, 0xe800).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0xec00, 0xec00).w("ymsnd", FUNC(ym3812_device::data_w)); map(0xf000, 0xf7ff).ram(); map(0xfc00, 0xfc00).ram(); // unknown port } @@ -168,8 +168,8 @@ void thenextspace_state::sound_map(address_map &map) void thenextspace_state::sound_iomap(address_map &map) { map.global_mask(0xff); - map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::write_port_w)); + map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::data_w)); map(0x3b, 0x3b).nopr(); // unknown read port map(0x3d, 0x3d).nopr(); // unknown read port map(0x7b, 0x7b).nopr(); // unknown read port diff --git a/src/mame/drivers/amaticmg.cpp b/src/mame/drivers/amaticmg.cpp index efdd0fd950b..7b9b46cb264 100644 --- a/src/mame/drivers/amaticmg.cpp +++ b/src/mame/drivers/amaticmg.cpp @@ -414,7 +414,7 @@ #include "cpu/z80/z80.h" #include "machine/i8255.h" #include "machine/ds1994.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "video/mc6845.h" //#include "sound/dac.h" #include "emupal.h" diff --git a/src/mame/drivers/armedf.cpp b/src/mame/drivers/armedf.cpp index 13e83b79a15..2b494d679b2 100644 --- a/src/mame/drivers/armedf.cpp +++ b/src/mame/drivers/armedf.cpp @@ -320,9 +320,9 @@ Notes: #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "cpu/mcs51/mcs51.h" -#include "sound/3526intf.h" -#include "sound/3812intf.h" #include "sound/dac.h" +#include "sound/ym3526.h" +#include "sound/ym3812.h" #include "speaker.h" #define LEGION_HACK 0 diff --git a/src/mame/drivers/battlane.cpp b/src/mame/drivers/battlane.cpp index bf752bdc4a7..74f31a42670 100644 --- a/src/mame/drivers/battlane.cpp +++ b/src/mame/drivers/battlane.cpp @@ -16,7 +16,7 @@ #include "includes/battlane.h" #include "cpu/m6809/m6809.h" -#include "sound/3526intf.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/battlnts.cpp b/src/mame/drivers/battlnts.cpp index a43185575e1..70c68f3507d 100644 --- a/src/mame/drivers/battlnts.cpp +++ b/src/mame/drivers/battlnts.cpp @@ -20,7 +20,7 @@ #include "cpu/z80/z80.h" #include "machine/gen_latch.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/bebox.cpp b/src/mame/drivers/bebox.cpp index eada264e7bf..f7c6e85880f 100644 --- a/src/mame/drivers/bebox.cpp +++ b/src/mame/drivers/bebox.cpp @@ -15,7 +15,7 @@ /* Components */ #include "video/clgd542x.h" #include "bus/lpci/cirrus.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "machine/mc146818.h" #include "machine/pckeybrd.h" #include "bus/lpci/mpc105.h" diff --git a/src/mame/drivers/bigkarnk_ms.cpp b/src/mame/drivers/bigkarnk_ms.cpp index d85c4123f04..25f8558b4fb 100644 --- a/src/mame/drivers/bigkarnk_ms.cpp +++ b/src/mame/drivers/bigkarnk_ms.cpp @@ -289,7 +289,7 @@ Sound Board 9/2 #include "speaker.h" #include "tilemap.h" #include "sound/msm5205.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "machine/gen_latch.h" #include "machine/bankdev.h" diff --git a/src/mame/drivers/brkthru.cpp b/src/mame/drivers/brkthru.cpp index a6c1ba5ec55..7e5fea93f03 100644 --- a/src/mame/drivers/brkthru.cpp +++ b/src/mame/drivers/brkthru.cpp @@ -145,8 +145,8 @@ buttons down after the game has started then pressing F3 to reset the game. #include "includes/brkthru.h" #include "cpu/m6809/m6809.h" -#include "sound/3526intf.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/bublbobl.cpp b/src/mame/drivers/bublbobl.cpp index e8d2889242b..d6107629daa 100644 --- a/src/mame/drivers/bublbobl.cpp +++ b/src/mame/drivers/bublbobl.cpp @@ -273,8 +273,8 @@ TODO: #include "cpu/m6800/m6801.h" #include "cpu/z80/z80.h" #include "machine/watchdog.h" -#include "sound/3526intf.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/cop01.cpp b/src/mame/drivers/cop01.cpp index e13e93df911..b56be48d5c8 100644 --- a/src/mame/drivers/cop01.cpp +++ b/src/mame/drivers/cop01.cpp @@ -59,7 +59,7 @@ Mighty Guy board layout: #include "cpu/z80/z80.h" #include "sound/ay8910.h" -#include "sound/3526intf.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" #include "sound/dac.h" diff --git a/src/mame/drivers/crospang.cpp b/src/mame/drivers/crospang.cpp index 1d0f7970c4b..167ad509d3b 100644 --- a/src/mame/drivers/crospang.cpp +++ b/src/mame/drivers/crospang.cpp @@ -32,7 +32,7 @@ #include "cpu/z80/z80.h" #include "cpu/m68000/m68000.h" #include "sound/okim6295.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "emupal.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/cybertnk.cpp b/src/mame/drivers/cybertnk.cpp index b073f8c9cf3..ebc26c9ed9e 100644 --- a/src/mame/drivers/cybertnk.cpp +++ b/src/mame/drivers/cybertnk.cpp @@ -173,7 +173,7 @@ lev 7 : 0x7c : 0000 07e0 - input device clear? #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "machine/gen_latch.h" -#include "sound/8950intf.h" +#include "sound/y8950.h" #include "emupal.h" #include "layout/generic.h" #include "screen.h" diff --git a/src/mame/drivers/dcon.cpp b/src/mame/drivers/dcon.cpp index 316bfc7b713..32248a76e34 100644 --- a/src/mame/drivers/dcon.cpp +++ b/src/mame/drivers/dcon.cpp @@ -19,9 +19,9 @@ #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2151.h" +#include "sound/ym3812.h" #include "video/seibu_crtc.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/dec0.cpp b/src/mame/drivers/dec0.cpp index 3e9ee1ffb81..142c3c1fcb4 100644 --- a/src/mame/drivers/dec0.cpp +++ b/src/mame/drivers/dec0.cpp @@ -397,9 +397,9 @@ Notes: #include "cpu/z80/z80.h" #include "cpu/m6805/m68705.h" #include "machine/upd4701.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/dec8.cpp b/src/mame/drivers/dec8.cpp index 1add551632d..8f93e9f5991 100644 --- a/src/mame/drivers/dec8.cpp +++ b/src/mame/drivers/dec8.cpp @@ -48,10 +48,10 @@ To do: #include "cpu/m6809/hd6309.h" #include "cpu/m6809/m6809.h" #include "machine/deco222.h" -#include "sound/3526intf.h" -#include "sound/3812intf.h" #include "sound/msm5205.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/deniam.cpp b/src/mame/drivers/deniam.cpp index eba37d23709..c5a55395ec8 100644 --- a/src/mame/drivers/deniam.cpp +++ b/src/mame/drivers/deniam.cpp @@ -49,7 +49,7 @@ Notes: #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "sound/okim6295.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/discoboy.cpp b/src/mame/drivers/discoboy.cpp index bd846bbcaa6..376f9d23b0c 100644 --- a/src/mame/drivers/discoboy.cpp +++ b/src/mame/drivers/discoboy.cpp @@ -47,7 +47,7 @@ Notes: #include "machine/bankdev.h" #include "machine/gen_latch.h" #include "sound/msm5205.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "emupal.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/dunhuang.cpp b/src/mame/drivers/dunhuang.cpp index 5f6498aa59a..4e8ce0a747f 100644 --- a/src/mame/drivers/dunhuang.cpp +++ b/src/mame/drivers/dunhuang.cpp @@ -511,8 +511,8 @@ void dunhuang_state::dunhuang_io_map(address_map &map) map(0x001b, 0x001b).w(FUNC(dunhuang_state::block_dest_w)); - map(0x0081, 0x0081).w("ymsnd", FUNC(ym2413_device::register_port_w)); - map(0x0089, 0x0089).w("ymsnd", FUNC(ym2413_device::data_port_w)); + map(0x0081, 0x0081).w("ymsnd", FUNC(ym2413_device::address_w)); + map(0x0089, 0x0089).w("ymsnd", FUNC(ym2413_device::data_w)); map(0x0082, 0x0082).w("oki", FUNC(okim6295_device::write)); diff --git a/src/mame/drivers/dynax.cpp b/src/mame/drivers/dynax.cpp index f4790b7f2a7..904123d40c0 100644 --- a/src/mame/drivers/dynax.cpp +++ b/src/mame/drivers/dynax.cpp @@ -85,10 +85,10 @@ TODO: #include "cpu/z80/tmpz84c015.h" #include "machine/msm6242.h" #include "machine/nvram.h" -#include "sound/3812intf.h" #include "sound/ay8910.h" #include "sound/ym2203.h" #include "sound/ym2413.h" +#include "sound/ym3812.h" #include "layout/generic.h" #include "speaker.h" diff --git a/src/mame/drivers/dynduke.cpp b/src/mame/drivers/dynduke.cpp index 1a84348a6fa..8a55cbbd11c 100644 --- a/src/mame/drivers/dynduke.cpp +++ b/src/mame/drivers/dynduke.cpp @@ -72,8 +72,8 @@ Also, implemented conditional port for Coin Mode (SW1:1) #include "cpu/nec/nec.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/esd16.cpp b/src/mame/drivers/esd16.cpp index eb51cde8e3d..d7c0b1f3245 100644 --- a/src/mame/drivers/esd16.cpp +++ b/src/mame/drivers/esd16.cpp @@ -70,8 +70,8 @@ ToDo: #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "emupal.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/exprraid.cpp b/src/mame/drivers/exprraid.cpp index 8a35a494a90..c4a106cf96c 100644 --- a/src/mame/drivers/exprraid.cpp +++ b/src/mame/drivers/exprraid.cpp @@ -211,8 +211,8 @@ Stephh's notes (based on the games M6502 code and some tests) : #include "cpu/m6502/deco16.h" #include "cpu/m6502/m6502.h" #include "cpu/m6809/m6809.h" -#include "sound/3526intf.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/fantland.cpp b/src/mame/drivers/fantland.cpp index 1405fa7aea1..941f8f9c9e8 100644 --- a/src/mame/drivers/fantland.cpp +++ b/src/mame/drivers/fantland.cpp @@ -46,10 +46,10 @@ Year + Game Main CPU Sound CPU Sound Video #include "cpu/i86/i86.h" #include "cpu/nec/nec.h" #include "cpu/z80/z80.h" -#include "sound/3526intf.h" #include "sound/dac.h" #include "sound/sn76496.h" #include "sound/ym2151.h" +#include "sound/ym3526.h" #include "speaker.h" diff --git a/src/mame/drivers/firetrap.cpp b/src/mame/drivers/firetrap.cpp index 365342e70b2..aa318ffe587 100644 --- a/src/mame/drivers/firetrap.cpp +++ b/src/mame/drivers/firetrap.cpp @@ -176,7 +176,7 @@ the MSM5205-derived interrupt assigned to the NMI line instead. #include "cpu/z80/z80.h" #include "cpu/m6502/m6502.h" -#include "sound/3526intf.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/fresh.cpp b/src/mame/drivers/fresh.cpp index c0074390bf1..a07c2908fd1 100644 --- a/src/mame/drivers/fresh.cpp +++ b/src/mame/drivers/fresh.cpp @@ -200,8 +200,8 @@ void fresh_state::fresh_map(address_map &map) map(0xc40000, 0xc417ff).ram().w(m_palette, FUNC(palette_device::write16)).share("palette"); map(0xc50000, 0xc517ff).ram().w(m_palette, FUNC(palette_device::write16_ext)).share("palette_ext"); - map(0xd00001, 0xd00001).w("ymsnd", FUNC(ym2413_device::register_port_w)); - map(0xd10001, 0xd10001).w("ymsnd", FUNC(ym2413_device::data_port_w)); + map(0xd00001, 0xd00001).w("ymsnd", FUNC(ym2413_device::address_w)); + map(0xd10001, 0xd10001).w("ymsnd", FUNC(ym2413_device::data_w)); map(0xd30000, 0xd30001).w(FUNC(fresh_state::d30000_write)); map(0xd40000, 0xd40001).portr("IN0"); //.nopw(); // checks for 0x10 diff --git a/src/mame/drivers/fuukifg2.cpp b/src/mame/drivers/fuukifg2.cpp index c3d3db8e846..802b73994d0 100644 --- a/src/mame/drivers/fuukifg2.cpp +++ b/src/mame/drivers/fuukifg2.cpp @@ -50,8 +50,8 @@ To Do: #include "cpu/z80/z80.h" #include "cpu/m68000/m68000.h" -#include "sound/3812intf.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/gaelco.cpp b/src/mame/drivers/gaelco.cpp index f68d28271c9..002e66449f4 100644 --- a/src/mame/drivers/gaelco.cpp +++ b/src/mame/drivers/gaelco.cpp @@ -26,7 +26,7 @@ Year Game PCB NOTES #include "cpu/m6809/m6809.h" #include "cpu/m68000/m68000.h" #include "sound/okim6295.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/galivan.cpp b/src/mame/drivers/galivan.cpp index b008eb54918..f4f89f09b6a 100644 --- a/src/mame/drivers/galivan.cpp +++ b/src/mame/drivers/galivan.cpp @@ -30,8 +30,8 @@ TODO #include "includes/galivan.h" #include "cpu/z80/z80.h" -#include "sound/3526intf.h" #include "sound/dac.h" +#include "sound/ym3526.h" #include "speaker.h" diff --git a/src/mame/drivers/galspnbl.cpp b/src/mame/drivers/galspnbl.cpp index 54acc1fb0fb..7bd17dca196 100644 --- a/src/mame/drivers/galspnbl.cpp +++ b/src/mame/drivers/galspnbl.cpp @@ -43,7 +43,7 @@ Manuals for both games define the controls as 4 push buttons: #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "sound/okim6295.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/ginganin.cpp b/src/mame/drivers/ginganin.cpp index d1d05241392..620c46441b1 100644 --- a/src/mame/drivers/ginganin.cpp +++ b/src/mame/drivers/ginganin.cpp @@ -63,7 +63,7 @@ f5d6 print 7 digit BCD number: d0.l to (a1)+ color $3000 #include "cpu/m6809/m6809.h" #include "machine/6840ptm.h" #include "sound/ay8910.h" -#include "sound/8950intf.h" +#include "sound/y8950.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/goodejan.cpp b/src/mame/drivers/goodejan.cpp index 3dbacb36554..a20c7358b38 100644 --- a/src/mame/drivers/goodejan.cpp +++ b/src/mame/drivers/goodejan.cpp @@ -74,8 +74,8 @@ Secret menu hack [totmejan only] (I couldn't find official way to enter, so it's #include "audio/seibu.h" #include "cpu/nec/nec.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "video/seibu_crtc.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/hcastle.cpp b/src/mame/drivers/hcastle.cpp index 297ff9ef33b..af08958572c 100644 --- a/src/mame/drivers/hcastle.cpp +++ b/src/mame/drivers/hcastle.cpp @@ -16,8 +16,8 @@ #include "cpu/z80/z80.h" #include "machine/gen_latch.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" #include "sound/k051649.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/igs011.cpp b/src/mame/drivers/igs011.cpp index 1881ee7517a..7c0f18bc24b 100644 --- a/src/mame/drivers/igs011.cpp +++ b/src/mame/drivers/igs011.cpp @@ -66,10 +66,10 @@ Notes: #include "emu.h" #include "cpu/m68000/m68000.h" -#include "sound/okim6295.h" -#include "sound/3812intf.h" #include "sound/ics2115.h" +#include "sound/okim6295.h" #include "sound/ym2413.h" +#include "sound/ym3812.h" #include "machine/nvram.h" #include "machine/timer.h" #include "emupal.h" diff --git a/src/mame/drivers/itech8.cpp b/src/mame/drivers/itech8.cpp index 921b6f0fd65..75c76de3426 100644 --- a/src/mame/drivers/itech8.cpp +++ b/src/mame/drivers/itech8.cpp @@ -505,10 +505,10 @@ #include "cpu/z80/z80.h" #include "machine/6522via.h" #include "machine/6821pia.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2203.h" #include "sound/ym2608.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/jackpot.cpp b/src/mame/drivers/jackpot.cpp index c38ca3c9016..fc2090269f4 100644 --- a/src/mame/drivers/jackpot.cpp +++ b/src/mame/drivers/jackpot.cpp @@ -39,8 +39,8 @@ In the same period Electronic Projects also released games on different platform #include "speaker.h" #include "cpu/z80/z80.h" #include "machine/eepromser.h" -#include "sound/3526intf.h" #include "sound/ay8910.h" +#include "sound/ym3526.h" #include "video/mc6845.h" class jackpot_state : public driver_device diff --git a/src/mame/drivers/karnov.cpp b/src/mame/drivers/karnov.cpp index c6ff4cbbbd5..b8a5fcfcea2 100644 --- a/src/mame/drivers/karnov.cpp +++ b/src/mame/drivers/karnov.cpp @@ -83,9 +83,9 @@ Stephh's notes (based on the games M68000 code and some tests) : #include "cpu/m68000/m68000.h" #include "cpu/m6502/m6502.h" #include "machine/input_merger.h" -#include "sound/3526intf.h" -#include "sound/3812intf.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/legionna.cpp b/src/mame/drivers/legionna.cpp index bf8e6abe45d..cc11647cf89 100644 --- a/src/mame/drivers/legionna.cpp +++ b/src/mame/drivers/legionna.cpp @@ -87,8 +87,8 @@ Preliminary COP MCU memory map #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/ym2151.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/lordgun.cpp b/src/mame/drivers/lordgun.cpp index 2b028fba58b..b565385ae0b 100644 --- a/src/mame/drivers/lordgun.cpp +++ b/src/mame/drivers/lordgun.cpp @@ -45,7 +45,7 @@ Notes: #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "machine/i8255.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "sound/ymf278b.h" #include "speaker.h" diff --git a/src/mame/drivers/matmania.cpp b/src/mame/drivers/matmania.cpp index cfb07b2d09b..c78812f54a2 100644 --- a/src/mame/drivers/matmania.cpp +++ b/src/mame/drivers/matmania.cpp @@ -36,9 +36,9 @@ The driver has been updated accordingly. #include "cpu/m6502/m6502.h" #include "cpu/m6809/m6809.h" -#include "sound/3526intf.h" #include "sound/ay8910.h" #include "sound/dac.h" +#include "sound/ym3526.h" #include "speaker.h" diff --git a/src/mame/drivers/mephistp.cpp b/src/mame/drivers/mephistp.cpp index c7c13cb6dcf..bb169df4246 100644 --- a/src/mame/drivers/mephistp.cpp +++ b/src/mame/drivers/mephistp.cpp @@ -18,7 +18,7 @@ #include "machine/nvram.h" #include "sound/ay8910.h" #include "sound/dac.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "speaker.h" // mephisto_state was also defined in mess/drivers/mephisto.c diff --git a/src/mame/drivers/metlclsh.cpp b/src/mame/drivers/metlclsh.cpp index dc9d3fa6b00..0e0dacd199f 100644 --- a/src/mame/drivers/metlclsh.cpp +++ b/src/mame/drivers/metlclsh.cpp @@ -37,8 +37,8 @@ metlclsh: #include "includes/metlclsh.h" #include "cpu/m6809/m6809.h" -#include "sound/3526intf.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/missb2.cpp b/src/mame/drivers/missb2.cpp index c2741d99330..bb9e974cf8b 100644 --- a/src/mame/drivers/missb2.cpp +++ b/src/mame/drivers/missb2.cpp @@ -18,8 +18,8 @@ written, so it may be normal behaviour. #include "includes/bublbobl.h" #include "cpu/z80/z80.h" -#include "sound/3526intf.h" #include "sound/okim6295.h" +#include "sound/ym3526.h" #include "machine/watchdog.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/mitchell.cpp b/src/mame/drivers/mitchell.cpp index 96b4b466e8c..cbf911e4701 100644 --- a/src/mame/drivers/mitchell.cpp +++ b/src/mame/drivers/mitchell.cpp @@ -123,9 +123,9 @@ mw-9.rom = ST M27C1001 / GFX #include "cpu/z80/z80.h" #include "machine/kabuki.h" // needed for decoding functions only #include "sound/okim6295.h" -#include "sound/3812intf.h" #include "sound/msm5205.h" #include "sound/ym2413.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" @@ -350,8 +350,8 @@ void mitchell_state::mitchell_io_map(address_map &map) map(0x00, 0x02).r(FUNC(mitchell_state::input_r)); /* The Mahjong games and Block Block need special input treatment */ map(0x01, 0x01).w(FUNC(mitchell_state::input_w)); map(0x02, 0x02).w(FUNC(mitchell_state::pang_bankswitch_w)); /* Code bank register */ - map(0x03, 0x03).w("ymsnd", FUNC(ym2413_device::data_port_w)); - map(0x04, 0x04).w("ymsnd", FUNC(ym2413_device::register_port_w)); + map(0x03, 0x03).w("ymsnd", FUNC(ym2413_device::data_w)); + map(0x04, 0x04).w("ymsnd", FUNC(ym2413_device::address_w)); map(0x05, 0x05).r(FUNC(mitchell_state::pang_port5_r)).w(m_oki, FUNC(okim6295_device::write)); map(0x06, 0x06).noprw(); /* watchdog? IRQ ack? video buffering? */ map(0x07, 0x07).w(FUNC(mitchell_state::pang_video_bank_w)); /* Video RAM bank register */ @@ -455,7 +455,7 @@ void mitchell_state::pkladiesbl_io_map(address_map &map) // TODO: check everythi { map.global_mask(0xff); map(0x00, 0x00).portr("IN0").w(FUNC(mitchell_state::pang_gfxctrl_w)); /* Palette bank, layer enable, coin counters, more */ - map(0x01, 0x01).portr("IN1").w("ymsnd", FUNC(ym2413_device::register_port_w)); // TODO: hold buttons are here, multiplexed but not in the same way as the original + map(0x01, 0x01).portr("IN1").w("ymsnd", FUNC(ym2413_device::address_w)); // TODO: hold buttons are here, multiplexed but not in the same way as the original map(0x02, 0x02).portr("IN2").w(FUNC(mitchell_state::pang_bankswitch_w)); /* Code bank register */ map(0x03, 0x03).portr("DSW0"); map(0x04, 0x04).portr("DSW1"); @@ -463,7 +463,7 @@ void mitchell_state::pkladiesbl_io_map(address_map &map) // TODO: check everythi map(0x06, 0x06).noprw(); /* watchdog? IRQ ack? video buffering? */ map(0x07, 0x07).w(FUNC(mitchell_state::pang_video_bank_w)); /* Video RAM bank register */ map(0x08, 0x08).w(FUNC(mitchell_state::eeprom_cs_w)); - map(0x09, 0x09).w("ymsnd", FUNC(ym2413_device::data_port_w)); + map(0x09, 0x09).w("ymsnd", FUNC(ym2413_device::data_w)); map(0x10, 0x10).w(FUNC(mitchell_state::eeprom_clock_w)); map(0x18, 0x18).w(FUNC(mitchell_state::eeprom_serial_w)); } diff --git a/src/mame/drivers/nbmj8688.cpp b/src/mame/drivers/nbmj8688.cpp index dda0bc7b032..a821e992a2a 100644 --- a/src/mame/drivers/nbmj8688.cpp +++ b/src/mame/drivers/nbmj8688.cpp @@ -35,9 +35,9 @@ TODO: #include "cpu/z80/z80.h" #include "machine/nvram.h" -#include "sound/3812intf.h" #include "sound/ay8910.h" #include "sound/dac.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/nbmj8891.cpp b/src/mame/drivers/nbmj8891.cpp index 2769e2f61f8..1c13574e12e 100644 --- a/src/mame/drivers/nbmj8891.cpp +++ b/src/mame/drivers/nbmj8891.cpp @@ -45,9 +45,9 @@ TODO: #include "cpu/z80/z80.h" #include "machine/nvram.h" -#include "sound/3812intf.h" #include "sound/ay8910.h" #include "sound/dac.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/nbmj8900.cpp b/src/mame/drivers/nbmj8900.cpp index 6ae3e270906..51da2345fb2 100644 --- a/src/mame/drivers/nbmj8900.cpp +++ b/src/mame/drivers/nbmj8900.cpp @@ -29,8 +29,8 @@ TODO: #include "includes/nbmj8900.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/dac.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/nbmj8991.cpp b/src/mame/drivers/nbmj8991.cpp index 3d35485eb13..1744ebe0742 100644 --- a/src/mame/drivers/nbmj8991.cpp +++ b/src/mame/drivers/nbmj8991.cpp @@ -35,9 +35,9 @@ Notes: #include "cpu/z80/z80.h" #include "machine/nvram.h" -#include "sound/3812intf.h" #include "sound/ay8910.h" #include "sound/dac.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/nbmj9195.cpp b/src/mame/drivers/nbmj9195.cpp index 909ee3d8dfe..8a75765f544 100644 --- a/src/mame/drivers/nbmj9195.cpp +++ b/src/mame/drivers/nbmj9195.cpp @@ -26,8 +26,8 @@ Notes: #include "machine/gen_latch.h" #include "machine/nvram.h" -#include "sound/3812intf.h" #include "sound/dac.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/nemesis.cpp b/src/mame/drivers/nemesis.cpp index aede8553117..d3317e4a18d 100644 --- a/src/mame/drivers/nemesis.cpp +++ b/src/mame/drivers/nemesis.cpp @@ -115,10 +115,10 @@ initials #include "machine/gen_latch.h" #include "machine/rescap.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" #include "sound/ay8910.h" #include "sound/k051649.h" #include "sound/ym2151.h" +#include "sound/ym3812.h" #include "speaker.h" #include "konamigt.lh" diff --git a/src/mame/drivers/nmg5.cpp b/src/mame/drivers/nmg5.cpp index c887aaa54f1..02d52d46084 100644 --- a/src/mame/drivers/nmg5.cpp +++ b/src/mame/drivers/nmg5.cpp @@ -225,8 +225,8 @@ Stephh's notes (based on the games M68000 code and some tests) : #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "machine/gen_latch.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "video/decospr.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/nmk16.cpp b/src/mame/drivers/nmk16.cpp index ae51adef7b7..a1ea370b948 100644 --- a/src/mame/drivers/nmk16.cpp +++ b/src/mame/drivers/nmk16.cpp @@ -204,10 +204,10 @@ Reference of music tempo: #include "cpu/z80/z80.h" #include "machine/nmk004.h" #include "machine/nmk112.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2151.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/oneshot.cpp b/src/mame/drivers/oneshot.cpp index a3e8aa41971..20ff5c7e8a7 100644 --- a/src/mame/drivers/oneshot.cpp +++ b/src/mame/drivers/oneshot.cpp @@ -131,8 +131,8 @@ Clock measurements: #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "machine/gen_latch.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/onetwo.cpp b/src/mame/drivers/onetwo.cpp index 777448e47a4..735e3e93a61 100644 --- a/src/mame/drivers/onetwo.cpp +++ b/src/mame/drivers/onetwo.cpp @@ -45,8 +45,8 @@ Note: this is quite clearly a 'Korean bootleg' of Shisensho - Joshiryo-Hen / Mat #include "cpu/z80/z80.h" #include "machine/gen_latch.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "emupal.h" #include "screen.h" #include "speaker.h" @@ -198,8 +198,8 @@ void onetwo_state::sound_cpu(address_map &map) void onetwo_state::sound_cpu_io(address_map &map) { map.global_mask(0xff); - map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::write_port_w)); + map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::data_w)); map(0x40, 0x40).rw("oki", FUNC(okim6295_device::read), FUNC(okim6295_device::write)); map(0xc0, 0xc0).w(m_soundlatch, FUNC(generic_latch_8_device::acknowledge_w)); } diff --git a/src/mame/drivers/pcktgal.cpp b/src/mame/drivers/pcktgal.cpp index 0ca4ff69e83..c2be156ea1c 100644 --- a/src/mame/drivers/pcktgal.cpp +++ b/src/mame/drivers/pcktgal.cpp @@ -18,8 +18,8 @@ #include "includes/pcktgal.h" #include "cpu/m6502/m6502.h" -#include "sound/3812intf.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "machine/deco222.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/pokechmp.cpp b/src/mame/drivers/pokechmp.cpp index 3fe9eb19d77..a5b250a5f2a 100644 --- a/src/mame/drivers/pokechmp.cpp +++ b/src/mame/drivers/pokechmp.cpp @@ -44,9 +44,9 @@ ClawGrip, Jul 2006 #include "includes/pokechmp.h" #include "cpu/m6502/m6502.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/prehisle.cpp b/src/mame/drivers/prehisle.cpp index d28e5b87521..e21ee218ba5 100644 --- a/src/mame/drivers/prehisle.cpp +++ b/src/mame/drivers/prehisle.cpp @@ -16,7 +16,7 @@ #include "cpu/z80/z80.h" #include "cpu/m68000/m68000.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" @@ -75,8 +75,8 @@ void prehisle_state::prehisle_sound_map(address_map &map) void prehisle_state::prehisle_sound_io_map(address_map &map) { map.global_mask(0xff); - map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::write_port_w)); + map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::data_w)); map(0x40, 0x40).w(FUNC(prehisle_state::upd_port_w)); map(0x80, 0x80).lw8(NAME([this] (u8 data) { m_upd7759->reset_w(BIT(data, 7)); })); } diff --git a/src/mame/drivers/r2dx_v33.cpp b/src/mame/drivers/r2dx_v33.cpp index 69ab307b2c0..484f9e13205 100644 --- a/src/mame/drivers/r2dx_v33.cpp +++ b/src/mame/drivers/r2dx_v33.cpp @@ -68,9 +68,8 @@ Then it puts settings at 0x9e08 and 0x9e0a (bp 91acb) #include "cpu/nec/nec.h" #include "cpu/z80/z80.h" #include "machine/eepromser.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" -//#include "sound/ym2151.h" +#include "sound/ym3812.h" #include "machine/r2crypt.h" #include "speaker.h" diff --git a/src/mame/drivers/raiden.cpp b/src/mame/drivers/raiden.cpp index 585adbf3d67..e4564c2fd31 100644 --- a/src/mame/drivers/raiden.cpp +++ b/src/mame/drivers/raiden.cpp @@ -78,8 +78,8 @@ #include "cpu/nec/nec.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "video/seibu_crtc.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/raiden2.cpp b/src/mame/drivers/raiden2.cpp index 066d049bc6a..202f2ad376a 100644 --- a/src/mame/drivers/raiden2.cpp +++ b/src/mame/drivers/raiden2.cpp @@ -174,9 +174,9 @@ Protection Notes: #include "cpu/nec/nec.h" #include "cpu/z80/z80.h" #include "machine/eepromser.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2151.h" +#include "sound/ym3812.h" #include "machine/r2crypt.h" #include "debugger.h" diff --git a/src/mame/drivers/renegade.cpp b/src/mame/drivers/renegade.cpp index 0e8eef9e9fc..14e4b2ec327 100644 --- a/src/mame/drivers/renegade.cpp +++ b/src/mame/drivers/renegade.cpp @@ -119,7 +119,7 @@ $8000 - $ffff ROM #include "cpu/m6502/m6502.h" #include "cpu/m6809/m6809.h" -#include "sound/3526intf.h" +#include "sound/ym3526.h" #include "emupal.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/rollerg.cpp b/src/mame/drivers/rollerg.cpp index a32ac3efe82..4faae5fa505 100644 --- a/src/mame/drivers/rollerg.cpp +++ b/src/mame/drivers/rollerg.cpp @@ -17,8 +17,8 @@ #include "cpu/z80/z80.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" #include "sound/k053260.h" +#include "sound/ym3812.h" #include "emupal.h" #include "speaker.h" diff --git a/src/mame/drivers/sauro.cpp b/src/mame/drivers/sauro.cpp index f7700e8bf17..01311d12f32 100644 --- a/src/mame/drivers/sauro.cpp +++ b/src/mame/drivers/sauro.cpp @@ -131,7 +131,7 @@ Stephh's notes (based on the games Z80 code and some tests) : #include "cpu/z80/z80.h" #include "machine/nvram.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/sengokmj.cpp b/src/mame/drivers/sengokmj.cpp index 4882ee4038d..56bcf37dd49 100644 --- a/src/mame/drivers/sengokmj.cpp +++ b/src/mame/drivers/sengokmj.cpp @@ -58,8 +58,8 @@ RSSENGO2.72 chr. #include "cpu/nec/nec.h" #include "machine/nvram.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "video/seibu_crtc.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/seta.cpp b/src/mame/drivers/seta.cpp index cef8dc12c97..b838f28b2eb 100644 --- a/src/mame/drivers/seta.cpp +++ b/src/mame/drivers/seta.cpp @@ -1421,11 +1421,11 @@ Note: on screen copyright is (c)1998 Coinmaster. #include "machine/nvram.h" #include "machine/pit8253.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2151.h" #include "sound/ym2203.h" #include "sound/ym2612.h" +#include "sound/ym3812.h" #include "diserial.h" #include "screen.h" diff --git a/src/mame/drivers/sidepckt.cpp b/src/mame/drivers/sidepckt.cpp index a727bf27254..50004db0d79 100644 --- a/src/mame/drivers/sidepckt.cpp +++ b/src/mame/drivers/sidepckt.cpp @@ -136,8 +136,8 @@ Additional notes: #include "cpu/m6809/m6809.h" #include "cpu/m6502/m6502.h" -#include "sound/3526intf.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/sigmab31.cpp b/src/mame/drivers/sigmab31.cpp index 209d6f96ada..ffad2d67198 100644 --- a/src/mame/drivers/sigmab31.cpp +++ b/src/mame/drivers/sigmab31.cpp @@ -95,7 +95,7 @@ chip at location ic50 28 pin dip stamped Hitachi logo? 1A1 R #include "machine/6840ptm.h" #include "machine/6850acia.h" #include "machine/nvram.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/sigmab52.cpp b/src/mame/drivers/sigmab52.cpp index f6019846879..df7e8b6068f 100644 --- a/src/mame/drivers/sigmab52.cpp +++ b/src/mame/drivers/sigmab52.cpp @@ -111,7 +111,7 @@ #include "machine/6850acia.h" #include "machine/gen_latch.h" #include "machine/nvram.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "video/hd63484.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/snk.cpp b/src/mame/drivers/snk.cpp index 039680dda14..f47f37c77bf 100644 --- a/src/mame/drivers/snk.cpp +++ b/src/mame/drivers/snk.cpp @@ -578,9 +578,9 @@ TODO: #include "cpu/z80/z80.h" #include "sound/snkwave.h" #include "sound/ay8910.h" -#include "sound/3526intf.h" -#include "sound/3812intf.h" -#include "sound/8950intf.h" +#include "sound/y8950.h" +#include "sound/ym3526.h" +#include "sound/ym3812.h" #include "speaker.h" @@ -1760,10 +1760,10 @@ void snk_state::YM3526_YM3526_sound_map(address_map &map) map(0x0000, 0xbfff).rom(); map(0xc000, 0xcfff).ram(); map(0xe000, 0xe000).r(m_soundlatch, FUNC(generic_latch_8_device::read)); - map(0xe800, 0xe800).rw("ym1", FUNC(ym3526_device::status_port_r), FUNC(ym3526_device::control_port_w)); - map(0xec00, 0xec00).w("ym1", FUNC(ym3526_device::write_port_w)); - map(0xf000, 0xf000).rw("ym2", FUNC(ym3526_device::status_port_r), FUNC(ym3526_device::control_port_w)); - map(0xf400, 0xf400).w("ym2", FUNC(ym3526_device::write_port_w)); + map(0xe800, 0xe800).rw("ym1", FUNC(ym3526_device::status_r), FUNC(ym3526_device::address_w)); + map(0xec00, 0xec00).w("ym1", FUNC(ym3526_device::data_w)); + map(0xf000, 0xf000).rw("ym2", FUNC(ym3526_device::status_r), FUNC(ym3526_device::address_w)); + map(0xf400, 0xf400).w("ym2", FUNC(ym3526_device::data_w)); map(0xf800, 0xf800).rw(FUNC(snk_state::snk_sound_status_r), FUNC(snk_state::snk_sound_status_w)); } @@ -1772,8 +1772,8 @@ void snk_state::YM3812_sound_map(address_map &map) map(0x0000, 0xbfff).rom(); map(0xc000, 0xcfff).ram(); map(0xe000, 0xe000).r(m_soundlatch, FUNC(generic_latch_8_device::read)); - map(0xe800, 0xe800).rw("ym1", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0xec00, 0xec00).w("ym1", FUNC(ym3812_device::write_port_w)); + map(0xe800, 0xe800).rw("ym1", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0xec00, 0xec00).w("ym1", FUNC(ym3812_device::data_w)); map(0xf800, 0xf800).rw(FUNC(snk_state::snk_sound_status_r), FUNC(snk_state::snk_sound_status_w)); } @@ -1782,10 +1782,10 @@ void snk_state::YM3526_Y8950_sound_map(address_map &map) map(0x0000, 0xbfff).rom(); map(0xc000, 0xcfff).ram(); map(0xe000, 0xe000).r(m_soundlatch, FUNC(generic_latch_8_device::read)); - map(0xe800, 0xe800).rw("ym1", FUNC(ym3526_device::status_port_r), FUNC(ym3526_device::control_port_w)); - map(0xec00, 0xec00).w("ym1", FUNC(ym3526_device::write_port_w)); - map(0xf000, 0xf000).rw("ym2", FUNC(y8950_device::status_port_r), FUNC(y8950_device::control_port_w)); - map(0xf400, 0xf400).w("ym2", FUNC(y8950_device::write_port_w)); + map(0xe800, 0xe800).rw("ym1", FUNC(ym3526_device::status_r), FUNC(ym3526_device::address_w)); + map(0xec00, 0xec00).w("ym1", FUNC(ym3526_device::data_w)); + map(0xf000, 0xf000).rw("ym2", FUNC(y8950_device::status_r), FUNC(y8950_device::address_w)); + map(0xf400, 0xf400).w("ym2", FUNC(y8950_device::data_w)); map(0xf800, 0xf800).rw(FUNC(snk_state::snk_sound_status_r), FUNC(snk_state::snk_sound_status_w)); } @@ -1794,10 +1794,10 @@ void snk_state::YM3812_Y8950_sound_map(address_map &map) map(0x0000, 0xbfff).rom(); map(0xc000, 0xcfff).ram(); map(0xe000, 0xe000).r(m_soundlatch, FUNC(generic_latch_8_device::read)); - map(0xe800, 0xe800).rw("ym1", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0xec00, 0xec00).w("ym1", FUNC(ym3812_device::write_port_w)); - map(0xf000, 0xf000).rw("ym2", FUNC(y8950_device::status_port_r), FUNC(y8950_device::control_port_w)); - map(0xf400, 0xf400).w("ym2", FUNC(y8950_device::write_port_w)); + map(0xe800, 0xe800).rw("ym1", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0xec00, 0xec00).w("ym1", FUNC(ym3812_device::data_w)); + map(0xf000, 0xf000).rw("ym2", FUNC(y8950_device::status_r), FUNC(y8950_device::address_w)); + map(0xf400, 0xf400).w("ym2", FUNC(y8950_device::data_w)); map(0xf800, 0xf800).rw(FUNC(snk_state::snk_sound_status_r), FUNC(snk_state::snk_sound_status_w)); } @@ -1806,8 +1806,8 @@ void snk_state::Y8950_sound_map(address_map &map) map(0x0000, 0xbfff).rom(); map(0xc000, 0xcfff).ram(); map(0xe000, 0xe000).r(m_soundlatch, FUNC(generic_latch_8_device::read)); - map(0xf000, 0xf000).rw("ym2", FUNC(y8950_device::status_port_r), FUNC(y8950_device::control_port_w)); - map(0xf400, 0xf400).w("ym2", FUNC(y8950_device::write_port_w)); + map(0xf000, 0xf000).rw("ym2", FUNC(y8950_device::status_r), FUNC(y8950_device::address_w)); + map(0xf400, 0xf400).w("ym2", FUNC(y8950_device::data_w)); map(0xf800, 0xf800).rw(FUNC(snk_state::snk_sound_status_r), FUNC(snk_state::snk_sound_status_w)); } @@ -4216,7 +4216,7 @@ void snk_state::victroad(machine_config &config) /* sound hardware */ y8950_device &ym2(Y8950(config.replace(), "ym2", XTAL(8'000'000)/2)); /* verified on pcb */ - ym2.irq().set(FUNC(snk_state::ymirq_callback_2)); + ym2.irq_handler().set(FUNC(snk_state::ymirq_callback_2)); ym2.add_route(ALL_OUTPUTS, "mono", 2.0); } @@ -4260,7 +4260,7 @@ void snk_state::bermudat(machine_config &config) ym1.add_route(ALL_OUTPUTS, "mono", 2.0); y8950_device &ym2(Y8950(config, "ym2", XTAL(8'000'000)/2)); /* verified on pcb */ - ym2.irq().set(FUNC(snk_state::ymirq_callback_2)); + ym2.irq_handler().set(FUNC(snk_state::ymirq_callback_2)); ym2.add_route(ALL_OUTPUTS, "mono", 2.0); } @@ -4342,7 +4342,7 @@ void snk_state::tdfever(machine_config &config) ym1.add_route(ALL_OUTPUTS, "mono", 1.0); y8950_device &ym2(Y8950(config, "ym2", 4000000)); - ym2.irq().set(FUNC(snk_state::ymirq_callback_2)); + ym2.irq_handler().set(FUNC(snk_state::ymirq_callback_2)); ym2.add_route(ALL_OUTPUTS, "mono", 1.0); } diff --git a/src/mame/drivers/snk68.cpp b/src/mame/drivers/snk68.cpp index 170eb2addc1..7c5b561a6d6 100644 --- a/src/mame/drivers/snk68.cpp +++ b/src/mame/drivers/snk68.cpp @@ -47,8 +47,8 @@ Notes: #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/msm5205.h" +#include "sound/ym3812.h" #include "emupal.h" #include "speaker.h" @@ -152,8 +152,8 @@ void snk68_state::D7759_write_port_0_w(uint8_t data) void snk68_state::sound_io_map(address_map &map) { map.global_mask(0xff); - map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::write_port_w)); + map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::data_w)); map(0x40, 0x40).w(FUNC(snk68_state::D7759_write_port_0_w)); map(0x80, 0x80).lw8(NAME([this] (u8 data) { m_upd7759->reset_w(BIT(data, 7)); } )); } @@ -161,8 +161,8 @@ void snk68_state::sound_io_map(address_map &map) void snk68_state::powb_sound_io_map(address_map &map) { map.global_mask(0xff); - map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::write_port_w)); + map(0x00, 0x00).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x20, 0x20).w("ymsnd", FUNC(ym3812_device::data_w)); } /******************************************************************************/ diff --git a/src/mame/drivers/snowbros.cpp b/src/mame/drivers/snowbros.cpp index 2dfb9a1a4df..481573e3163 100644 --- a/src/mame/drivers/snowbros.cpp +++ b/src/mame/drivers/snowbros.cpp @@ -81,8 +81,8 @@ a joystick. This is not an emulation bug. #include "cpu/mcs51/mcs51.h" // for semicom mcu #include "cpu/z80/z80.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" #include "sound/ym2151.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/spbactn.cpp b/src/mame/drivers/spbactn.cpp index fd81f429bef..f3bd6a4d9ce 100644 --- a/src/mame/drivers/spbactn.cpp +++ b/src/mame/drivers/spbactn.cpp @@ -136,8 +136,8 @@ cpu #0 (PC=00001A1A): unmapped memory word write to 00090030 = 00F7 & 00FF #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/spdodgeb.cpp b/src/mame/drivers/spdodgeb.cpp index c2af7ff0693..1a839aa959f 100644 --- a/src/mame/drivers/spdodgeb.cpp +++ b/src/mame/drivers/spdodgeb.cpp @@ -28,7 +28,7 @@ Notes: #include "cpu/m6502/m6502.h" #include "cpu/m6800/m6801.h" #include "cpu/m6809/m6809.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/speedbal.cpp b/src/mame/drivers/speedbal.cpp index 2c6f79c6df0..85dfb06cb4b 100644 --- a/src/mame/drivers/speedbal.cpp +++ b/src/mame/drivers/speedbal.cpp @@ -33,7 +33,7 @@ Interrupt frequency on audio CPU is not a periodical signal, but there are a lot #include "includes/speedbal.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/splash.cpp b/src/mame/drivers/splash.cpp index 045c81c8a9f..f00ae2a8c49 100644 --- a/src/mame/drivers/splash.cpp +++ b/src/mame/drivers/splash.cpp @@ -50,8 +50,8 @@ More notes about Funny Strip protection issues at the bottom of source file (ini #include "cpu/z80/z80.h" #include "cpu/m68000/m68000.h" -#include "sound/3812intf.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/splash_ms.cpp b/src/mame/drivers/splash_ms.cpp index f16b9504ccb..4128071a99a 100644 --- a/src/mame/drivers/splash_ms.cpp +++ b/src/mame/drivers/splash_ms.cpp @@ -14,7 +14,7 @@ #include "speaker.h" #include "tilemap.h" #include "sound/msm5205.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "machine/gen_latch.h" #include "machine/bankdev.h" diff --git a/src/mame/drivers/spy.cpp b/src/mame/drivers/spy.cpp index 62fa497c1e6..8098c63308f 100644 --- a/src/mame/drivers/spy.cpp +++ b/src/mame/drivers/spy.cpp @@ -27,7 +27,7 @@ #include "cpu/z80/z80.h" #include "machine/gen_latch.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/stadhero.cpp b/src/mame/drivers/stadhero.cpp index 6e9679e589d..87e78216670 100644 --- a/src/mame/drivers/stadhero.cpp +++ b/src/mame/drivers/stadhero.cpp @@ -94,9 +94,9 @@ #include "cpu/m68000/m68000.h" #include "cpu/m6502/m6502.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "emupal.h" #include "speaker.h" diff --git a/src/mame/drivers/subsino.cpp b/src/mame/drivers/subsino.cpp index e0e9044ecd4..73db1cafa6c 100644 --- a/src/mame/drivers/subsino.cpp +++ b/src/mame/drivers/subsino.cpp @@ -228,9 +228,9 @@ To Do: #include "machine/nvram.h" #include "machine/subsino.h" #include "machine/ticket.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2413.h" +#include "sound/ym3812.h" #include "video/ramdac.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/subsino2.cpp b/src/mame/drivers/subsino2.cpp index 3fd82e606f7..726ee99f967 100644 --- a/src/mame/drivers/subsino2.cpp +++ b/src/mame/drivers/subsino2.cpp @@ -49,8 +49,8 @@ To do: #include "machine/nvram.h" #include "machine/subsino.h" #include "machine/ticket.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "video/ramdac.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/suna16.cpp b/src/mame/drivers/suna16.cpp index d7dc54fe370..6dfe218b395 100644 --- a/src/mame/drivers/suna16.cpp +++ b/src/mame/drivers/suna16.cpp @@ -29,10 +29,10 @@ Year + Game By Board Hardware #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" -#include "sound/3526intf.h" #include "sound/ay8910.h" #include "sound/dac.h" #include "sound/ym2151.h" +#include "sound/ym3526.h" #include "speaker.h" diff --git a/src/mame/drivers/suna8.cpp b/src/mame/drivers/suna8.cpp index 5fb069b75d1..f4fad3465ff 100644 --- a/src/mame/drivers/suna8.cpp +++ b/src/mame/drivers/suna8.cpp @@ -40,10 +40,10 @@ Notes: #include "cpu/z80/z80.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" #include "sound/ay8910.h" #include "sound/dac.h" #include "sound/ym2203.h" +#include "sound/ym3812.h" #include "speaker.h" @@ -1299,7 +1299,7 @@ void suna8_state::hardhead_sound_map(address_map &map) map(0xa000, 0xa001).rw("ymsnd", FUNC(ym3812_device::read), FUNC(ym3812_device::write)); map(0xa002, 0xa003).w("aysnd", FUNC(ay8910_device::address_data_w)); map(0xc000, 0xc7ff).ram(); // RAM - map(0xc800, 0xc800).r("ymsnd", FUNC(ym3812_device::status_port_r)); // ? unsure + map(0xc800, 0xc800).r("ymsnd", FUNC(ym3812_device::status_r)); // ? unsure map(0xd000, 0xd000).w(m_soundlatch2, FUNC(generic_latch_8_device::write)); // map(0xd800, 0xd800).r(m_soundlatch, FUNC(generic_latch_8_device::read)); // From Main CPU } diff --git a/src/mame/drivers/tbowl.cpp b/src/mame/drivers/tbowl.cpp index c51ee2c76e3..59247dc2169 100644 --- a/src/mame/drivers/tbowl.cpp +++ b/src/mame/drivers/tbowl.cpp @@ -18,7 +18,7 @@ Might be some priority glitches #include "includes/tbowl.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "layout/generic.h" #include "screen.h" diff --git a/src/mame/drivers/tecmo.cpp b/src/mame/drivers/tecmo.cpp index 2c754fe709f..b75ea23d208 100644 --- a/src/mame/drivers/tecmo.cpp +++ b/src/mame/drivers/tecmo.cpp @@ -232,8 +232,8 @@ Notes: #include "cpu/z80/z80.h" #include "machine/gen_latch.h" #include "machine/watchdog.h" -#include "sound/3526intf.h" -#include "sound/3812intf.h" +#include "sound/ym3526.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/tecmosys.cpp b/src/mame/drivers/tecmosys.cpp index 5c7498e532c..0f8c3dc930e 100644 --- a/src/mame/drivers/tecmosys.cpp +++ b/src/mame/drivers/tecmosys.cpp @@ -191,8 +191,8 @@ ae500w07.ad1 - M6295 Samples (23c4001) #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" -#include "sound/262intf.h" #include "sound/okim6295.h" +#include "sound/ymf262.h" #include "sound/ymz280b.h" #include "speaker.h" diff --git a/src/mame/drivers/terracre.cpp b/src/mame/drivers/terracre.cpp index a804bc3e43d..8310c13f78e 100644 --- a/src/mame/drivers/terracre.cpp +++ b/src/mame/drivers/terracre.cpp @@ -85,9 +85,9 @@ AT-2 #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" -#include "sound/3526intf.h" #include "sound/dac.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/tmnt.cpp b/src/mame/drivers/tmnt.cpp index 4b681d94d8d..84e3affa34d 100644 --- a/src/mame/drivers/tmnt.cpp +++ b/src/mame/drivers/tmnt.cpp @@ -73,6 +73,7 @@ Updates: #include "sound/okim6295.h" #include "sound/samples.h" #include "sound/ym2151.h" +#include "sound/ymfm.h" #include "speaker.h" @@ -230,25 +231,11 @@ SAMPLES_START_CB_MEMBER(tmnt_state::tmnt_decode_sample) int i; uint8_t *source = memregion("title")->base(); - /* Sound sample for TMNT.D05 is stored in the following mode (ym3012 format): - * - * Bit 15-13: Exponent (2 ^ x) - * Bit 12-3 : Sound data (10 bit) - * - * (Sound info courtesy of Dave ) - */ - + // sample data is encoded in Yamaha FP format for (i = 0; i < 0x40000; i++) { int val = source[2 * i] + source[2 * i + 1] * 256; - int expo = val >> 13; - - val = (val >> 3) & (0x3ff); /* 10 bit, Max Amplitude 0x400 */ - val -= 0x200; /* Centralize value */ - - val = (val << expo) >> 3; - - m_sampledata[i] = val; + m_sampledata[i] = ymfm_decode_fp(val >> 3); } } @@ -2093,7 +2080,7 @@ void tmnt_state::tmnt(machine_config &config) SAMPLES(config, m_samples); m_samples->set_channels(1); /* 1 channel for the title music */ m_samples->set_samples_start_callback(FUNC(tmnt_state::tmnt_decode_sample)); - m_samples->add_route(ALL_OUTPUTS, "mono", 1.0); + m_samples->add_route(ALL_OUTPUTS, "mono", 0.5); } void tmnt_state::punkshot(machine_config &config) diff --git a/src/mame/drivers/toaplan1.cpp b/src/mame/drivers/toaplan1.cpp index 1166b7aee5e..83fd2af94f6 100644 --- a/src/mame/drivers/toaplan1.cpp +++ b/src/mame/drivers/toaplan1.cpp @@ -973,8 +973,8 @@ void toaplan1_state::vimana_hd647180_io_map(address_map &map) map(0x82, 0x82).portr("DSWA"); map(0x83, 0x83).portr("SYSTEM"); map(0x84, 0x84).w(FUNC(toaplan1_state::coin_w)); // Coin counter/lockout // needs verify - map(0x87, 0x87).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x8f, 0x8f).rw("ymsnd", FUNC(ym3812_device::read_port_r), FUNC(ym3812_device::write_port_w)); + map(0x87, 0x87).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x8f, 0x8f).w("ymsnd", FUNC(ym3812_device::data_w)); } u8 toaplan1_state::vimana_dswb_invert_r() @@ -1018,8 +1018,7 @@ void toaplan1_samesame_state::hd647180_io_map(address_map &map) map(0xa0, 0xa0).r(FUNC(toaplan1_samesame_state::soundlatch_r)); map(0xb0, 0xb0).w(FUNC(toaplan1_samesame_state::sound_done_w)); - map(0x80, 0x80).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x81, 0x81).rw("ymsnd", FUNC(ym3812_device::read_port_r), FUNC(ym3812_device::write_port_w)); + map(0x80, 0x81).rw("ymsnd", FUNC(ym3812_device::read), FUNC(ym3812_device::write)); } /***************************************************************************** diff --git a/src/mame/drivers/toaplan2.cpp b/src/mame/drivers/toaplan2.cpp index e34a5d236f2..4bc4a5ba14e 100644 --- a/src/mame/drivers/toaplan2.cpp +++ b/src/mame/drivers/toaplan2.cpp @@ -397,8 +397,8 @@ To reset the NVRAM in Othello Derby, hold P1 Button 1 down while booting. #include "cpu/z80/z80.h" #include "cpu/z180/hd647180x.h" #include "machine/nvram.h" -#include "sound/3812intf.h" #include "sound/ym2151.h" +#include "sound/ym3812.h" #include "sound/ymz280b.h" #include "speaker.h" @@ -1487,8 +1487,8 @@ void toaplan2_state::hd647180_io_map(address_map &map) map(0x70, 0x75).nopw(); // DDRs are written with the wrong upper addresses! map(0x84, 0x84).r(m_soundlatch, FUNC(generic_latch_8_device::read)); - map(0x82, 0x82).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); - map(0x83, 0x83).rw("ymsnd", FUNC(ym3812_device::read_port_r), FUNC(ym3812_device::write_port_w)); + map(0x82, 0x82).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); + map(0x83, 0x83).w("ymsnd", FUNC(ym3812_device::data_w)); } diff --git a/src/mame/drivers/toki.cpp b/src/mame/drivers/toki.cpp index 02a99b40ae4..88f13f850e9 100644 --- a/src/mame/drivers/toki.cpp +++ b/src/mame/drivers/toki.cpp @@ -98,8 +98,8 @@ Notes: #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "machine/watchdog.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/tumbleb.cpp b/src/mame/drivers/tumbleb.cpp index 9f469e5c47a..2700f3d7b0d 100644 --- a/src/mame/drivers/tumbleb.cpp +++ b/src/mame/drivers/tumbleb.cpp @@ -307,9 +307,9 @@ Stephh's notes (based on the games M68000 code and some tests) : #include "cpu/mcs51/mcs51.h" // for semicom mcu #include "cpu/pic16c5x/pic16c5x.h" #include "machine/decocrpt.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2151.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/twincobr.cpp b/src/mame/drivers/twincobr.cpp index 2000483f20d..80649733cad 100644 --- a/src/mame/drivers/twincobr.cpp +++ b/src/mame/drivers/twincobr.cpp @@ -384,7 +384,7 @@ Shark Zame #include "cpu/m68000/m68000.h" #include "cpu/mcs48/mcs48.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/umipoker.cpp b/src/mame/drivers/umipoker.cpp index c9e907cce5b..fcbefe0e963 100644 --- a/src/mame/drivers/umipoker.cpp +++ b/src/mame/drivers/umipoker.cpp @@ -20,8 +20,8 @@ #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "machine/nvram.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" +#include "sound/ym3812.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/drivers/unico.cpp b/src/mame/drivers/unico.cpp index 16335789901..9f561c8d8fe 100644 --- a/src/mame/drivers/unico.cpp +++ b/src/mame/drivers/unico.cpp @@ -29,9 +29,9 @@ Year + Game PCB Notes #include "cpu/m68000/m68000.h" #include "machine/eepromser.h" -#include "sound/3812intf.h" #include "sound/okim6295.h" #include "sound/ym2151.h" +#include "sound/ym3812.h" #include "speaker.h" @@ -63,8 +63,8 @@ void unico_state::burglarx_map(address_map &map) map(0x800030, 0x800031).nopw(); // ? 0 map(0x80010c, 0x800121).rw(FUNC(unico_state::scroll_r), FUNC(unico_state::scroll_w)); // Scroll map(0x800189, 0x800189).rw(m_oki, FUNC(okim6295_device::read), FUNC(okim6295_device::write)); // Sound - map(0x80018a, 0x80018a).w("ymsnd", FUNC(ym3812_device::write_port_w)); - map(0x80018c, 0x80018c).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); + map(0x80018a, 0x80018a).w("ymsnd", FUNC(ym3812_device::data_w)); + map(0x80018c, 0x80018c).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); map(0x80018e, 0x80018e).w(FUNC(unico_state::burglarx_okibank_w)); // map(0x8001e0, 0x8001e1).nopw(); // IRQ Ack map(0x904000, 0x90ffff).rw(FUNC(unico_state::vram_r), FUNC(unico_state::vram_w)); // Layers 1, 2, 0 @@ -146,8 +146,8 @@ void zeropnt_state::zeropnt_map(address_map &map) map(0x800178, 0x800179).r(FUNC(zeropnt_state::guny_1_msb_r)); // map(0x80017c, 0x80017d).r(FUNC(zeropnt_state::gunx_1_msb_r)); // map(0x800189, 0x800189).rw("oki", FUNC(okim6295_device::read), FUNC(okim6295_device::write)); // Sound - map(0x80018a, 0x80018a).w("ymsnd", FUNC(ym3812_device::write_port_w)); - map(0x80018c, 0x80018c).rw("ymsnd", FUNC(ym3812_device::status_port_r), FUNC(ym3812_device::control_port_w)); + map(0x80018a, 0x80018a).w("ymsnd", FUNC(ym3812_device::data_w)); + map(0x80018c, 0x80018c).rw("ymsnd", FUNC(ym3812_device::status_r), FUNC(ym3812_device::address_w)); map(0x80018e, 0x80018e).w(FUNC(zeropnt_state::zeropnt_okibank_leds_w)); // map(0x8001e0, 0x8001e1).nopw(); // ? IRQ Ack map(0x904000, 0x90ffff).rw(FUNC(zeropnt_state::vram_r), FUNC(zeropnt_state::vram_w)); // Layers 1, 2, 0 diff --git a/src/mame/drivers/vgmplay.cpp b/src/mame/drivers/vgmplay.cpp index dafabd67bf5..3ea1d4f88c9 100644 --- a/src/mame/drivers/vgmplay.cpp +++ b/src/mame/drivers/vgmplay.cpp @@ -14,10 +14,6 @@ #include "cpu/m6502/n2a03.h" #include "cpu/m68000/m68000.h" #include "cpu/sh/sh2.h" -#include "sound/262intf.h" -#include "sound/3526intf.h" -#include "sound/3812intf.h" -#include "sound/8950intf.h" #include "sound/ay8910.h" #include "sound/c140.h" #include "sound/c352.h" @@ -42,13 +38,17 @@ #include "sound/upd7759.h" #include "sound/vgm_visualizer.h" #include "sound/x1_010.h" +#include "sound/y8950.h" #include "sound/ym2151.h" #include "sound/ym2203.h" #include "sound/ym2413.h" #include "sound/ym2608.h" #include "sound/ym2610.h" #include "sound/ym2612.h" +#include "sound/ym3526.h" +#include "sound/ym3812.h" #include "sound/ymf271.h" +#include "sound/ymf262.h" #include "sound/ymf278b.h" #include "sound/ymz280b.h" #include "audio/vboy.h" diff --git a/src/mame/drivers/vis.cpp b/src/mame/drivers/vis.cpp index c1d60446242..88435f75906 100644 --- a/src/mame/drivers/vis.cpp +++ b/src/mame/drivers/vis.cpp @@ -6,8 +6,8 @@ #include "cpu/i86/i286.h" #include "machine/8042kbdc.h" #include "machine/at.h" -#include "sound/262intf.h" #include "sound/dac.h" +#include "sound/ymf262.h" #include "video/pc_vga.h" #include "speaker.h" diff --git a/src/mame/drivers/wardner.cpp b/src/mame/drivers/wardner.cpp index 05c392f972d..04e1b6549ed 100644 --- a/src/mame/drivers/wardner.cpp +++ b/src/mame/drivers/wardner.cpp @@ -134,7 +134,7 @@ out: #include "cpu/z80/z80.h" #include "machine/74259.h" #include "machine/bankdev.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "speaker.h" namespace { diff --git a/src/mame/drivers/yunsun16.cpp b/src/mame/drivers/yunsun16.cpp index d3e69ed2f08..1d2f52adad9 100644 --- a/src/mame/drivers/yunsun16.cpp +++ b/src/mame/drivers/yunsun16.cpp @@ -93,7 +93,7 @@ Stephh's notes (based on the games M68000 code and some tests) : #include "cpu/m68000/m68000.h" #include "cpu/z80/z80.h" #include "sound/okim6295.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "speaker.h" diff --git a/src/mame/drivers/yunsung8.cpp b/src/mame/drivers/yunsung8.cpp index 1bb5f1e3bf3..998ba432f31 100644 --- a/src/mame/drivers/yunsung8.cpp +++ b/src/mame/drivers/yunsung8.cpp @@ -32,7 +32,7 @@ Notes: #include "cpu/z80/z80.h" #include "machine/gen_latch.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "screen.h" #include "speaker.h" diff --git a/src/mame/includes/alpha68k.h b/src/mame/includes/alpha68k.h index b5c03a7166e..4d28ccc00fd 100644 --- a/src/mame/includes/alpha68k.h +++ b/src/mame/includes/alpha68k.h @@ -13,11 +13,11 @@ #include "cpu/m68000/m68000.h" #include "cpu/mcs48/mcs48.h" #include "cpu/z80/z80.h" -#include "sound/3812intf.h" #include "sound/ay8910.h" #include "sound/dac.h" #include "sound/ym2203.h" #include "sound/ym2413.h" +#include "sound/ym3812.h" #include "machine/74259.h" #include "machine/gen_latch.h" #include "video/snk68_spr.h" diff --git a/src/mame/includes/bloodbro.h b/src/mame/includes/bloodbro.h index c0528c9c6bf..03eafa6fad5 100644 --- a/src/mame/includes/bloodbro.h +++ b/src/mame/includes/bloodbro.h @@ -6,7 +6,7 @@ #pragma once #include "audio/seibu.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "emupal.h" #include "screen.h" #include "tilemap.h" diff --git a/src/mame/includes/bublbobl.h b/src/mame/includes/bublbobl.h index 20ead09ef19..ad9662cbc43 100644 --- a/src/mame/includes/bublbobl.h +++ b/src/mame/includes/bublbobl.h @@ -9,8 +9,8 @@ #include "cpu/mcs48/mcs48.h" #include "machine/input_merger.h" #include "machine/gen_latch.h" -#include "sound/3526intf.h" #include "sound/ym2203.h" +#include "sound/ym3526.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/includes/toaplan1.h b/src/mame/includes/toaplan1.h index 367c89183f5..a10fa5d6388 100644 --- a/src/mame/includes/toaplan1.h +++ b/src/mame/includes/toaplan1.h @@ -11,7 +11,7 @@ #include "cpu/m68000/m68000.h" #include "cpu/tms32010/tms32010.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "video/toaplan_scu.h" #include "emupal.h" #include "screen.h" diff --git a/src/mame/machine/toaplan1.cpp b/src/mame/machine/toaplan1.cpp index 9a66cae33c0..f4f12766bae 100644 --- a/src/mame/machine/toaplan1.cpp +++ b/src/mame/machine/toaplan1.cpp @@ -8,7 +8,7 @@ #include "emu.h" #include "cpu/z80/z80.h" #include "cpu/tms32010/tms32010.h" -#include "sound/3812intf.h" +#include "sound/ym3812.h" #include "includes/toaplan1.h" -- cgit v1.2.3