summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/audio/amiga.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/audio/amiga.c')
-rw-r--r--src/mame/audio/amiga.c280
1 files changed, 280 insertions, 0 deletions
diff --git a/src/mame/audio/amiga.c b/src/mame/audio/amiga.c
new file mode 100644
index 00000000000..1fd3e647061
--- /dev/null
+++ b/src/mame/audio/amiga.c
@@ -0,0 +1,280 @@
+/***************************************************************************
+
+ Amiga Computer / Arcadia Game System
+
+ Driver by:
+
+ Ernesto Corvi & Mariusz Wojcieszek
+
+***************************************************************************/
+
+#include "driver.h"
+#include "streams.h"
+#include "includes/amiga.h"
+#include "cpu/m68000/m68000.h"
+
+
+/*************************************
+ *
+ * Debugging
+ *
+ *************************************/
+
+#define LOG(x)
+//#define LOG(x) logerror x
+
+
+
+/*************************************
+ *
+ * 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;
+};
+
+
+
+/*************************************
+ *
+ * Globals
+ *
+ *************************************/
+
+static amiga_audio *audio_state;
+
+
+
+/*************************************
+ *
+ * DMA reload/IRQ signalling
+ *
+ *************************************/
+
+static TIMER_CALLBACK( signal_irq )
+{
+ amiga_custom_w(REG_INTREQ, 0x8000 | (0x80 << param), 0);
+}
+
+
+static void dma_reload(audio_channel *chan)
+{
+ chan->curlocation = CUSTOM_REG_LONG(REG_AUD0LCH + chan->index * 8);
+ chan->curlength = CUSTOM_REG(REG_AUD0LEN + chan->index * 8);
+ timer_adjust(chan->irq_timer, ATTOTIME_IN_HZ(15750), chan->index, attotime_zero);
+ LOG(("dma_reload(%d): offs=%05X len=%04X\n", chan->index, chan->curlocation, chan->curlength));
+}
+
+
+
+/*************************************
+ *
+ * Manual mode data writer
+ *
+ *************************************/
+
+void amiga_audio_data_w(int which, UINT16 data)
+{
+ audio_state->channel[which].manualmode = TRUE;
+}
+
+
+
+/*************************************
+ *
+ * Stream updater
+ *
+ *************************************/
+
+void amiga_audio_update(void)
+{
+ stream_update(audio_state->stream);
+}
+
+
+
+static void amiga_stream_update(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int length)
+{
+ amiga_audio *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) * length);
+ return;
+ }
+
+ length *= 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(chan);
+ chan->dmaenabled = (CUSTOM_REG(REG_DMACON) >> channum) & 1;
+ }
+
+ /* loop until done */
+ while (length > 0)
+ {
+ int nextper, nextvol;
+ int ticks = length;
+
+ /* 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) = amiga_chip_ram_r(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->dmaenabled && chan->manualmode && (chan->curlocation & 1))
+ {
+ signal_irq(Machine, channum);
+ chan->manualmode = FALSE;
+ }
+ }
+ }
+
+ /* bump ourselves forward by the number of ticks */
+ sampoffs += ticks;
+ length -= ticks;
+ }
+}
+
+
+
+/*************************************
+ *
+ * Sound system startup
+ *
+ *************************************/
+
+void *amiga_sh_start(int clock, const struct CustomSound_interface *config)
+{
+ int i;
+
+ /* allocate a new audio state */
+ audio_state = auto_malloc(sizeof(*audio_state));
+ memset(audio_state, 0, sizeof(*audio_state));
+ for (i = 0; i < 4; i++)
+ {
+ audio_state->channel[i].index = i;
+ audio_state->channel[i].irq_timer = timer_alloc(signal_irq);
+ }
+
+ /* create the stream */
+ audio_state->stream = stream_create(0, 4, clock / CLOCK_DIVIDER, audio_state, amiga_stream_update);
+ return audio_state;
+}