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.
This commit is contained in:
Aaron Giles 2021-04-02 10:58:04 -07:00 committed by GitHub
parent 1cc65ff1c9
commit 52f0acb25c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
157 changed files with 5294 additions and 11056 deletions

View File

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

View File

@ -10,7 +10,7 @@
#define MAME_BUS_BBC_1MHZBUS_BEEBOPL_H
#include "1mhzbus.h"
#include "sound/3812intf.h"
#include "sound/ym3812.h"
//**************************************************************************

View File

@ -12,7 +12,7 @@
#pragma once
#include "exp.h"
#include "sound/3526intf.h"
#include "sound/ym3526.h"

View File

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

View File

@ -6,7 +6,7 @@
#pragma once
#include "isa.h"
#include "sound/3812intf.h"
#include "sound/ym3812.h"
//**************************************************************************
// TYPE DEFINITIONS

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -157,7 +157,7 @@ protected:
virtual void device_add_mconfig(machine_config &config) override;
private:
required_device<vrc7snd_device> m_vrc7snd;
required_device<ds1001_device> m_vrc7snd;
};

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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)
{
}

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<ymf262_device *>(param)->irq_handler(irq); }
static void static_timer_handler(device_t *param, int c, const attotime &period) { downcast<ymf262_device *>(param)->timer_handler(c, period); }
static void static_update_request(device_t *param, int interval) { downcast<ymf262_device *>(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

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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)
{
}

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<ym3526_device *>(param)->irq_handler(irq); }
static void static_timer_handler(device_t *param, int c, const attotime &period) { downcast<ym3526_device *>(param)->timer_handler(c, period); }
static void static_update_request(device_t *param, int interval) { downcast<ym3526_device *>(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

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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)
{
}

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<ym3812_device *>(param)->irq_handler(irq); }
static void static_timer_handler(device_t *param, int c, const attotime &period) { downcast<ym3812_device *>(param)->timer_handler(c, period); }
static void static_update_request(device_t *param, int interval) { downcast<ym3812_device *>(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

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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)
{
}

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<y8950_device *>(param)->read_byte(offset); }
static void static_write_byte(device_t *param, offs_t offset, uint8_t data) { return downcast<y8950_device *>(param)->space().write_byte(offset, data); }
static void static_irq_handler(device_t *param, int irq) { downcast<y8950_device *>(param)->irq_handler(irq); }
static void static_timer_handler(device_t *param, int c, const attotime &period) { downcast<y8950_device *>(param)->timer_handler(c, period); }
static void static_update_request(device_t *param, int interval) { downcast<y8950_device *>(param)->update_request(); }
static unsigned char static_port_handler_r(device_t *param) { return downcast<y8950_device *>(param)->port_handler_r(); }
static void static_port_handler_w(device_t *param, unsigned char data) { downcast<y8950_device *>(param)->port_handler_w(data); }
static unsigned char static_keyboard_handler_r(device_t *param) { return downcast<y8950_device *>(param)->keyboard_handler_r(); }
static void static_keyboard_handler_w(device_t *param, unsigned char data) { downcast<y8950_device *>(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

File diff suppressed because it is too large Load Diff

View File

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

309
src/devices/sound/y8950.cpp Normal file
View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<int>(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);
}

81
src/devices/sound/y8950.h Normal file
View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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

View File

@ -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::vector<read_s
for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++)
{
// clock the system
m_opm.clock(0xff);
m_fm.clock(fm_engine::ALL_CHANNELS);
// update the OPM content; OPM is full 14-bit with no intermediate clipping
s32 lsum = 0, rsum = 0;
m_opm.output(lsum, rsum, 0, 32767, 0xff);
// update the FM content; YM2151 is full 14-bit with no intermediate clipping
s32 sums[fm_engine::OUTPUTS] = { 0 };
m_fm.output(sums, 0, 32767, fm_engine::ALL_CHANNELS);
// convert to 10.3 floating point value for the DAC and back
// OPM is stereo
outputs[0].put_int_clamp(sampindex, fp_to_linear(linear_to_fp(lsum)), 32768);
outputs[1].put_int_clamp(sampindex, fp_to_linear(linear_to_fp(rsum)), 32768);
// YM2151 is stereo
for (int index = 0; index < fm_engine::OUTPUTS; index++)
outputs[index].put_int(sampindex, ymfm_roundtrip_fp(sums[index]), 32768);
}
}
@ -260,15 +215,15 @@ void ym2164_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);
// 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;
}
}

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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

