diff options
Diffstat (limited to 'src/emu/sound')
-rw-r--r-- | src/emu/sound/amiga.c | 240 | ||||
-rw-r--r-- | src/emu/sound/amiga.h | 63 | ||||
-rw-r--r-- | src/emu/sound/sound.mak | 11 |
3 files changed, 314 insertions, 0 deletions
diff --git a/src/emu/sound/amiga.c b/src/emu/sound/amiga.c new file mode 100644 index 00000000000..c4c9f045352 --- /dev/null +++ b/src/emu/sound/amiga.c @@ -0,0 +1,240 @@ +/*************************************************************************** + + Amiga audio hardware + +***************************************************************************/ + +#include "amiga.h" +#include "includes/amiga.h" + + +//************************************************************************** +// CONSTANTS / MACROS +//************************************************************************** + +#define VERBOSE 0 +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type AMIGA = &device_creator<amiga_sound_device>; + + +//************************************************************************* +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// amiga_sound_device - constructor +//------------------------------------------------- + +amiga_sound_device::amiga_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, AMIGA, "Amiga Paula", tag, owner, clock, "amiga_paula", __FILE__), + device_sound_interface(mconfig, *this), + m_stream(NULL) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void amiga_sound_device::device_start() +{ + // initialize channels + for (int i = 0; i < 4; i++) + { + m_channel[i].index = i; + m_channel[i].irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(amiga_sound_device::signal_irq), this)); + } + + // create the stream + m_stream = machine().sound().stream_alloc(*this, 0, 4, clock() / CLOCK_DIVIDER); +} + + +//************************************************************************* +// IMPLEMENTATION +//************************************************************************** + +//------------------------------------------------- +// signal_irq - irq signaling +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER( amiga_sound_device::signal_irq ) +{ + amiga_state *state = machine().driver_data<amiga_state>(); + + state->custom_chip_w(REG_INTREQ, INTENA_SETCLR | (0x80 << param)); +} + +//------------------------------------------------- +// dma_reload +//------------------------------------------------- + +void amiga_sound_device::dma_reload(audio_channel *chan) +{ + amiga_state *state = machine().driver_data<amiga_state>(); + + chan->curlocation = CUSTOM_REG_LONG(REG_AUD0LCH + chan->index * 8); + chan->curlength = CUSTOM_REG(REG_AUD0LEN + chan->index * 8); + chan->irq_timer->adjust(attotime::from_hz(15750), chan->index); + + LOG(("dma_reload(%d): offs=%05X len=%04X\n", chan->index, chan->curlocation, chan->curlength)); +} + +//------------------------------------------------- +// data_w - manual mode data writer +//------------------------------------------------- + +void amiga_sound_device::data_w(int which, UINT16 data) +{ + m_channel[which].manualmode = true; +} + +//------------------------------------------------- +// update - stream updater +//------------------------------------------------- + +void amiga_sound_device::update() +{ + m_stream->update(); +} + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void amiga_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + amiga_state *state = machine().driver_data<amiga_state>(); + int channum, sampoffs = 0; + + // if all DMA off, disable all channels + if (!(CUSTOM_REG(REG_DMACON) & 0x0200)) + { + m_channel[0].dma_enabled = + m_channel[1].dma_enabled = + m_channel[2].dma_enabled = + m_channel[3].dma_enabled = false; + + // clear the sample data to 0 + for (channum = 0; channum < 4; channum++) + memset(outputs[channum], 0, sizeof(stream_sample_t) * samples); + return; + } + + samples *= CLOCK_DIVIDER; + + // update the DMA states on each channel and reload if fresh + for (channum = 0; channum < 4; channum++) + { + audio_channel *chan = &m_channel[channum]; + if (!chan->dma_enabled && ((CUSTOM_REG(REG_DMACON) >> channum) & 1)) + dma_reload(chan); + chan->dma_enabled = (CUSTOM_REG(REG_DMACON) >> channum) & 1; + } + + // loop until done + while (samples > 0) + { + int nextper, nextvol; + int ticks = samples; + + // determine the number of ticks we can do in this chunk + if (ticks > m_channel[0].curticks) + ticks = m_channel[0].curticks; + if (ticks > m_channel[1].curticks) + ticks = m_channel[1].curticks; + if (ticks > m_channel[2].curticks) + ticks = m_channel[2].curticks; + if (ticks > m_channel[3].curticks) + ticks = m_channel[3].curticks; + + // loop over channels + nextper = nextvol = -1; + for (channum = 0; channum < 4; channum++) + { + int volume = (nextvol == -1) ? CUSTOM_REG(REG_AUD0VOL + channum * 8) : nextvol; + int period = (nextper == -1) ? CUSTOM_REG(REG_AUD0PER + channum * 8) : nextper; + audio_channel *chan = &m_channel[channum]; + stream_sample_t sample; + int i; + + // normalize the volume value + volume = (volume & 0x40) ? 64 : (volume & 0x3f); + volume *= 4; + + // are we modulating the period of the next channel? + if ((CUSTOM_REG(REG_ADKCON) >> channum) & 0x10) + { + nextper = CUSTOM_REG(REG_AUD0DAT + channum * 8); + nextvol = -1; + sample = 0; + } + + // are we modulating the volume of the next channel? + else if ((CUSTOM_REG(REG_ADKCON) >> channum) & 0x01) + { + nextper = -1; + nextvol = CUSTOM_REG(REG_AUD0DAT + channum * 8); + sample = 0; + } + + // otherwise, we are generating data + else + { + nextper = nextvol = -1; + sample = chan->latched * volume; + } + + // fill the buffer with the sample + for (i = 0; i < ticks; i += CLOCK_DIVIDER) + outputs[channum][(sampoffs + i) / CLOCK_DIVIDER] = sample; + + // account for the ticks; if we hit 0, advance + chan->curticks -= ticks; + if (chan->curticks == 0) + { + // reset the clock and ensure we're above the minimum ticks + chan->curticks = period; + if (chan->curticks < 124) + chan->curticks = 124; + + // move forward one byte; if we move to an even byte, fetch new + if (chan->dma_enabled || chan->manualmode) + chan->curlocation++; + if (chan->dma_enabled && !(chan->curlocation & 1)) + { + CUSTOM_REG(REG_AUD0DAT + channum * 8) = (*state->m_chip_ram_r)(state, chan->curlocation); + if (chan->curlength != 0) + chan->curlength--; + + // if we run out of data, reload the dma + if (chan->curlength == 0) + dma_reload(chan); + } + + // latch the next byte of the sample + if (!(chan->curlocation & 1)) + chan->latched = CUSTOM_REG(REG_AUD0DAT + channum * 8) >> 8; + else + chan->latched = CUSTOM_REG(REG_AUD0DAT + channum * 8) >> 0; + + // if we're in manual mode, signal an interrupt once we latch the low byte + if (!chan->dma_enabled && chan->manualmode && (chan->curlocation & 1)) + { + signal_irq(NULL, channum); + chan->manualmode = false; + } + } + } + + // bump ourselves forward by the number of ticks + sampoffs += ticks; + samples -= ticks; + } +} diff --git a/src/emu/sound/amiga.h b/src/emu/sound/amiga.h new file mode 100644 index 00000000000..56e092b248f --- /dev/null +++ b/src/emu/sound/amiga.h @@ -0,0 +1,63 @@ +/*************************************************************************** + + Amiga audio hardware + +***************************************************************************/ + +#pragma once + +#ifndef __SOUND_AMIGA_H__ +#define __SOUND_AMIGA_H__ + +#include "emu.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> amiga_sound_device + +class amiga_sound_device : public device_t, public device_sound_interface +{ +public: + amiga_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~amiga_sound_device() {} + + void update(); + void data_w(int which, UINT16 data); + +protected: + // device-level overrides + virtual void device_start(); + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); + +private: + static const int CLOCK_DIVIDER = 16; + + struct audio_channel + { + emu_timer *irq_timer; + UINT32 curlocation; + UINT16 curlength; + UINT16 curticks; + UINT8 index; + bool dma_enabled; + bool manualmode; + INT8 latched; + }; + + void dma_reload(audio_channel *chan); + + // internal state + audio_channel m_channel[4]; + sound_stream *m_stream; + + TIMER_CALLBACK_MEMBER( signal_irq ); +}; + +extern const device_type AMIGA; + +#endif // __SOUND_AMIGA_H__ diff --git a/src/emu/sound/sound.mak b/src/emu/sound/sound.mak index 070534c8e09..6a6a0cf0d0d 100644 --- a/src/emu/sound/sound.mak +++ b/src/emu/sound/sound.mak @@ -101,6 +101,17 @@ endif #------------------------------------------------- +# Amiga audio hardware +#@src/emu/sound/amiga.h,SOUNDS += AMIGA +#------------------------------------------------- + +ifneq ($(filter AMIGA,$(SOUNDS)),) +SOUNDOBJS += $(SOUNDOBJ)/amiga.o +endif + + + +#------------------------------------------------- # Bally Astrocade sound system #@src/emu/sound/astrocde.h,SOUNDS += ASTROCADE #------------------------------------------------- |