diff --git a/src/sound/snd_gus.c b/src/sound/snd_gus.c index 235bd9ca9..aeff42101 100644 --- a/src/sound/snd_gus.c +++ b/src/sound/snd_gus.c @@ -13,6 +13,7 @@ #include "../timer.h" #include "../device.h" #include "sound.h" +#include "midi.h" #include "snd_gus.h" enum @@ -100,9 +101,9 @@ typedef struct gus_t uint8_t ad_status, ad_data; uint8_t ad_timer_ctrl; - uint8_t midi_ctrl, midi_status; - uint8_t midi_data; - int midi_loopback; + uint8_t uart_queue[64]; + int uart_pos, uart_used; + int uart_in, uart_out, sysex; uint8_t gp1, gp2; uint16_t gp1_addr, gp2_addr; @@ -111,7 +112,6 @@ typedef struct gus_t } gus_t; static int gus_irqs[8] = {-1, 2, 5, 3, 7, 11, 12, 15}; -static int gus_irqs_midi[8] = {-1, 2, 5, 3, 7, 11, 12, 15}; static int gus_dmas[8] = {-1, 1, 3, 5, 6, 7, -1, -1}; int gusfreqs[]= @@ -151,31 +151,6 @@ void pollgusirqs(gus_t *gus) if (!gus->irqstatus && gus->irq != -1) picintc(1 << gus->irq); } -void gus_midi_update_int_status(gus_t *gus) -{ - gus->midi_status &= ~MIDI_INT_MASTER; - if ((gus->midi_ctrl & MIDI_CTRL_TRANSMIT_MASK) == MIDI_CTRL_TRANSMIT && (gus->midi_status & MIDI_INT_TRANSMIT)) - { - gus->midi_status |= MIDI_INT_MASTER; - gus->irqstatus |= GUS_INT_MIDI_TRANSMIT; - } - else - gus->irqstatus &= ~GUS_INT_MIDI_TRANSMIT; - - if ((gus->midi_ctrl & MIDI_CTRL_RECEIVE) && (gus->midi_status & MIDI_INT_RECEIVE)) - { - gus->midi_status |= MIDI_INT_MASTER; - gus->irqstatus |= GUS_INT_MIDI_RECEIVE; - } - else - gus->irqstatus &= ~GUS_INT_MIDI_RECEIVE; - - if ((gus->midi_status & MIDI_INT_MASTER) && (gus->irq_midi != -1)) - { - picint(1 << gus->irq_midi); - } -} - void writegus(uint16_t addr, uint8_t val, void *p) { gus_t *gus = (gus_t *)p; @@ -188,34 +163,34 @@ void writegus(uint16_t addr, uint8_t val, void *p) else port = addr & 0xf0f; - if (gus->latch_enable && port != 0x20b) - gus->latch_enable = 0; - switch (port) { case 0x300: /*MIDI control*/ - old = gus->midi_ctrl; - gus->midi_ctrl = val; - - if ((val & 3) == 3) - gus->midi_status = 0; - else if ((old & 3) == 3) - { - gus->midi_status |= MIDI_INT_TRANSMIT; - } - gus_midi_update_int_status(gus); + gus->uart_out = 1; + if (val == 3 || !val) { + gus->uart_in = 0; + gus->irqstatus &= ~0x03; + gus->uart_used = 0; + return; + } + + if (val & 0x20) { + gus->irqstatus |= 0x01; + } else { + gus->irqstatus &= ~0x01; + } + + if (val & 0x80) + gus->uart_in = 1; + else + gus->uart_in = 0; break; - case 0x301: /*MIDI data*/ - if (gus->midi_loopback) - { - gus->midi_status |= MIDI_INT_RECEIVE; - gus->midi_data = val; + if (gus->uart_out) { + midi_raw_out_byte(val); + picint(1 << gus->irq_midi); } - else - gus->midi_status |= MIDI_INT_TRANSMIT; - break; - + break; case 0x302: /*Voice select*/ gus->voice=val&31; break; @@ -246,11 +221,11 @@ void writegus(uint16_t addr, uint8_t val, void *p) gus->end[gus->voice]=(gus->end[gus->voice]&0x1FFFFF00)|val; break; - case 0x6: /*Ramp frequency*/ + case 6: /*Ramp frequency*/ gus->rfreq[gus->voice] = (int)( (double)((val & 63)*512)/(double)(1 << (3*(val >> 6)))); break; - case 0x9: /*Current volume*/ + case 9: /*Current volume*/ gus->curvol[gus->voice] = gus->rcur[gus->voice] = (gus->rcur[gus->voice] & ~(0xff << 6)) | (val << 6); break; @@ -278,10 +253,6 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xF807F00)|((val<<7)<<8); switch (gus->global) { case 0: /*Voice control*/ - if (!(val&1) && gus->ctrl[gus->voice]&1) - { - } - gus->ctrl[gus->voice] = val & 0x7f; old = gus->waveirqs[gus->voice]; @@ -309,16 +280,16 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xF807F00)|((val<<7)<<8); gus->end[gus->voice]=(gus->end[gus->voice]&0x1FFF00FF)|(val<<8); break; - case 0x6: /*Ramp frequency*/ + case 6: /*Ramp frequency*/ gus->rfreq[gus->voice] = (int)( (double)((val & 63) * (1 << 10))/(double)(1 << (3 * (val >> 6)))); break; - case 0x7: /*Ramp start*/ + case 7: /*Ramp start*/ gus->rstart[gus->voice] = val << 14; break; - case 0x8: /*Ramp end*/ + case 8: /*Ramp end*/ gus->rend[gus->voice] = val << 14; break; - case 0x9: /*Current volume*/ + case 9: /*Current volume*/ gus->curvol[gus->voice] = gus->rcur[gus->voice] = (gus->rcur[gus->voice] & ~(0xff << 14)) | (val << 14); break; @@ -510,33 +481,75 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xFFF8000)|((val&0x7F)<<8); break; case 0x200: - gus->midi_loopback = val & 0x20; - gus->latch_enable = (val & 0x40) ? 2 : 1; + gus->latch_enable = val; break; case 0x20b: switch (gus->reg_ctrl & 0x07) { case 0: - if (gus->latch_enable == 1) - gus->dma = gus_dmas[val & 7]; - if (gus->latch_enable == 2) - { - gus->irq = gus_irqs[val & 7]; - - if (val & 0x40) - { - if (gus->irq == -1) - gus->irq = gus->irq_midi = gus_irqs[(val >> 3) & 7]; - else - gus->irq_midi = gus->irq; - } - else - gus->irq_midi = gus_irqs_midi[(val >> 3) & 7]; - - gus->sb_nmi = val & 0x80; - } - gus->latch_enable = 0; + if (gus->latch_enable & 0x40) { + // GUS SDK: IRQ Control Register + // Channel 1 GF1 IRQ selector (bits 2-0) + // 0=reserved, do not use + // 1=IRQ2 + // 2=IRQ5 + // 3=IRQ3 + // 4=IRQ7 + // 5=IRQ11 + // 6=IRQ12 + // 7=IRQ15 + // Channel 2 MIDI IRQ selector (bits 5-3) + // 0=no interrupt + // 1=IRQ2 + // 2=IRQ5 + // 3=IRQ3 + // 4=IRQ7 + // 5=IRQ11 + // 6=IRQ12 + // 7=IRQ15 + // Combine both IRQs using channel 1 (bit 6) + // Reserved (bit 7) + // + // "If both channels are sharing an IRQ, channel 2's IRQ must be set to 0 and turn on bit 6. A + // bus conflict will occur if both latches are programmed with the same IRQ #." + if (gus_irqs[val & 7] != -1) + gus->irq = gus_irqs[val & 7]; + + if (val & 0x40) // "Combine both IRQs" + gus->irq_midi = gus->irq; + else + gus->irq_midi = gus_irqs[(val >> 3) & 7]; + + gus->sb_nmi = val & 0x80; + } else { + // GUS SDK: DMA Control Register + // Channel 1 (bits 2-0) + // 0=NO DMA + // 1=DMA1 + // 2=DMA3 + // 3=DMA5 + // 4=DMA6 + // 5=DMA7 + // 6=? + // 7=? + // Channel 2 (bits 5-3) + // 0=NO DMA + // 1=DMA1 + // 2=DMA3 + // 3=DMA5 + // 4=DMA6 + // 5=DMA7 + // 6=? + // 7=? + // Combine both DMA channels using channel 1 (bit 6) + // Reserved (bit 7) + // + // "If both channels are sharing an DMA, channel 2's DMA must be set to 0 and turn on bit 6. A + // bus conflict will occur if both latches are programmed with the same DMA #." + if (gus_dmas[val & 7] != -1) + gus->dma = gus_dmas[val & 7]; + } break; case 1: gus->gp1 = val; @@ -559,13 +572,12 @@ gus->curx[gus->voice]=(gus->curx[gus->voice]&0xFFF8000)|((val&0x7F)<<8); break; case 0x206: - gus->ad_status |= 0x08; - if (gus->sb_ctrl & 0x20) - { + if (gus->sb_ctrl & 0x20) { + gus->ad_status |= 0x08; if (gus->sb_nmi) - nmi = 1; + nmi = 1; else if (gus->irq != -1) - picint(1 << gus->irq); + picint(1 << gus->irq); } break; case 0x20a: @@ -607,26 +619,49 @@ uint8_t readgus(uint16_t addr, void *p) switch (port) { case 0x300: /*MIDI status*/ - val = gus->midi_status; + val = ((gus->irqstatus & 0x03 ? 0x80 : 0) | + (gus->uart_in && gus->uart_used >= 64 ? 0x20:0) | + ((gus->uart_used && gus->uart_in) ? 1 : 0) | (gus->uart_out ? 2 : 0)); break; case 0x301: /*MIDI data*/ - val = gus->midi_data; - gus->midi_status &= ~MIDI_INT_RECEIVE; - gus_midi_update_int_status(gus); + val = 0; + if (gus->uart_in) { + if (gus->uart_used) { + if (gus->uart_pos >= 64) + gus->uart_pos -= 64; + val = gus->uart_queue[gus->uart_pos]; + gus->uart_pos++; + gus->uart_used--; + } + if (!gus->uart_used) + gus->irqstatus &= ~0x02; + } break; - case 0x200: return 0; - case 0x206: /*IRQ status*/ + case 0x200: + val = 0xff; + break; + + case 0x206: /*IRQ status*/ val = gus->irqstatus & ~0x10; if (gus->ad_status & 0x19) val |= 0x10; - return val; + break; - case 0x20F: return 0; - case 0x302: return gus->voice; - case 0x303: return gus->global; - case 0x304: /*Global low*/ + case 0x20F: + val = 0; + break; + + case 0x302: + val = gus->voice; + break; + + case 0x303: + val = gus->global; + break; + + case 0x304: /*Global low*/ switch (gus->global) { case 0x82: /*Start addr high*/ @@ -798,7 +833,6 @@ void gus_poll_timer_1(void *p) if (gus->irq != -1) picint(1 << gus->irq); } - gus_midi_update_int_status(gus); } void gus_poll_timer_2(void *p) @@ -1026,6 +1060,55 @@ static void gus_get_buffer(int32_t *buffer, int len, void *p) gus->pos = 0; } +static void gus_queue(gus_t *gus, uint8_t val) +{ + int pos; + + if (gus->uart_used < 64) { + pos = gus->uart_used + gus->uart_pos; + if (pos >= 64) + pos -= 64; + gus->uart_queue[pos] = val; + gus->uart_used++; + } +} + +static void gus_input_msg(void *p, uint8_t *msg) +{ + gus_t *gus = (gus_t *)p; + uint8_t i; + + if (gus->sysex) + return; + + if (gus->uart_in) { + gus->irqstatus |= 0x02; + for (i=0;iirq_midi); + } +} + +static int gus_input_sysex(void *p, uint8_t *buffer, uint32_t len, int abort) +{ + gus_t *gus = (gus_t *)p; + uint32_t i; + + if (abort) { + gus->uart_used = 0; + gus->sysex = 0; + return 0; + } + gus->sysex = 1; + for (i=0;iuart_used >= 64) { + return (len-i); + } + gus_queue(gus, buffer[i]); + } + gus->sysex = 0; + return 0; +} void *gus_init(const device_t *info) { @@ -1062,6 +1145,7 @@ void *gus_init(const device_t *info) io_sethandler(gus->base, 0x0010, readgus, NULL, NULL, writegus, NULL, NULL, gus); io_sethandler(0x0100+gus->base, 0x0010, readgus, NULL, NULL, writegus, NULL, NULL, gus); io_sethandler(0x0506+gus->base, 0x0001, readgus, NULL, NULL, writegus, NULL, NULL, gus); + io_sethandler(0x0388, 0x0002, readgus, NULL, NULL, writegus, NULL, NULL, gus); timer_add(&gus->samp_timer, gus_poll_wave, gus, 1); timer_add(&gus->timer_1, gus_poll_timer_1, gus, 1); @@ -1069,6 +1153,10 @@ void *gus_init(const device_t *info) sound_add_handler(gus_get_buffer, gus); + input_msg = gus_input_msg; + input_sysex = gus_input_sysex; + midi_in_p = gus; + return gus; }