View File

@ -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::vector<read_s
for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++)
{
// clock the system
m_opn.clock(0x07);
m_fm.clock(fm_engine::ALL_CHANNELS);
// update the OPN content; OPN is full 14-bit with no intermediate clipping
s32 lsum = 0, rsum = 0;
m_opn.output(lsum, rsum, 0, 32767, 0x07);
// update the FM content; YM2203 is full 14-bit with no intermediate clipping
s32 sums[fm_engine::OUTPUTS] = { 0 };
m_fm.output(sums, 0, 32767, fm_engine::ALL_CHANNELS);
// convert to 10.3 floating point value for the DAC and back
// OPN is mono, so only the left sum matters
outputs[0].put_int_clamp(sampindex, fp_to_linear(linear_to_fp(lsum)), 32768);
// YM2203 is mono
for (int index = 0; index < fm_engine::OUTPUTS; index++)
outputs[index].put_int(sampindex, ymfm_roundtrip_fp(sums[index]), 32768);
}
}
@ -223,17 +179,17 @@ void ym2203_device::sound_stream_update(sound_stream &stream, std::vector<read_s
void ym2203_device::update_prescale(u8 newval)
{
// inform the OPN engine and refresh our clock rate
m_opn.set_clock_prescale(newval);
m_stream->set_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();
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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;
// constructor
ym2413_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YM2413, u8 const *instruments = nullptr);
// no read access present
// write access
void address_w(u8 data); // A0=0
void data_w(u8 data); // A0=1
void write(offs_t offset, u8 data);
void register_port_w(u8 data);
void data_port_w(u8 data);
protected:
ym2413_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
// 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<read_stream_view> const &inputs, std::vector<write_stream_view> &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<<SIN_BITS),
SIN_MASK = (SIN_LEN-1),
TL_RES_LEN = (256), /* 8 bits addressing (real chip) */
/* TL_TAB_LEN is calculated as:
* 11 - sinus amplitude bits (Y axis)
* 2 - sinus sign bit (Y axis)
* TL_RES_LEN - sinus resolution (X axis)
*/
TL_TAB_LEN = (11*2*TL_RES_LEN),
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 uint8_t eg_inc[15*RATE_STEPS];
static const uint8_t eg_rate_select[16+64+16];
static const uint8_t eg_rate_shift[16+64+16];
static const uint8_t mul_tab[16];
static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS];
static const int8_t lfo_pm_table[8*8];
static const uint8_t table[19][8];
int tl_tab[TL_TAB_LEN];
/* sin waveform table in 'decibel' scale */
/* two waveforms on OPLL type chips */
unsigned int sin_tab[SIN_LEN * 2];
OPLL_CH P_CH[9]; /* OPLL chips have 9 channels*/
uint8_t instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/
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 */
/* LFO */
uint32_t LFO_AM;
int32_t LFO_PM;
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 */
/* instrument settings */
/*
0-user instrument
1-15 - fixed instruments
16 -bass drum settings
17,18 - other percussion instruments
*/
uint8_t inst_tab[19][8];
uint32_t fn_tab[1024]; /* fnumber->increment counter */
uint8_t address; /* address register */
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);
u8 m_address; // address register
sound_stream *m_stream; // sound stream
required_region_ptr<u8> m_internal; // internal memory region
fm_engine m_fm; // core FM engine
};
DECLARE_DEVICE_TYPE(YM2413, ym2413_device)
class vrc7snd_device : public ym2413_device
// ======================> ym2423_device
DECLARE_DEVICE_TYPE(YM2423, ym2423_device);
class ym2423_device : public ym2413_device
{
public:
vrc7snd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// constructor
ym2423_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;
};
// ======================> 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;
};
// ======================> ds1001_device
DECLARE_DEVICE_TYPE(DS1001, ds1001_device);
class ds1001_device : public ym2413_device
{
public:
// constructor
ds1001_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(VRC7, vrc7snd_device)
#endif // MAME_SOUND_YM2413_H

View File

@ -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::vector<read_s
}
// top bit of the IRQ enable flags controls 3-channel vs 6-channel mode
u8 opnmask = BIT(m_irq_enable, 7) ? 0x3f : 0x07;
u8 fmmask = BIT(m_irq_enable, 7) ? 0x3f : 0x07;
// iterate over all target samples
for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++)
{
// clock the OPN
u32 env_counter = m_opn.clock(opnmask);
// clock the FM
u32 env_counter = m_fm.clock(fmmask);
// clock the ADPCM-A engine on every envelope cycle
// (channels 4 and 5 clock every 2 envelope clocks)
@ -317,17 +318,17 @@ void ym2608_device::sound_stream_update(sound_stream &stream, std::vector<read_s
// clock the ADPCM-B engine every cycle
m_adpcm_b.clock(0x01);
// update the OPN content; OPNA is 13-bit with no intermediate clipping
s32 lsum = 0, rsum = 0;
m_opn.output(lsum, rsum, 1, 32767, opnmask);
// update the FM content; YM2608 is 13-bit with no intermediate clipping
s32 sums[fm_engine::OUTPUTS] = { 0 };
m_fm.output(sums, 1, 32767, fmmask);
// mix in the ADPCM
m_adpcm_a.output(lsum, rsum, 0x3f);
m_adpcm_b.output(lsum, rsum, 2, 0x01);
m_adpcm_a.output(sums, 0x3f);
m_adpcm_b.output(sums, 2, 0x01);
// YM2608 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[index], 32768);
}
}
@ -339,32 +340,32 @@ void ym2608_device::sound_stream_update(sound_stream &stream, std::vector<read_s
void ym2608_device::update_prescale(u8 newval)
{
// inform the OPN engine and refresh our clock rate
m_opn.set_clock_prescale(newval);
m_stream->set_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;
}

View File

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

View File

@ -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::vector<read_s
// iterate over all target samples
for (int sampindex = 0; sampindex < outputs[0].samples(); sampindex++)
{
// clock the OPN
u32 env_counter = m_opn.clock(m_opn_mask);
// clock the FM
u32 env_counter = m_fm.clock(m_fm_mask);
// clock the ADPCM-A engine on every envelope cycle
if (BIT(env_counter, 0, 2) == 0)
@ -274,20 +275,20 @@ void ym2610_device::sound_stream_update(sound_stream &stream, std::vector<read_s
// clock the ADPCM-B engine every cycle
m_adpcm_b.clock(0x01);
if ((m_adpcm_b.status(0) & ymadpcm_b_channel::STATUS_EOS) != 0)
if ((m_adpcm_b.status() & ymadpcm_b_channel::STATUS_EOS) != 0)
m_eos_status |= 0x80;
// update the OPN content; OPNB is 13-bit with no intermediate clipping
s32 lsum = 0, rsum = 0;
m_opn.output(lsum, rsum, 1, 32767, m_opn_mask);
// update the FM content; YM2610 is 13-bit with no intermediate clipping
s32 sums[fm_engine::OUTPUTS] = { 0 };
m_fm.output(sums, 1, 32767, m_fm_mask);
// mix in the ADPCM
m_adpcm_a.output(lsum, rsum, 0x3f);
m_adpcm_b.output(lsum, rsum, 2, 0x01);
m_adpcm_a.output(sums, 0x3f);
m_adpcm_b.output(sums, 2, 0x01);
// YM2608 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[index], 32768);
}
}

View File

@ -18,11 +18,14 @@ DECLARE_DEVICE_TYPE(YM2610, ym2610_device);
class ym2610_device : public ay8910_device, public device_memory_interface
{
public:
// YM2610 is OPNA
using fm_engine = ymopna_engine;
// constructor
ym2610_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YM2610, u8 opn_mask = 0x36);
ym2610_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, device_type type = YM2610, u8 fm_mask = 0x36);
// 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);
@ -50,13 +53,13 @@ private:
address_space_config const m_adpcm_b_config; // address space 1 config (ADPCM-B)
optional_memory_region m_adpcm_a_region; // ADPCM-A memory region
optional_memory_region m_adpcm_b_region; // ADPCM-B 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
attotime m_busy_duration; // precomputed busy signal duration
u16 m_address; // address register
u8 const m_opn_mask; // OPN channel mask
u8 const m_fm_mask; // FM channel mask
u8 m_eos_status; // end-of-sample signals
u8 m_flag_mask; // flag mask control
};

