Fixing audio interrupt handling (unknown if it works)
authorAndreas Eversberg <jolly@eversberg.eu>
Wed, 4 Apr 2018 16:52:42 +0000 (18:52 +0200)
committerAndreas Eversberg <jolly@eversberg.eu>
Thu, 5 Apr 2018 16:01:59 +0000 (18:01 +0200)
src/libsound/sound.c
src/libsound/sound.h
src/mercenary/main.c

index 081a16e..4073d8c 100644 (file)
@@ -29,6 +29,7 @@
 
 #define CLOCK          3546895.0
 #define DMACON         0xdff096
+#define INTENA         0xdff09a
 #define IOBASE         0xdff000
 #define AUDIOBASE      0xdff0a0
 #define AUDxLCH                0x0
@@ -39,6 +40,7 @@
 
 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 */
@@ -48,14 +50,16 @@ static struct audio_channel {
        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 */
@@ -81,11 +85,23 @@ static int start_dma(int c, struct audio_channel *chan)
        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 */
@@ -112,6 +128,27 @@ void emulate_sound_write(uint32_t address, uint16_t value, int samplerate)
                        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 */
@@ -175,18 +212,18 @@ void render_sound(uint8_t *memory, stereo_t *sample, int buffer_size, int buffer
                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 */
@@ -196,16 +233,16 @@ void render_sound(uint8_t *memory, stereo_t *sample, int buffer_size, int buffer
                                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) {
@@ -226,7 +263,7 @@ void render_sound(uint8_t *memory, stereo_t *sample, int buffer_size, int buffer
        }
 }
 
-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;
@@ -240,5 +277,7 @@ void sound_init_filter(int samplerate)
        printf("filter-cutoff: %.0f Hz (alpha = %.4f)\n", cutoff, alpha);
 #endif
        filter_alpha = alpha;
+
+       sound_irq = _sound_irq;
 }
 
index b7f8278..0c9289e 100644 (file)
@@ -5,5 +5,5 @@ typedef struct stereo {
 
 void emulate_sound_write(uint32_t address, uint16_t value, int samplerate);
 void render_sound(uint8_t *memory, stereo_t *sample, int buffer_size, int buffer_pos, int length, int filter);
-void sound_init_filter(int samplerate);
+void sound_init(int samplerate, void (*sound_irq)(void));
 
index 5272f8a..27935dd 100644 (file)
@@ -277,7 +277,6 @@ static void skip_intro(void)
                }
                /* VBL */
                execute_cpu(3, NULL);
-               execute_cpu(4, NULL);
                /* count down render delay */
                if (render_delay) {
                        render_delay -= 1.0 / (double)IRQ_RATE;
@@ -439,8 +438,8 @@ static void main_loop(void)
                                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
@@ -507,7 +506,7 @@ static uint16_t io_read(uint32_t address)
 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);
@@ -860,6 +859,12 @@ void audio_sdl(float *data, int length)
        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;
@@ -1000,8 +1005,8 @@ int main(int argc, char *argv[])
        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();