Files
86Box/src/sound/snd_gus.c
Jasmine Iwanek 63066a9084 Preliminary SB AWE64 Value and Standard, also, correct memory sizes
Also add 2MB option for AWE64 Value and Standard.

People sometimes removed the onboard RAM and replaced it with a 2MB chip.
2022-02-12 21:35:19 -05:00

1327 lines
46 KiB
C

#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/io.h>
#include <86box/nmi.h>
#include <86box/pic.h>
#include <86box/dma.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/sound.h>
#include <86box/midi.h>
#include <86box/snd_ad1848.h>
#include <math.h>
enum
{
MIDI_INT_RECEIVE = 0x01,
MIDI_INT_TRANSMIT = 0x02,
MIDI_INT_MASTER = 0x80
};
enum
{
MIDI_CTRL_TRANSMIT_MASK = 0x60,
MIDI_CTRL_TRANSMIT = 0x20,
MIDI_CTRL_RECEIVE = 0x80
};
enum
{
GUS_INT_MIDI_TRANSMIT = 0x01,
GUS_INT_MIDI_RECEIVE = 0x02
};
enum
{
GUS_TIMER_CTRL_AUTO = 0x01
};
enum
{
GUS_CLASSIC = 0,
GUS_MAX = 1,
};
typedef struct gus_t
{
int reset;
int global;
uint32_t addr,dmaaddr;
int voice;
uint32_t start[32],end[32],cur[32];
uint32_t startx[32],endx[32],curx[32];
int rstart[32],rend[32];
int rcur[32];
uint16_t freq[32];
uint16_t rfreq[32];
uint8_t ctrl[32];
uint8_t rctrl[32];
int curvol[32];
int pan_l[32], pan_r[32];
int t1on,t2on;
uint8_t tctrl;
uint16_t t1,t2,t1l,t2l;
uint8_t irqstatus,irqstatus2;
uint8_t adcommand;
int waveirqs[32],rampirqs[32];
int voices;
uint8_t dmactrl;
int32_t out_l, out_r;
int16_t buffer[2][SOUNDBUFLEN];
int pos;
pc_timer_t samp_timer;
uint64_t samp_latch;
uint8_t *ram;
uint32_t gus_end_ram;
int irqnext;
pc_timer_t timer_1, timer_2;
int irq, dma, irq_midi;
uint16_t base;
int latch_enable;
uint8_t sb_2xa, sb_2xc, sb_2xe;
uint8_t sb_ctrl;
int sb_nmi;
uint8_t reg_ctrl;
uint8_t ad_status, ad_data;
uint8_t ad_timer_ctrl;
uint8_t midi_ctrl, midi_status, midi_queue[64], midi_data;
int midi_r, midi_w;
int uart_in, uart_out, sysex;
uint8_t gp1, gp2;
uint16_t gp1_addr, gp2_addr;
uint8_t usrr;
uint8_t max_ctrl;
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
ad1848_t ad1848;
#endif
} gus_t;
static int gus_gf1_irqs[8] = {-1, 2, 5, 3, 7, 11, 12, 15};
static int gus_midi_irqs[8] = {-1, 2, 5, 3, 7, 11, 12, 15};
static int gus_dmas[8] = {-1, 1, 3, 5, 6, 7, -1, -1};
int gusfreqs[]=
{
44100,41160,38587,36317,34300,32494,30870,29400,28063,26843,25725,24696,
23746,22866,22050,21289,20580,19916,19293
};
double vol16bit[4096];
void gus_update_int_status(gus_t *gus)
{
int c;
int irq_pending = 0;
int midi_irq_pending = 0;
gus->irqstatus&=~0x60;
gus->irqstatus2=0xE0;
for (c=0;c<32;c++)
{
if (gus->waveirqs[c])
{
gus->irqstatus2=0x60|c;
if (gus->rampirqs[c])
gus->irqstatus2 |= 0x80;
gus->irqstatus|=0x20;
irq_pending = 1;
break;
}
if (gus->rampirqs[c])
{
gus->irqstatus2=0xA0|c;
gus->irqstatus|=0x40;
irq_pending = 1;
break;
}
}
if ((gus->tctrl & 4) && (gus->irqstatus & 0x04))
irq_pending = 1; /*Timer 1 interrupt pending*/
if ((gus->tctrl & 8) && (gus->irqstatus & 0x08))
irq_pending = 1; /*Timer 2 interrupt pending*/
if ((gus->irqstatus & 0x80) && (gus->dmactrl & 0x20))
irq_pending = 1; /*DMA TC interrupt pending*/
midi_irq_pending = gus->midi_status & MIDI_INT_MASTER;
if (gus->irq == gus->irq_midi && gus->irq != -1)
{
if (irq_pending || midi_irq_pending)
picintlevel(1 << gus->irq);
else
picintc(1 << gus->irq);
}
else
{
if (gus->irq != -1)
{
if (irq_pending)
picintlevel(1 << gus->irq);
else
picintc(1 << gus->irq);
}
if (gus->irq_midi != -1)
{
if (midi_irq_pending)
picintlevel(1 << gus->irq_midi);
else
picintc(1 << gus->irq_midi);
}
}
}
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;
gus_update_int_status(gus);
}
void writegus(uint16_t addr, uint8_t val, void *p)
{
gus_t *gus = (gus_t *)p;
int c, d;
int old;
uint16_t port;
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
uint16_t csioport;
#endif
if ((addr == 0x388) || (addr == 0x389))
port = addr;
else
port = addr & 0xf0f;
switch (port)
{
case 0x300: /*MIDI control*/
old = gus->midi_ctrl;
gus->midi_ctrl = val;
gus->uart_out = 1;
if ((val & 3) == 3) { /*Master reset*/
gus->uart_in = 0;
gus->midi_status = 0;
gus->midi_r = 0;
gus->midi_w = 0;
} else if ((old & 3) == 3) {
gus->midi_status |= MIDI_INT_TRANSMIT;
} else if (gus->midi_ctrl & MIDI_CTRL_RECEIVE) {
gus->uart_in = 1;
}
gus_midi_update_int_status(gus);
break;
case 0x301: /*MIDI data*/
gus->midi_data = val;
if (gus->uart_out) {
midi_raw_out_byte(val);
}
if (gus->latch_enable & 0x20) {
gus->midi_status |= MIDI_INT_RECEIVE;
} else
gus->midi_status |= MIDI_INT_TRANSMIT;
break;
case 0x302: /*Voice select*/
gus->voice=val&31;
break;
case 0x303: /*Global select*/
gus->global=val;
break;
case 0x304: /*Global low*/
switch (gus->global)
{
case 0: /*Voice control*/
gus->ctrl[gus->voice]=val;
break;
case 1: /*Frequency control*/
gus->freq[gus->voice]=(gus->freq[gus->voice]&0xFF00)|val;
break;
case 2: /*Start addr high*/
gus->startx[gus->voice]=(gus->startx[gus->voice]&0xF807F)|(val<<7);
gus->start[gus->voice]=(gus->start[gus->voice]&0x1F00FFFF)|(val<<16);
break;
case 3: /*Start addr low*/
gus->start[gus->voice]=(gus->start[gus->voice]&0x1FFFFF00)|val;
break;
case 4: /*End addr high*/
gus->endx[gus->voice]=(gus->endx[gus->voice]&0xF807F)|(val<<7);
gus->end[gus->voice]=(gus->end[gus->voice]&0x1F00FFFF)|(val<<16);
break;
case 5: /*End addr low*/
gus->end[gus->voice]=(gus->end[gus->voice]&0x1FFFFF00)|val;
break;
case 6: /*Ramp frequency*/
gus->rfreq[gus->voice] = (int)( (double)((val & 63)*512)/(double)(1 << (3*(val >> 6))));
break;
case 9: /*Current volume*/
gus->curvol[gus->voice] = gus->rcur[gus->voice] = (gus->rcur[gus->voice] & ~(0xff << 6)) | (val << 6);
break;
case 0xA: /*Current addr high*/
gus->cur[gus->voice]=(gus->cur[gus->voice]&0x1F00FFFF)|(val<<16);
gus->curx[gus->voice]=(gus->curx[gus->voice]&0xF807F00)|((val<<7)<<8);
break;
case 0xB: /*Current addr low*/
gus->cur[gus->voice]=(gus->cur[gus->voice]&0x1FFFFF00)|val;
break;
case 0x42: /*DMA address low*/
gus->dmaaddr=(gus->dmaaddr&0xFF000)|(val<<4);
break;
case 0x43: /*Address low*/
gus->addr=(gus->addr&0xFFF00)|val;
break;
case 0x45: /*Timer control*/
gus->tctrl=val;
gus_update_int_status(gus);
break;
}
break;
case 0x305: /*Global high*/
switch (gus->global)
{
case 0: /*Voice control*/
gus->ctrl[gus->voice] = val & 0x7f;
old = gus->waveirqs[gus->voice];
gus->waveirqs[gus->voice] = ((val & 0xa0) == 0xa0) ? 1 : 0;
if (gus->waveirqs[gus->voice] != old)
gus_update_int_status(gus);
break;
case 1: /*Frequency control*/
gus->freq[gus->voice]=(gus->freq[gus->voice]&0xFF)|(val<<8);
break;
case 2: /*Start addr high*/
gus->startx[gus->voice]=(gus->startx[gus->voice]&0x07FFF)|(val<<15);
gus->start[gus->voice]=(gus->start[gus->voice]&0x00FFFFFF)|((val&0x1F)<<24);
break;
case 3: /*Start addr low*/
gus->startx[gus->voice]=(gus->startx[gus->voice]&0xFFF80)|(val&0x7F);
gus->start[gus->voice]=(gus->start[gus->voice]&0x1FFF00FF)|(val<<8);
break;
case 4: /*End addr high*/
gus->endx[gus->voice]=(gus->endx[gus->voice]&0x07FFF)|(val<<15);
gus->end[gus->voice]=(gus->end[gus->voice]&0x00FFFFFF)|((val&0x1F)<<24);
break;
case 5: /*End addr low*/
gus->endx[gus->voice]=(gus->endx[gus->voice]&0xFFF80)|(val&0x7F);
gus->end[gus->voice]=(gus->end[gus->voice]&0x1FFF00FF)|(val<<8);
break;
case 6: /*Ramp frequency*/
gus->rfreq[gus->voice] = (int)( (double)((val & 63) * (1 << 10))/(double)(1 << (3 * (val >> 6))));
break;
case 7: /*Ramp start*/
gus->rstart[gus->voice] = val << 14;
break;
case 8: /*Ramp end*/
gus->rend[gus->voice] = val << 14;
break;
case 9: /*Current volume*/
gus->curvol[gus->voice] = gus->rcur[gus->voice] = (gus->rcur[gus->voice] & ~(0xff << 14)) | (val << 14);
break;
case 0xA: /*Current addr high*/
gus->cur[gus->voice]=(gus->cur[gus->voice]&0x00FFFFFF)|((val&0x1F)<<24);
gus->curx[gus->voice]=(gus->curx[gus->voice]&0x07FFF00)|((val<<15)<<8);
break;
case 0xB: /*Current addr low*/
gus->cur[gus->voice]=(gus->cur[gus->voice]&0x1FFF00FF)|(val<<8);
gus->curx[gus->voice]=(gus->curx[gus->voice]&0xFFF8000)|((val&0x7F)<<8);
break;
case 0xC: /*Pan*/
gus->pan_l[gus->voice] = 15 - (val & 0xf);
gus->pan_r[gus->voice] = (val & 0xf);
break;
case 0xD: /*Ramp control*/
old = gus->rampirqs[gus->voice];
gus->rctrl[gus->voice] = val & 0x7F;
gus->rampirqs[gus->voice] = ((val & 0xa0) == 0xa0) ? 1 : 0;
if (gus->rampirqs[gus->voice] != old)
gus_update_int_status(gus);
break;
case 0xE:
gus->voices=(val&63)+1;
if (gus->voices>32) gus->voices=32;
if (gus->voices<14) gus->voices=14;
gus->global=val;
if (gus->voices < 14)
gus->samp_latch = (uint64_t)(TIMER_USEC * (1000000.0 / 44100.0));
else
gus->samp_latch = (uint64_t)(TIMER_USEC * (1000000.0 / gusfreqs[gus->voices - 14]));
break;
case 0x41: /*DMA*/
if (val&1 && gus->dma != -1)
{
if (val & 2)
{
c=0;
while (c<65536)
{
int dma_result;
if (val & 0x04)
{
uint32_t gus_addr = (gus->dmaaddr & 0xc0000) | ((gus->dmaaddr & 0x1ffff) << 1);
d = gus->ram[gus_addr] | (gus->ram[gus_addr + 1] << 8);
if (val & 0x80)
d ^= 0x8080;
dma_result = dma_channel_write(gus->dma, d);
if (dma_result == DMA_NODATA)
break;
}
else
{
d = gus->ram[gus->dmaaddr];
if (val & 0x80)
d ^= 0x80;
dma_result = dma_channel_write(gus->dma, d);
if (dma_result == DMA_NODATA)
break;
}
gus->dmaaddr++;
gus->dmaaddr &= 0xFFFFF;
c++;
if (dma_result & DMA_OVER)
break;
}
gus->dmactrl=val&~0x40;
gus->irqnext=1;
}
else
{
c=0;
while (c<65536)
{
d = dma_channel_read(gus->dma);
if (d == DMA_NODATA)
break;
if (val & 0x04)
{
uint32_t gus_addr = (gus->dmaaddr & 0xc0000) | ((gus->dmaaddr & 0x1ffff) << 1);
if (val & 0x80)
d ^= 0x8080;
gus->ram[gus_addr] = d & 0xff;
gus->ram[gus_addr +1] = (d >> 8) & 0xff;
}
else
{
if (val & 0x80)
d ^= 0x80;
gus->ram[gus->dmaaddr] = d;
}
gus->dmaaddr++;
gus->dmaaddr &= 0xFFFFF;
c++;
if (d & DMA_OVER)
break;
}
gus->dmactrl=val&~0x40;
gus->irqnext=1;
}
}
break;
case 0x42: /*DMA address low*/
gus->dmaaddr=(gus->dmaaddr&0xFF0)|(val<<12);
break;
case 0x43: /*Address low*/
gus->addr=(gus->addr&0xF00FF)|(val<<8);
break;
case 0x44: /*Address high*/
gus->addr=(gus->addr&0xFFFF)|((val<<16)&0xF0000);
break;
case 0x45: /*Timer control*/
if (!(val&4)) gus->irqstatus&=~4;
if (!(val&8)) gus->irqstatus&=~8;
if (!(val & 0x20))
{
gus->ad_status &= ~0x18;
nmi = 0;
}
if (!(val & 0x02))
{
gus->ad_status &= ~0x01;
nmi = 0;
}
gus->tctrl=val;
gus->sb_ctrl = val;
gus_update_int_status(gus);
break;
case 0x46: /*Timer 1*/
gus->t1 = gus->t1l = val;
gus->t1on = 1;
break;
case 0x47: /*Timer 2*/
gus->t2 = gus->t2l = val;
gus->t2on = 1;
break;
case 0x4c: /*Reset*/
gus->reset = val;
break;
}
break;
case 0x307: /*DRAM access*/
if (gus->addr < gus->gus_end_ram)
gus->ram[gus->addr]=val;
gus->addr&=0xFFFFF;
break;
case 0x208: case 0x388:
gus->adcommand = val;
break;
case 0x389:
if ((gus->tctrl & GUS_TIMER_CTRL_AUTO) || gus->adcommand != 4)
{
gus->ad_data = val;
gus->ad_status |= 0x01;
if (gus->sb_ctrl & 0x02)
{
if (gus->sb_nmi)
nmi = 1;
else if (gus->irq != -1)
picint(1 << gus->irq);
}
}
else if (!(gus->tctrl & GUS_TIMER_CTRL_AUTO) && gus->adcommand == 4)
{
if (val & 0x80)
{
gus->ad_status &= ~0x60;
}
else
{
gus->ad_timer_ctrl = val;
if (val & 0x01)
gus->t1on = 1;
else
gus->t1 = gus->t1l;
if (val & 0x02)
gus->t2on = 1;
else
gus->t2 = gus->t2l;
}
}
break;
case 0x200:
gus->latch_enable = val;
break;
case 0x20b:
switch (gus->reg_ctrl & 0x07)
{
case 0:
if (gus->latch_enable & 0x40) {
gus->irq = gus_gf1_irqs[val & 7];
if (val & 0x40)
{
if (gus->irq == -1)
gus->irq = gus->irq_midi = gus_gf1_irqs[(val >> 3) & 7];
else
gus->irq_midi = gus->irq;
}
else
gus->irq_midi = gus_midi_irqs[(val >> 3) & 7];
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
ad1848_setirq(&gus->ad1848, gus->irq);
#endif
gus->sb_nmi = val & 0x80;
} else {
gus->dma = gus_dmas[val & 7];
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
ad1848_setdma(&gus->ad1848, gus->dma);
#endif
}
break;
case 1:
gus->gp1 = val;
break;
case 2:
gus->gp2 = val;
break;
case 3:
gus->gp1_addr = val;
break;
case 4:
gus->gp2_addr = val;
break;
case 5:
gus->usrr = 0;
break;
case 6:
break;
}
break;
case 0x206:
gus->ad_status |= 0x08;
if (gus->sb_ctrl & 0x20)
{
if (gus->sb_nmi)
nmi = 1;
else if (gus->irq != -1)
picint(1 << gus->irq);
}
break;
case 0x20a:
gus->sb_2xa = val;
break;
case 0x20c:
gus->ad_status |= 0x10;
if (gus->sb_ctrl & 0x20)
{
if (gus->sb_nmi)
nmi = 1;
else if (gus->irq != -1)
picint(1 << gus->irq);
}
/*FALLTHROUGH*/
case 0x20d:
gus->sb_2xc = val;
break;
case 0x20e:
gus->sb_2xe = val;
break;
case 0x20f:
gus->reg_ctrl = val;
break;
case 0x306: case 0x706:
if (gus->dma >= 4)
val |= 0x30;
gus->max_ctrl = (val >> 6) & 1;
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
if (val & 0x40) {
if ((val & 0xF) != ((addr >> 4) & 0xF)) {
csioport = 0x30c | ((addr >> 4) & 0xf);
io_removehandler(csioport, 4,
ad1848_read,NULL,NULL,
ad1848_write,NULL,NULL,&gus->ad1848);
csioport = 0x30c | ((val & 0xf) << 4);
io_sethandler(csioport, 4,
ad1848_read,NULL,NULL,
ad1848_write,NULL,NULL, &gus->ad1848);
}
}
#endif
break;
}
}
uint8_t readgus(uint16_t addr, void *p)
{
gus_t *gus = (gus_t *)p;
uint8_t val = 0xff;
uint16_t port;
if ((addr == 0x388) || (addr == 0x389))
port = addr;
else
port = addr & 0xf0f;
switch (port)
{
case 0x300: /*MIDI status*/
val = gus->midi_status;
break;
case 0x301: /*MIDI data*/
val = 0;
if (gus->uart_in) {
if ((gus->midi_data == 0xaa) && (gus->midi_ctrl & MIDI_CTRL_RECEIVE)) /*Handle master reset*/
val = gus->midi_data;
else {
val = gus->midi_queue[gus->midi_r];
if (gus->midi_r != gus->midi_w) {
gus->midi_r++;
gus->midi_r &= 63;
}
}
gus->midi_status &= ~MIDI_INT_RECEIVE;
gus_midi_update_int_status(gus);
}
break;
case 0x200:
return 0;
case 0x206: /*IRQ status*/
val = gus->irqstatus & ~0x10;
if (gus->ad_status & 0x19)
val |= 0x10;
return val;
case 0x20F:
if (gus->max_ctrl)
val = 0x02;
else
val = 0x00;
break;
case 0x302:
return gus->voice;
case 0x303:
return gus->global;
case 0x304: /*Global low*/
switch (gus->global)
{
case 0x82: /*Start addr high*/
return gus->start[gus->voice]>>16;
case 0x83: /*Start addr low*/
return gus->start[gus->voice]&0xFF;
case 0x89: /*Current volume*/
return gus->rcur[gus->voice]>>6;
case 0x8A: /*Current addr high*/
return gus->cur[gus->voice]>>16;
case 0x8B: /*Current addr low*/
return gus->cur[gus->voice]&0xFF;
case 0x8F: /*IRQ status*/
val=gus->irqstatus2;
gus->rampirqs[gus->irqstatus2&0x1F]=0;
gus->waveirqs[gus->irqstatus2&0x1F]=0;
gus_update_int_status(gus);
return val;
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
val = 0xff;
break;
}
break;
case 0x305: /*Global high*/
switch (gus->global)
{
case 0x80: /*Voice control*/
return gus->ctrl[gus->voice]|(gus->waveirqs[gus->voice]?0x80:0);
case 0x82: /*Start addr high*/
return gus->start[gus->voice]>>24;
case 0x83: /*Start addr low*/
return gus->start[gus->voice]>>8;
case 0x89: /*Current volume*/
return gus->rcur[gus->voice]>>14;
case 0x8A: /*Current addr high*/
return gus->cur[gus->voice]>>24;
case 0x8B: /*Current addr low*/
return gus->cur[gus->voice]>>8;
case 0x8C: /*Pan*/
return gus->pan_r[gus->voice];
case 0x8D:
return gus->rctrl[gus->voice]|(gus->rampirqs[gus->voice]?0x80:0);
case 0x8F: /*IRQ status*/
val=gus->irqstatus2;
gus->rampirqs[gus->irqstatus2&0x1F]=0;
gus->waveirqs[gus->irqstatus2&0x1F]=0;
gus_update_int_status(gus);
return val;
case 0x41: /*DMA control*/
val=gus->dmactrl|((gus->irqstatus&0x80)?0x40:0);
gus->irqstatus&=~0x80;
return val;
case 0x45: /*Timer control*/
return gus->tctrl;
case 0x49: /*Sampling control*/
return 0;
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
val = 0xff;
break;
}
break;
case 0x306: case 0x706:
if (gus->max_ctrl)
val = 0x0a; /* GUS MAX */
else
val = 0xff; /*Pre 3.7 - no mixer*/
break;
break;
case 0x307: /*DRAM access*/
val=gus->ram[gus->addr];
gus->addr&=0xFFFFF;
if (gus->addr < gus->gus_end_ram)
val = gus->ram[gus->addr];
else
val = 0;
return val;
case 0x309: return 0;
case 0x20b:
switch (gus->reg_ctrl & 0x07)
{
case 1:
val = gus->gp1;
break;
case 2:
val = gus->gp2;
break;
case 3:
val = gus->gp1_addr;
break;
case 4:
val = gus->gp2_addr;
break;
}
break;
case 0x20c:
val = gus->sb_2xc;
if (gus->reg_ctrl & 0x20)
gus->sb_2xc &= 0x80;
break;
case 0x20e:
return gus->sb_2xe;
case 0x208: case 0x388:
if (gus->tctrl & GUS_TIMER_CTRL_AUTO)
val = gus->sb_2xa;
else
{
val = gus->ad_status & ~(gus->ad_timer_ctrl & 0x60);
if (val & 0x60)
val |= 0x80;
}
break;
case 0x209:
gus->ad_status &= ~0x01;
nmi = 0;
/*FALLTHROUGH*/
case 0x389:
val = gus->ad_data;
break;
case 0x20A:
val = gus->adcommand;
break;
}
return val;
}
void gus_poll_timer_1(void *p)
{
gus_t *gus = (gus_t *)p;
timer_advance_u64(&gus->timer_1, (uint64_t)(TIMER_USEC * 80));
if (gus->t1on)
{
gus->t1++;
if (gus->t1 > 0xFF)
{
gus->t1=gus->t1l;
gus->ad_status |= 0x40;
if (gus->tctrl&4)
{
gus->ad_status |= 0x04;
gus->irqstatus |= 0x04;
}
}
}
if (gus->irqnext)
{
gus->irqnext=0;
gus->irqstatus|=0x80;
}
gus_midi_update_int_status(gus);
gus_update_int_status(gus);
}
void gus_poll_timer_2(void *p)
{
gus_t *gus = (gus_t *)p;
timer_advance_u64(&gus->timer_2, (uint64_t)(TIMER_USEC * 320));
if (gus->t2on)
{
gus->t2++;
if (gus->t2 > 0xFF)
{
gus->t2=gus->t2l;
gus->ad_status |= 0x20;
if (gus->tctrl&8)
{
gus->ad_status |= 0x02;
gus->irqstatus |= 0x08;
}
}
}
if (gus->irqnext)
{
gus->irqnext=0;
gus->irqstatus|=0x80;
}
gus_update_int_status(gus);
}
static void gus_update(gus_t *gus)
{
for (; gus->pos < sound_pos_global; gus->pos++)
{
if (gus->out_l < -32768)
gus->buffer[0][gus->pos] = -32768;
else if (gus->out_l > 32767)
gus->buffer[0][gus->pos] = 32767;
else
gus->buffer[0][gus->pos] = gus->out_l;
if (gus->out_r < -32768)
gus->buffer[1][gus->pos] = -32768;
else if (gus->out_r > 32767)
gus->buffer[1][gus->pos] = 32767;
else
gus->buffer[1][gus->pos] = gus->out_r;
}
}
void gus_poll_wave(void *p)
{
gus_t *gus = (gus_t *)p;
uint32_t addr;
int d;
int16_t v;
int32_t vl;
int update_irqs = 0;
gus_update(gus);
timer_advance_u64(&gus->samp_timer, gus->samp_latch);
gus->out_l = gus->out_r = 0;
if ((gus->reset & 3) != 3)
return;
for (d=0;d<32;d++)
{
if (!(gus->ctrl[d] & 3))
{
if (gus->ctrl[d] & 4)
{
addr = gus->cur[d] >> 9;
addr = (addr & 0xC0000) | ((addr << 1) & 0x3FFFE);
if (!(gus->freq[d] >> 10)) /*Interpolate*/
{
vl = (int16_t)(int8_t)((gus->ram[(addr + 1) & 0xFFFFF] ^ 0x80) - 0x80) * (511 - (gus->cur[d] & 511));
vl += (int16_t)(int8_t)((gus->ram[(addr + 3) & 0xFFFFF] ^ 0x80) - 0x80) * (gus->cur[d] & 511);
v = vl >> 9;
}
else
v = (int16_t)(int8_t)((gus->ram[(addr + 1) & 0xFFFFF] ^ 0x80) - 0x80);
}
else
{
if (!(gus->freq[d] >> 10)) /*Interpolate*/
{
vl = ((int8_t)((gus->ram[(gus->cur[d] >> 9) & 0xFFFFF] ^ 0x80) - 0x80)) * (511 - (gus->cur[d] & 511));
vl += ((int8_t)((gus->ram[((gus->cur[d] >> 9) + 1) & 0xFFFFF] ^ 0x80) - 0x80)) * (gus->cur[d] & 511);
v = vl >> 9;
}
else
v = (int16_t)(int8_t)((gus->ram[(gus->cur[d] >> 9) & 0xFFFFF] ^ 0x80) - 0x80);
}
if ((gus->rcur[d] >> 14) > 4095) v = (int16_t)(float)(v) * 24.0 * vol16bit[4095];
else v = (int16_t)(float)(v) * 24.0 * vol16bit[(gus->rcur[d]>>10) & 4095];
gus->out_l += (v * gus->pan_l[d]) / 7;
gus->out_r += (v * gus->pan_r[d]) / 7;
if (gus->ctrl[d]&0x40)
{
gus->cur[d] -= (gus->freq[d] >> 1);
if (gus->cur[d] <= gus->start[d])
{
int diff = gus->start[d] - gus->cur[d];
if (gus->ctrl[d]&8)
{
if (gus->ctrl[d]&0x10) gus->ctrl[d]^=0x40;
gus->cur[d] = (gus->ctrl[d] & 0x40) ? (gus->end[d] - diff) : (gus->start[d] + diff);
}
else if (!(gus->rctrl[d]&4))
{
gus->ctrl[d] |= 1;
gus->cur[d] = (gus->ctrl[d] & 0x40) ? gus->end[d] : gus->start[d];
}
if ((gus->ctrl[d] & 0x20) && !gus->waveirqs[d])
{
gus->waveirqs[d] = 1;
update_irqs = 1;
}
}
}
else
{
gus->cur[d] += (gus->freq[d] >> 1);
if (gus->cur[d] >= gus->end[d])
{
int diff = gus->cur[d] - gus->end[d];
if (gus->ctrl[d]&8)
{
if (gus->ctrl[d]&0x10) gus->ctrl[d]^=0x40;
gus->cur[d] = (gus->ctrl[d] & 0x40) ? (gus->end[d] - diff) : (gus->start[d] + diff);
}
else if (!(gus->rctrl[d]&4))
{
gus->ctrl[d] |= 1;
gus->cur[d] = (gus->ctrl[d] & 0x40) ? gus->end[d] : gus->start[d];
}
if ((gus->ctrl[d] & 0x20) && !gus->waveirqs[d])
{
gus->waveirqs[d] = 1;
update_irqs = 1;
}
}
}
}
if (!(gus->rctrl[d] & 3))
{
if (gus->rctrl[d] & 0x40)
{
gus->rcur[d] -= gus->rfreq[d];
if (gus->rcur[d] <= gus->rstart[d])
{
int diff = gus->rstart[d] - gus->rcur[d];
if (!(gus->rctrl[d] & 8))
{
gus->rctrl[d] |= 1;
gus->rcur[d] = (gus->rctrl[d] & 0x40) ? gus->rstart[d] : gus->rend[d];
}
else
{
if (gus->rctrl[d] & 0x10) gus->rctrl[d] ^= 0x40;
gus->rcur[d] = (gus->rctrl[d] & 0x40) ? (gus->rend[d] - diff) : (gus->rstart[d] + diff);
}
if ((gus->rctrl[d] & 0x20) && !gus->rampirqs[d])
{
gus->rampirqs[d] = 1;
update_irqs = 1;
}
}
}
else
{
gus->rcur[d] += gus->rfreq[d];
if (gus->rcur[d] >= gus->rend[d])
{
int diff = gus->rcur[d] - gus->rend[d];
if (!(gus->rctrl[d] & 8))
{
gus->rctrl[d] |= 1;
gus->rcur[d] = (gus->rctrl[d] & 0x40) ? gus->rstart[d] : gus->rend[d];
}
else
{
if (gus->rctrl[d] & 0x10) gus->rctrl[d] ^= 0x40;
gus->rcur[d] = (gus->rctrl[d] & 0x40) ? (gus->rend[d] - diff) : (gus->rstart[d] + diff);
}
if ((gus->rctrl[d] & 0x20) && !gus->rampirqs[d])
{
gus->rampirqs[d] = 1;
update_irqs = 1;
}
}
}
}
}
if (update_irqs)
gus_update_int_status(gus);
}
static void gus_get_buffer(int32_t *buffer, int len, void *p)
{
gus_t *gus = (gus_t *)p;
int c;
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
if (gus->max_ctrl)
ad1848_update(&gus->ad1848);
#endif
gus_update(gus);
for (c = 0; c < len * 2; c++)
{
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
if (gus->max_ctrl)
buffer[c] += (int32_t)(gus->ad1848.buffer[c] / 2);
#endif
buffer[c] += (int32_t)gus->buffer[c & 1][c >> 1];
}
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
if (gus->max_ctrl)
gus->ad1848.pos = 0;
#endif
gus->pos = 0;
}
static void gus_input_msg(void *p, uint8_t *msg, uint32_t len)
{
gus_t *gus = (gus_t *)p;
uint8_t i;
if (gus->sysex)
return;
if (gus->uart_in) {
gus->midi_status |= MIDI_INT_RECEIVE;
for (i=0; i < len; i++) {
gus->midi_queue[gus->midi_w++] = msg[i];
gus->midi_w &= 63;
}
gus_midi_update_int_status(gus);
}
}
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->sysex = 0;
return 0;
}
gus->sysex = 1;
for (i=0;i<len;i++) {
if (gus->midi_r == gus->midi_w)
return (len-i);
gus->midi_queue[gus->midi_w++] = buffer[i];
gus->midi_w &= 63;
}
gus->sysex = 0;
return 0;
}
void *gus_init(const device_t *info)
{
int c;
double out = 1.0;
uint8_t gus_ram = device_get_config_int("gus_ram");
gus_t *gus = malloc(sizeof(gus_t));
memset(gus, 0, sizeof(gus_t));
gus->gus_end_ram = 1 << (18 + gus_ram);
gus->ram = (uint8_t *)malloc(gus->gus_end_ram);
memset(gus->ram, 0x00, (gus->gus_end_ram));
for (c=0;c<32;c++)
{
gus->ctrl[c]=1;
gus->rctrl[c]=1;
gus->rfreq[c]=63*512;
}
for (c=4095;c>=0;c--) {
vol16bit[c]=out;
out/=1.002709201; /* 0.0235 dB Steps */
}
gus->voices=14;
gus->samp_latch = (uint64_t)(TIMER_USEC * (1000000.0 / 44100.0));
gus->t1l = gus->t2l = 0xff;
gus->uart_out = 1;
gus->base = device_get_config_hex16("base");
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);
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
ad1848_init(&gus->ad1848, AD1848_TYPE_CS4231);
ad1848_setirq(&gus->ad1848, 5);
ad1848_setdma(&gus->ad1848, 3);
io_sethandler(0x10C+gus->base, 4,
ad1848_read,NULL,NULL, ad1848_write,NULL,NULL, &gus->ad1848);
#endif
timer_add(&gus->samp_timer, gus_poll_wave, gus, 1);
timer_add(&gus->timer_1, gus_poll_timer_1, gus, 1);
timer_add(&gus->timer_2, gus_poll_timer_2, gus, 1);
sound_add_handler(gus_get_buffer, gus);
if (device_get_config_int("receive_input"))
midi_in_handler(1, gus_input_msg, gus_input_sysex, gus);
return gus;
}
void gus_close(void *p)
{
gus_t *gus = (gus_t *)p;
free(gus->ram);
free(gus);
}
void gus_speed_changed(void *p)
{
gus_t *gus = (gus_t *)p;
if (gus->voices < 14)
gus->samp_latch = (uint64_t)(TIMER_USEC * (1000000.0 / 44100.0));
else
gus->samp_latch = (uint64_t)(TIMER_USEC * (1000000.0 / gusfreqs[gus->voices - 14]));
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
if (gus->max_ctrl)
ad1848_speed_changed(&gus->ad1848);
#endif
}
static const device_config_t gus_config[] = {
{
"type", "GUS type", CONFIG_SELECTION, "", 0, "", { 0 },
{
{
"Classic", GUS_CLASSIC
},
#if defined(DEV_BRANCH) && defined(USE_GUSMAX)
{
"MAX", GUS_MAX
},
#endif
{
NULL
}
},
},
{
"base", "Address", CONFIG_HEX16, "", 0x220, "", { 0 },
{
{
"210H", 0x210
},
{
"220H", 0x220
},
{
"230H", 0x230
},
{
"240H", 0x240
},
{
"250H", 0x250
},
{
"260H", 0x260
},
},
},
{
"gus_ram", "Onboard RAM", CONFIG_SELECTION, "", 0, "", { 0 },
{
{
"256 KB", 0
},
{
"512 KB", 1
},
{
"1 MB", 2
},
{
NULL
}
}
},
{
"receive_input", "Receive input (SB MIDI)", CONFIG_BINARY, "", 1
},
{
"", "", -1
}
};
const device_t gus_device =
{
"Gravis UltraSound",
"gus",
DEVICE_ISA | DEVICE_AT,
0,
gus_init, gus_close, NULL,
{ NULL },
gus_speed_changed,
NULL,
gus_config
};