View File

@ -8,7 +8,7 @@
// the YM2612/YM3438 just timeslice the output among all channels
// instead of summing them; turn this on to simulate (may create
// audible issues)
#define MULTIPLEX_YM2612_YM3438_OUTPUT (0)
#define MULTIPLEX_OUTPUT (0)
DEFINE_DEVICE_TYPE(YM2612, ym2612_device, "ym2612", "YM2612 OPN2")
@ -27,9 +27,9 @@ DEFINE_DEVICE_TYPE(YMF276, ymf276_device, "ymf276", "YMF276 OPN2L")
ym2612_device::ym2612_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_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),
m_dac_data(0),
m_dac_enable(0),
@ -47,8 +47,8 @@ u8 ym2612_device::read(offs_t offset)
u8 result = 0;
switch (offset & 3)
{
case 0: // status port, YM2203 compatible
result = m_opn.status();
case 0: // status port, YM2203 compatible
result = m_fm.status();
break;
case 1: // data port (unused)
@ -100,12 +100,12 @@ void ym2612_device::write(offs_t offset, u8 value)
}
else
{
// write to OPN
m_opn.write(m_address, value);
// write to FM
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
@ -118,12 +118,12 @@ void ym2612_device::write(offs_t offset, u8 value)
if (!BIT(m_address, 8))
break;
// 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;
}
}
@ -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<read_s
void ym2612_device::sound_stream_update_common(write_stream_view &outl, write_stream_view &outr, bool discontinuity)
{
u32 const sample_divider = (discontinuity ? 260 : 256) * (MULTIPLEX_YM2612_YM3438_OUTPUT ? 1 : 6);
u32 const sample_divider = (discontinuity ? 260 : 256) * (MULTIPLEX_OUTPUT ? 1 : fm_engine::CHANNELS);
// iterate over all target samples
s32 lsum = 0, rsum = 0;
s32 sums[2] = { 0 };
for (int sampindex = 0; sampindex < outl.samples(); )
{
// clock the OPN when we hit channel 0
// clock the FM when we hit channel 0
if (m_channel == 0)
m_opn.clock(0x3f);
m_fm.clock(fm_engine::ALL_CHANNELS);
// update the current OPN channel; OPN2 is 9-bit with intermediate clipping
s32 lchan = 0, rchan = 0;
// update the current FM channel; YM2612 is 9-bit with intermediate clipping
s32 outputs[2] = { 0 };
if (m_channel != 5 || !m_dac_enable)
m_opn.output(lchan, rchan, 5, 256, 1 << m_channel);
m_fm.output(outputs, 5, 256, 1 << m_channel);
else
lchan = rchan = s16(m_dac_data << 7) >> 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<read_stream_view> const &inputs, std::vector<write_stream_view> &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);
}
}

