diff options
Diffstat (limited to 'trunk/src/mame/audio/amiga.c')
-rw-r--r-- | trunk/src/mame/audio/amiga.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/trunk/src/mame/audio/amiga.c b/trunk/src/mame/audio/amiga.c new file mode 100644 index 00000000000..4fb762e563d --- /dev/null +++ b/trunk/src/mame/audio/amiga.c @@ -0,0 +1,298 @@ +/*************************************************************************** + + Amiga Computer / Arcadia Game System + + Driver by: + + Ernesto Corvi & Mariusz Wojcieszek + +***************************************************************************/ + +#include "emu.h" +#include "includes/amiga.h" +#include "cpu/m68000/m68000.h" + + +/************************************* + * + * Debugging + * + *************************************/ + +#define VERBOSE 0 +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + + + +/************************************* + * + * Constants + * + *************************************/ + +#define CLOCK_DIVIDER 16 + + + +/************************************* + * + * Type definitions + * + *************************************/ + +typedef struct _audio_channel audio_channel; +struct _audio_channel +{ + emu_timer * irq_timer; + UINT32 curlocation; + UINT16 curlength; + UINT16 curticks; + UINT8 index; + UINT8 dmaenabled; + UINT8 manualmode; + INT8 latched; +}; + + +typedef struct _amiga_audio amiga_audio; +struct _amiga_audio +{ + audio_channel channel[4]; + sound_stream * stream; +}; + + +INLINE amiga_audio *get_safe_token( device_t *device ) +{ + assert(device != NULL); + assert(device->type() == AMIGA); + + return (amiga_audio *)downcast<legacy_device_base *>(device)->token(); +} + +/************************************* + * + * DMA reload/IRQ signalling + * + *************************************/ + +static TIMER_CALLBACK( signal_irq ) +{ + amiga_custom_w(machine.device("maincpu")->memory().space(AS_PROGRAM), REG_INTREQ, 0x8000 | (0x80 << param), 0xffff); +} + + +static void dma_reload(amiga_state *state, audio_channel *chan) +{ + 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)); +} + + + +/************************************* + * + * Manual mode data writer + * + *************************************/ + +void amiga_audio_data_w(device_t *device, int which, UINT16 data) +{ + amiga_audio *audio_state = get_safe_token(device); + + audio_state->channel[which].manualmode = TRUE; +} + + + +/************************************* + * + * Stream updater + * + *************************************/ + +void amiga_audio_update(device_t *device) +{ + amiga_audio *audio_state = get_safe_token(device); + + audio_state->stream->update(); +} + + + +static STREAM_UPDATE( amiga_stream_update ) +{ + amiga_state *state = device->machine().driver_data<amiga_state>(); + amiga_audio *audio = (amiga_audio *)param; + int channum, sampoffs = 0; + + /* if all DMA off, disable all channels */ + if (!(CUSTOM_REG(REG_DMACON) & 0x0200)) + { + audio->channel[0].dmaenabled = + audio->channel[1].dmaenabled = + audio->channel[2].dmaenabled = + audio->channel[3].dmaenabled = 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 = &audio->channel[channum]; + if (!chan->dmaenabled && ((CUSTOM_REG(REG_DMACON) >> channum) & 1)) + dma_reload(state, chan); + chan->dmaenabled = (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 > audio->channel[0].curticks) + ticks = audio->channel[0].curticks; + if (ticks > audio->channel[1].curticks) + ticks = audio->channel[1].curticks; + if (ticks > audio->channel[2].curticks) + ticks = audio->channel[2].curticks; + if (ticks > audio->channel[3].curticks) + ticks = audio->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 = &audio->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->dmaenabled || chan->manualmode) + chan->curlocation++; + if (chan->dmaenabled && !(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(state, 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->dmaenabled && chan->manualmode && (chan->curlocation & 1)) + { + signal_irq(device->machine(), NULL, channum); + chan->manualmode = FALSE; + } + } + } + + /* bump ourselves forward by the number of ticks */ + sampoffs += ticks; + samples -= ticks; + } +} + + + +/************************************* + * + * Sound system startup + * + *************************************/ + +static DEVICE_START( amiga_sound ) +{ + amiga_audio *audio_state = get_safe_token(device); + int i; + + for (i = 0; i < 4; i++) + { + audio_state->channel[i].index = i; + audio_state->channel[i].irq_timer = device->machine().scheduler().timer_alloc(FUNC(signal_irq)); + } + + /* create the stream */ + audio_state->stream = device->machine().sound().stream_alloc(*device, 0, 4, device->clock() / CLOCK_DIVIDER, audio_state, amiga_stream_update); +} + + +DEVICE_GET_INFO( amiga_sound ) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(amiga_audio); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(amiga_sound); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "Amiga Paula"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(AMIGA, amiga_sound); |