#define CLOCK 3546895.0
#define DMACON 0xdff096
+#define INTENA 0xdff09a
#define IOBASE 0xdff000
#define AUDIOBASE 0xdff0a0
#define AUDxLCH 0x0
static struct audio_channel {
int dma_on; /* set if dma is running */
+ int irq_on; /* set if irq is enabled */
uint32_t start; /* sample address to start from */
int length; /* length of samples (not words) */
uint32_t pointer; /* pointer of renderer */
double volume; /* volume of channel in the result sample */
float sample; /* current (last read) sample */
} channel[4] = {
- { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
- { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
- { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
- { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
+ { 0, 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
+ { 0, 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
+ { 0, 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
+ { 0, 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
};
double filter_alpha = 0.0, filter_last = 0.0;
+static void (*sound_irq)(void) = NULL;
+
static int start_dma(int c, struct audio_channel *chan)
{
/* fetch sample start and length and use it for rendering */
return 0;
}
+static void do_irq(int __attribute__((unused)) c, struct audio_channel *chan)
+{
+ if (chan->irq_on) {
+#ifdef DEBUG_SOUND
+ printf("Audio channel %d: IRQ is triggered\n", c);
+#endif
+ print_info("Audio channel %d: IRQ is triggered, please report to author!\n", c);
+ sound_irq();
+ }
+}
+
void emulate_sound_write(uint32_t address, uint16_t value, int samplerate)
{
int c;
- if (address == DMACON) {
+ switch (address) {
+ case DMACON:
/* handle DMA flag and start DMA, if required */
for (c = 0; c < 4; c++) {
/* if nothing should be changed */
channel[c].dma_on = 1;
}
return;
+ case INTENA:
+ /* handle IRQ flag */
+ for (c = 0; c < 4; c++) {
+ /* if nothing should be changed */
+ if (!(value & (0x0080 << c)))
+ continue;
+ if (!(value & 0x8000)) {
+ channel[c].irq_on = 0;
+#ifdef DEBUG_SOUND
+ printf("Audio channel %d: Turning IRQ off\n", c);
+#endif
+ continue;
+ } else {
+ channel[c].irq_on = 1;
+#ifdef DEBUG_SOUND
+ printf("Audio channel %d: Turning IRQ on\n", c);
+#endif
+ continue;
+ }
+ }
+ return;
}
/* return if not register OR get channel from address */
pos = (pos + 1) % buffer_size;
}
- /* render each channel and sum the result */
- for (c = 0; c < 4; c++) {
- chan = &channel[c];
- /* skip if DMA is disabled */
- if (!chan->dma_on)
- continue;
- /* Note: We must render sound, even if volume is 0.0,
- * because the sample pointers must continue, as it would happen in a real Amiga.
- * But this may have no practical use in this game.
- */
- pos = buffer_pos;
- for (s = 0; s < length; s++) {
+ /* Note: We must render sound, even if volume is 0.0,
+ * because the sample pointers must continue, as it would happen in a real Amiga.
+ * But this may have no practical use in this game.
+ */
+ pos = buffer_pos;
+ for (s = 0; s < length; s++) {
+ /* render each channel and sum the result */
+ for (c = 0; c < 4; c++) {
+ chan = &channel[c];
+ /* skip if DMA is disabled */
+ if (!chan->dma_on)
+ continue;
new_offset = chan->offset + chan->step;
if (new_offset < 1.0)
/* no interpolation: use last input sample, because there is no change */
new_offset -= 1.0;
new_sample = (double)(((int8_t *)memory)[chan->pointer++]) / 128.0;
if (chan->pointer == chan->end) {
- if (start_dma(c, chan) < 0)
- break;
+ start_dma(c, chan);
+ do_irq(c, chan);
}
/* linear interpolation: add portion of last sample to portion of new sample */
sample[pos].left += (chan->sample * (1.0 - chan->offset) + new_sample * new_offset) / chan->step * chan->volume;
chan->sample = new_sample;
}
chan->offset = new_offset;
- pos = (pos + 1) % buffer_size;
}
+ pos = (pos + 1) % buffer_size;
}
if (filter) {
}
}
-void sound_init_filter(int samplerate)
+void sound_init(int samplerate, void (*_sound_irq)(void))
{
double dt = 1.0 / (double)samplerate;
double C = 0.0000001;
printf("filter-cutoff: %.0f Hz (alpha = %.4f)\n", cutoff, alpha);
#endif
filter_alpha = alpha;
+
+ sound_irq = _sound_irq;
}
}
/* VBL */
execute_cpu(3, NULL);
- execute_cpu(4, NULL);
/* count down render delay */
if (render_delay) {
render_delay -= 1.0 / (double)IRQ_RATE;
last_time = current_time - 1000 * SOUND_CHUNKS / IRQ_RATE;
}
while (diff > 1000 / IRQ_RATE) {
+ /* trigger and execute IRQ 3 = VBL */
execute_cpu(3, NULL);
- execute_cpu(4, NULL);
had_first_irq = 1;
/* transfer benson without game view
* because we only got benson refreshed during VBL IRQ
static void io_write(uint32_t address, uint16_t value)
{
/* dmacon and sound registers */
- if (address == 0xdff096 || (address >= 0xdff0a0 && address <= 0xdff0df))
+ if (address == 0xdff096 || address == 0xdff09a || (address >= 0xdff0a0 && address <= 0xdff0df))
emulate_sound_write(address, value, SOUND_SAMPLERATE);
if (address == 0xbfd100 || (address >= 0xdff020 && address <= 0xdff024))
emulate_disk_write(address, value);
sound_buffer_readp =(sound_buffer_readp + length) % sound_buffer_size;
}
+void sound_irq(void)
+{
+ /* trigger and execute IRQ 4 = sound */
+ execute_cpu(4, NULL);
+}
+
int main(int argc, char *argv[])
{
int rc;
if (!info_osd)
goto done;
- /* init audio filter */
- sound_init_filter(SOUND_SAMPLERATE);
+ /* init audio */
+ sound_init(SOUND_SAMPLERATE, sound_irq);
/* start cpu */
reset_cpu();