View File

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

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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);
}
}

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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);
}
}

View File

@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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

View File

@ -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<ymadpcm_a_channel>(m_regs.channel_registers(chnum), reader, addrshift);
for (int chnum = 0; chnum < CHANNELS; chnum++)
m_channel[chnum] = std::make_unique<ymadpcm_a_channel>(*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<ymadpcm_b_channel>(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<ymadpcm_b_channel>(*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

View File

@ -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:
// constructor
ymadpcm_a_registers(std::vector<u8> &regdata) :
m_chbase(0),
m_regdata(regdata)
{
}
// constants
static constexpr u32 OUTPUTS = 2;
static constexpr u32 CHANNELS = 6;
static constexpr u32 REGISTERS = 0x30;
static constexpr u32 ALL_CHANNELS = (1 << CHANNELS) - 1;
u8 chbase() const { return m_chbase; }
// constructor
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;
}
// 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<u8> &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 &regs() { return m_regs; }
private:
// internal state
std::unique_ptr<ymadpcm_a_channel> m_channel[CHANNELS]; // array of channels
std::vector<u8> 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<u8> &regdata) :
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<u8> &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 &regs() { return m_regs; }
private:
// internal state
std::unique_ptr<ymadpcm_b_channel> m_channel[CHANNELS]; // array of channels
std::vector<u8> m_regdata; // raw register data
ymadpcm_b_registers m_regs; // register accessor
};

