mirror of
https://github.com/holub/mame
synced 2025-06-05 04:16:28 +03:00
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:
parent
1cc65ff1c9
commit
52f0acb25c
@ -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
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#define MAME_BUS_BBC_1MHZBUS_BEEBOPL_H
|
||||
|
||||
#include "1mhzbus.h"
|
||||
#include "sound/3812intf.h"
|
||||
#include "sound/ym3812.h"
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
|
@ -12,7 +12,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "exp.h"
|
||||
#include "sound/3526intf.h"
|
||||
#include "sound/ym3526.h"
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "isa.h"
|
||||
#include "sound/3812intf.h"
|
||||
#include "sound/ym3812.h"
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
||||
//**************************************************************************
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
@ -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
|
@ -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)
|
||||
{
|
||||
}
|
@ -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
|
@ -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)
|
||||
{
|
||||
}
|
@ -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
|
@ -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)
|
||||
{
|
||||
}
|
@ -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
@ -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
309
src/devices/sound/y8950.cpp
Normal 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
81
src/devices/sound/y8950.h
Normal 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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
153
src/devices/sound/ym3526.cpp
Normal file
153
src/devices/sound/ym3526.cpp
Normal 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);
|
||||
}
|
||||
}
|
53
src/devices/sound/ym3526.h
Normal file
53
src/devices/sound/ym3526.h
Normal 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
|
151
src/devices/sound/ym3812.cpp
Normal file
151
src/devices/sound/ym3812.cpp
Normal 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);
|
||||
}
|
||||
}
|
53
src/devices/sound/ym3812.h
Normal file
53
src/devices/sound/ym3812.h
Normal 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
|
@ -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
|
||||
|
@ -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> ®data) :
|
||||
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 ®s() { 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> ®data) :
|
||||
m_regdata(regdata)
|
||||
{
|
||||
}
|
||||
ymadpcm_b_registers() { }
|
||||
|
||||
// register for save states
|
||||
void save(device_t &device);
|
||||
|
||||
// reset to initial state
|
||||
void reset();
|
||||
|
||||
// direct read/write access
|
||||
u8 read(u8 index) { return m_regdata[index]; }
|
||||
void write(u8 index, u8 data) { m_regdata[index] = data; }
|
||||
void write(u32 index, u8 data) { m_regdata[index] = data; }
|
||||
|
||||
// system-wide registers
|
||||
u8 execute() const /* 1 bit */ { return BIT(m_regdata[0x00], 7); }
|
||||
u8 record() const /* 1 bit */ { return BIT(m_regdata[0x00], 6); }
|
||||
u8 external() const /* 1 bit */ { return BIT(m_regdata[0x00], 5); }
|
||||
u8 repeat() const /* 1 bit */ { return BIT(m_regdata[0x00], 4); }
|
||||
u8 speaker() const /* 1 bit */ { return BIT(m_regdata[0x00], 3); }
|
||||
u8 reset() const /* 1 bit */ { return BIT(m_regdata[0x00], 0); }
|
||||
u8 pan_left() const /* 1 bit */ { return BIT(m_regdata[0x01], 7); }
|
||||
u8 pan_right() const /* 1 bit */ { return BIT(m_regdata[0x01], 6); }
|
||||
u8 start_conversion() const /* 1 bit */ { return BIT(m_regdata[0x01], 3); }
|
||||
u8 dac_enable() const /* 1 bit */ { return BIT(m_regdata[0x01], 2); }
|
||||
u8 dram_8bit() const /* 1 bit */ { return BIT(m_regdata[0x01], 1); }
|
||||
u8 rom_ram() const /* 1 bit */ { return BIT(m_regdata[0x01], 0); }
|
||||
u16 start() const /* 16 bits */ { return m_regdata[0x02] | (m_regdata[0x03] << 8); }
|
||||
u16 end() const /* 16 bits */ { return m_regdata[0x04] | (m_regdata[0x05] << 8); }
|
||||
u16 prescale() const /* 11 bits */ { return m_regdata[0x06] | (BIT(m_regdata[0x07], 0, 3) << 8); }
|
||||
u8 cpudata() const /* 8 bits */ { return m_regdata[0x08]; }
|
||||
u16 delta_n() const /* 16 bits */ { return m_regdata[0x09] | (m_regdata[0x0a] << 8); }
|
||||
u8 level() const /* 8 bits */ { return m_regdata[0x0b]; }
|
||||
u16 limit() const /* 16 bits */ { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); }
|
||||
u8 dac() const /* 8 bits */ { return m_regdata[0x0e]; }
|
||||
u8 pcm() const /* 8 bits */ { return m_regdata[0x0f]; }
|
||||
u32 execute() const { return BIT(m_regdata[0x00], 7); }
|
||||
u32 record() const { return BIT(m_regdata[0x00], 6); }
|
||||
u32 external() const { return BIT(m_regdata[0x00], 5); }
|
||||
u32 repeat() const { return BIT(m_regdata[0x00], 4); }
|
||||
u32 speaker() const { return BIT(m_regdata[0x00], 3); }
|
||||
u32 resetflag() const { return BIT(m_regdata[0x00], 0); }
|
||||
u32 pan_left() const { return BIT(m_regdata[0x01], 7); }
|
||||
u32 pan_right() const { return BIT(m_regdata[0x01], 6); }
|
||||
u32 start_conversion() const { return BIT(m_regdata[0x01], 3); }
|
||||
u32 dac_enable() const { return BIT(m_regdata[0x01], 2); }
|
||||
u32 dram_8bit() const { return BIT(m_regdata[0x01], 1); }
|
||||
u32 rom_ram() const { return BIT(m_regdata[0x01], 0); }
|
||||
u32 start() const { return m_regdata[0x02] | (m_regdata[0x03] << 8); }
|
||||
u32 end() const { return m_regdata[0x04] | (m_regdata[0x05] << 8); }
|
||||
u32 prescale() const { return m_regdata[0x06] | (BIT(m_regdata[0x07], 0, 3) << 8); }
|
||||
u32 cpudata() const { return m_regdata[0x08]; }
|
||||
u32 delta_n() const { return m_regdata[0x09] | (m_regdata[0x0a] << 8); }
|
||||
u32 level() const { return m_regdata[0x0b]; }
|
||||
u32 limit() const { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); }
|
||||
u32 dac() const { return m_regdata[0x0e]; }
|
||||
u32 pcm() const { return m_regdata[0x0f]; }
|
||||
|
||||
private:
|
||||
// internal state
|
||||
std::vector<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 ®s() { 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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user