View File

@ -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<<YM_DELTAT_SHIFT) )
{
step = DELTAT->now_step >> YM_DELTAT_SHIFT;
DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
do{
if ( DELTAT->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<<YM_DELTAT_SHIFT)-DELTAT->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<<YM_DELTAT_SHIFT) )
{
step = DELTAT->now_step >> YM_DELTAT_SHIFT;
DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
do{
if( DELTAT->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<<YM_DELTAT_SHIFT)-DELTAT->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;
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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);
#include "ymfm.h"
void *ymf262_init(device_t *device, int clock, int rate);
void *ymf278b_init(device_t *device, int clock, int rate);
// ======================> ymf262_device
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<write_stream_view> &buffers);
DECLARE_DEVICE_TYPE(YMF262, ymf262_device);
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);
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<read_stream_view> const &inputs, std::vector<write_stream_view> &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

View File

@ -50,7 +50,6 @@
#include "emu.h"
#include "ymf278b.h"
#include "ymf262.h"
#include <algorithm>
@ -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<read_
YMF278BSlot *slot;
int16_t sample = 0;
int32_t *mixp;
int32_t vl, vr;
ymf262_update_one(m_ymf262, outputs);
stream_buffer::sample_t fvl = stream_buffer::sample_t(m_mix_level[m_fm_l]) * (1.0 / 65536.0);
stream_buffer::sample_t fvr = stream_buffer::sample_t(m_mix_level[m_fm_r]) * (1.0 / 65536.0);
for (i = 0; i < outputs[0].samples(); i++)
{
// DO2 mixing
outputs[0].put(i, outputs[0].get(i) * fvl);
outputs[1].put(i, outputs[1].get(i) * fvr);
}
std::fill(m_mix_buffer.begin(), m_mix_buffer.end(), 0);
@ -314,29 +314,46 @@ void ymf278b_device::sound_stream_update(sound_stream &stream, std::vector<read_
}
mixp = &m_mix_buffer[0];
vl = m_mix_level[m_pcm_l];
vr = m_mix_level[m_pcm_r];
stream_buffer::sample_t wtl = stream_buffer::sample_t(m_mix_level[m_pcm_l]) / (65536.0f * 32768.0f);
stream_buffer::sample_t wtr = stream_buffer::sample_t(m_mix_level[m_pcm_r]) / (65536.0f * 32768.0f);
stream_buffer::sample_t fml = stream_buffer::sample_t(m_mix_level[m_fm_l]) / (65536.0f * 32768.0f);
stream_buffer::sample_t fmr = stream_buffer::sample_t(m_mix_level[m_fm_r]) / (65536.0f * 32768.0f);
for (i = 0; i < outputs[0].samples(); i++)
{
outputs[0].add_int(i, (*mixp++ * vl) >> 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_timer_busy->reset();
m_timer_ld->reset();
m_irq_line = 0;
m_current_irq = 0;
if (!m_irq_handler.isnull())
m_irq_handler(0);
ymf262_reset_chip(m_ymf262);
}
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)
{
}

View File

@ -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<ymf278b_device *>(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<int32_t> 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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More