Misc ViRGE fixes. Changes to memory mapping. Changes to PCI handling. Disabled 'AMI WinBIOS 486 PCI' due to incomplete chipset emulation. Invalid GUS register reads no longer kill the emulator.
1113 lines
47 KiB
C
1113 lines
47 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "ibm.h"
|
|
|
|
#include "device.h"
|
|
#include "dma.h"
|
|
#include "io.h"
|
|
#include "pic.h"
|
|
#include "sound.h"
|
|
#include "sound_gus.h"
|
|
#include "timer.h"
|
|
|
|
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 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;
|
|
|
|
int16_t out_l, out_r;
|
|
|
|
int16_t buffer[2][SOUNDBUFLEN];
|
|
int pos;
|
|
|
|
int samp_timer, samp_latch;
|
|
|
|
uint8_t *ram;
|
|
|
|
int irqnext;
|
|
|
|
int timer_1, timer_2;
|
|
|
|
int irq, dma, irq_midi;
|
|
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;
|
|
uint8_t midi_data;
|
|
int midi_loopback;
|
|
|
|
uint8_t gp1, gp2;
|
|
uint16_t gp1_addr, gp2_addr;
|
|
|
|
uint8_t usrr;
|
|
} 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[]=
|
|
{
|
|
44100,41160,38587,36317,34300,32494,30870,29400,28063,26843,25725,24696,
|
|
23746,22866,22050,21289,20580,19916,19293
|
|
};
|
|
|
|
double vol16bit[4096];
|
|
|
|
void pollgusirqs(gus_t *gus)
|
|
{
|
|
int c;
|
|
|
|
gus->irqstatus&=~0x60;
|
|
for (c=0;c<32;c++)
|
|
{
|
|
if (gus->waveirqs[c])
|
|
{
|
|
// gus->waveirqs[c]=0;
|
|
gus->irqstatus2=0x60|c;
|
|
if (gus->rampirqs[c]) gus->irqstatus2 |= 0x80;
|
|
gus->irqstatus|=0x20;
|
|
// printf("Voice IRQ %i %02X %i\n",c,gus->irqstatus2,ins);
|
|
if (gus->irq != -1)
|
|
picint(1 << gus->irq);
|
|
return;
|
|
}
|
|
if (gus->rampirqs[c])
|
|
{
|
|
// gus->rampirqs[c]=0;
|
|
gus->irqstatus2=0xA0|c;
|
|
gus->irqstatus|=0x40;
|
|
// printf("Ramp IRQ %i %02X %i\n",c,gus->irqstatus2,ins);
|
|
if (gus->irq != -1)
|
|
picint(1 << gus->irq);
|
|
return;
|
|
}
|
|
}
|
|
gus->irqstatus2=0xE0;
|
|
// gus->irqstatus&=~0x20;
|
|
if (!gus->irqstatus && gus->irq != -1) picintc(1 << gus->irq);
|
|
}
|
|
|
|
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
|
|
};
|
|
|
|
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))
|
|
{
|
|
// pclog("Take MIDI IRQ\n");
|
|
picint(1 << gus->irq_midi);
|
|
}
|
|
}
|
|
|
|
void writegus(uint16_t addr, uint8_t val, void *p)
|
|
{
|
|
gus_t *gus = (gus_t *)p;
|
|
int c, d;
|
|
int old;
|
|
// pclog("Write GUS %04X %02X %04X:%04X\n",addr,val,CS,pc);
|
|
if (gus->latch_enable && addr != 0x24b)
|
|
gus->latch_enable = 0;
|
|
switch (addr)
|
|
{
|
|
case 0x340: /*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;
|
|
// pclog("MIDI_INT_TRANSMIT\n");
|
|
}
|
|
gus_midi_update_int_status(gus);
|
|
break;
|
|
|
|
case 0x341: /*MIDI data*/
|
|
if (gus->midi_loopback)
|
|
{
|
|
gus->midi_status |= MIDI_INT_RECEIVE;
|
|
gus->midi_data = val;
|
|
}
|
|
else
|
|
gus->midi_status |= MIDI_INT_TRANSMIT;
|
|
break;
|
|
|
|
case 0x342: /*Voice select*/
|
|
gus->voice=val&31;
|
|
break;
|
|
case 0x343: /*Global select*/
|
|
gus->global=val;
|
|
break;
|
|
case 0x344: /*Global low*/
|
|
// if (gus->global!=0x43 && gus->global!=0x44) printf("Writing register %02X %02X %02X %i\n",gus->global,gus->voice,val, ins);
|
|
switch (gus->global)
|
|
{
|
|
case 0: /*Voice control*/
|
|
// if (val&1 && !(gus->ctrl[gus->voice]&1)) printf("Voice on %i\n",gus->voice);
|
|
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);
|
|
// printf("Write %i start %08X %08X\n",gus->voice,gus->start[gus->voice],gus->startx[gus->voice]);
|
|
break;
|
|
case 3: /*Start addr low*/
|
|
gus->start[gus->voice]=(gus->start[gus->voice]&0x1FFFFF00)|val;
|
|
// printf("Write %i start %08X %08X\n",gus->voice,gus->start[gus->voice],gus->startx[gus->voice]);
|
|
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);
|
|
// printf("Write %i end %08X %08X\n",gus->voice,gus->end[gus->voice],gus->endx[gus->voice]);
|
|
break;
|
|
case 5: /*End addr low*/
|
|
gus->end[gus->voice]=(gus->end[gus->voice]&0x1FFFFF00)|val;
|
|
// printf("Write %i end %08X %08X\n",gus->voice,gus->end[gus->voice],gus->endx[gus->voice]);
|
|
break;
|
|
|
|
case 0x6: /*Ramp frequency*/
|
|
gus->rfreq[gus->voice] = (int)( (double)((val & 63)*512)/(double)(1 << (3*(val >> 6))));
|
|
// printf("RFREQ %02X %i %i %f\n",val,gus->voice,gus->rfreq[gus->voice],(double)(val & 63)/(double)(1 << (3*(val >> 6))));
|
|
break;
|
|
|
|
case 0x9: /*Current volume*/
|
|
gus->curvol[gus->voice] = gus->rcur[gus->voice] = (gus->rcur[gus->voice] & ~(0xff << 6)) | (val << 6);
|
|
// printf("Vol %i is %04X\n",gus->voice,gus->curvol[gus->voice]);
|
|
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);
|
|
// gus->cur[gus->voice]=(gus->cur[gus->voice]&0x0F807F00)|((val<<7)<<8);
|
|
// printf("Write %i cur %08X\n",gus->voice,gus->cur[gus->voice],gus->curx[gus->voice]);
|
|
break;
|
|
case 0xB: /*Current addr low*/
|
|
gus->cur[gus->voice]=(gus->cur[gus->voice]&0x1FFFFF00)|val;
|
|
// printf("Write %i cur %08X\n",gus->voice,gus->cur[gus->voice],gus->curx[gus->voice]);
|
|
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*/
|
|
// printf("Timer control %02X\n",val);
|
|
gus->tctrl=val;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x345: /*Global high*/
|
|
// if (gus->global!=0x43 && gus->global!=0x44) printf("HWriting register %02X %02X %02X %04X:%04X %i %X\n",gus->global,gus->voice,val,CS,pc, ins, gus->rcur[1] >> 10);
|
|
switch (gus->global)
|
|
{
|
|
case 0: /*Voice control*/
|
|
if (!(val&1) && gus->ctrl[gus->voice]&1)
|
|
{
|
|
// printf("Voice on %i - start %05X end %05X freq %04X\n",gus->voice,gus->start[gus->voice],gus->end[gus->voice],gus->freq[gus->voice]);
|
|
// if (val&0x40) gus->cur[gus->voice]=gus->end[gus->voice]<<8;
|
|
// else gus->cur[gus->voice]=gus->start[gus->voice]<<8;
|
|
}
|
|
|
|
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)
|
|
pollgusirqs(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);
|
|
// printf("Write %i start %08X %08X %02X\n",gus->voice,gus->start[gus->voice],gus->startx[gus->voice],val);
|
|
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);
|
|
// printf("Write %i start %08X %08X\n",gus->voice,gus->start[gus->voice],gus->startx[gus->voice]);
|
|
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);
|
|
// printf("Write %i end %08X %08X %02X\n",gus->voice,gus->end[gus->voice],gus->endx[gus->voice],val);
|
|
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);
|
|
// printf("Write %i end %08X %08X\n",gus->voice,gus->end[gus->voice],gus->endx[gus->voice]);
|
|
break;
|
|
|
|
case 0x6: /*Ramp frequency*/
|
|
gus->rfreq[gus->voice] = (int)( (double)((val & 63) * (1 << 10))/(double)(1 << (3 * (val >> 6))));
|
|
// pclog("Ramp freq %02X %i %i %f %i\n", val, gus->voice, gus->rfreq[gus->voice], (double)(val & 63)/(double)(1 << (3*(val >> 6))), ins);
|
|
break;
|
|
case 0x7: /*Ramp start*/
|
|
gus->rstart[gus->voice] = val << 14;
|
|
// pclog("Ramp start %04X\n", gus->rstart[gus->voice] >> 10);
|
|
break;
|
|
case 0x8: /*Ramp end*/
|
|
gus->rend[gus->voice] = val << 14;
|
|
// pclog("Ramp end %04X\n", gus->rend[gus->voice] >> 10);
|
|
break;
|
|
case 0x9: /*Current volume*/
|
|
gus->curvol[gus->voice] = gus->rcur[gus->voice] = (gus->rcur[gus->voice] & ~(0xff << 14)) | (val << 14);
|
|
// printf("Vol %i is %04X\n",gus->voice,gus->curvol[gus->voice]);
|
|
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);
|
|
// printf("Write %i cur %08X %08X %02X\n",gus->voice,gus->cur[gus->voice],gus->curx[gus->voice],val);
|
|
// gus->cur[gus->voice]=(gus->cur[gus->voice]&0x007FFF00)|((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);
|
|
// gus->cur[gus->voice]=(gus->cur[gus->voice]&0x0FFF8000)|((val&0x7F)<<8);
|
|
// printf("Write %i cur %08X %08X\n",gus->voice,gus->cur[gus->voice],gus->curx[gus->voice]);
|
|
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)
|
|
pollgusirqs(gus);
|
|
// printf("Ramp control %02i %02X %02X %i\n",gus->voice,val,gus->rampirqs[gus->voice],ins);
|
|
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;
|
|
// printf("GUS voices %i\n",val&31);
|
|
if (gus->voices < 14)
|
|
gus->samp_latch = (int)(TIMER_USEC * (1000000.0 / 44100.0));
|
|
else
|
|
gus->samp_latch = (int)(TIMER_USEC * (1000000.0 / gusfreqs[gus->voices - 14]));
|
|
break;
|
|
|
|
case 0x41: /*DMA*/
|
|
if (val&1 && gus->dma != -1)
|
|
{
|
|
// printf("DMA start! %05X %02X\n",gus->dmaaddr,val);
|
|
if (val & 2)
|
|
{
|
|
c=0;
|
|
while (c<65536)
|
|
{
|
|
d = gus->ram[gus->dmaaddr];
|
|
if (val & 0x80) d ^= 0x80;
|
|
if (dma_channel_write(gus->dma, d) == DMA_NODATA) break;
|
|
gus->dmaaddr++;
|
|
gus->dmaaddr&=0xFFFFF;
|
|
c++;
|
|
}
|
|
// printf("GUS->MEM Transferred %i bytes\n",c);
|
|
gus->dmactrl=val&~0x40;
|
|
if (val&0x20) gus->irqnext=1;
|
|
}
|
|
else
|
|
{
|
|
c=0;
|
|
while (c<65536)
|
|
{
|
|
d = dma_channel_read(gus->dma);
|
|
if (d == DMA_NODATA) break;
|
|
if (val&0x80) d^=0x80;
|
|
gus->ram[gus->dmaaddr]=d;
|
|
gus->dmaaddr++;
|
|
gus->dmaaddr&=0xFFFFF;
|
|
c++;
|
|
}
|
|
// printf("MEM->GUS Transferred %i bytes\n",c);
|
|
gus->dmactrl=val&~0x40;
|
|
if (val&0x20) gus->irqnext=1;
|
|
}
|
|
// exit(-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;
|
|
}
|
|
// printf("Timer control %02X\n",val);
|
|
/* if ((val&4) && !(gus->tctrl&4))
|
|
{
|
|
gus->t1=gus->t1l;
|
|
gus->t1on=1;
|
|
}*/
|
|
gus->tctrl=val;
|
|
gus->sb_ctrl = val;
|
|
break;
|
|
case 0x46: /*Timer 1*/
|
|
gus->t1 = gus->t1l = val;
|
|
gus->t1on = 1;
|
|
// printf("GUS timer 1 %i\n",val);
|
|
break;
|
|
case 0x47: /*Timer 2*/
|
|
gus->t2 = gus->t2l = val;
|
|
gus->t2on = 1;
|
|
// printf("GUS timer 2 %i\n",val);
|
|
break;
|
|
|
|
case 0x4c: /*Reset*/
|
|
gus->reset = val;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x347: /*DRAM access*/
|
|
gus->ram[gus->addr]=val;
|
|
// pclog("GUS RAM write %05X %02X\n",gus->addr,val);
|
|
gus->addr&=0xFFFFF;
|
|
break;
|
|
case 0x248: case 0x388:
|
|
gus->adcommand = val;
|
|
// pclog("Setting ad command %02X %02X %p\n", val, gus->adcommand, &gus->adcommand);
|
|
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 0x240:
|
|
gus->midi_loopback = val & 0x20;
|
|
gus->latch_enable = (val & 0x40) ? 2 : 1;
|
|
break;
|
|
|
|
case 0x24b:
|
|
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)
|
|
gus->irq_midi = gus->irq;
|
|
else
|
|
gus->irq_midi = gus_irqs_midi[(val >> 3) & 7];
|
|
|
|
gus->sb_nmi = val & 0x80;
|
|
}
|
|
gus->latch_enable = 0;
|
|
// pclog("IRQ %i DMA %i\n", gus->irq, gus->dma);
|
|
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 0x246:
|
|
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 0x24a:
|
|
gus->sb_2xa = val;
|
|
break;
|
|
case 0x24c:
|
|
gus->ad_status |= 0x10;
|
|
if (gus->sb_ctrl & 0x20)
|
|
{
|
|
if (gus->sb_nmi)
|
|
nmi = 1;
|
|
else if (gus->irq != -1)
|
|
picint(1 << gus->irq);
|
|
}
|
|
case 0x24d:
|
|
gus->sb_2xc = val;
|
|
break;
|
|
case 0x24e:
|
|
gus->sb_2xe = val;
|
|
break;
|
|
case 0x24f:
|
|
gus->reg_ctrl = val;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t readgus(uint16_t addr, void *p)
|
|
{
|
|
gus_t *gus = (gus_t *)p;
|
|
uint8_t val;
|
|
// /*if (addr!=0x246) */printf("Read GUS %04X %04X(%06X):%04X %02X\n",addr,CS,cs,pc,gus->global);
|
|
switch (addr)
|
|
{
|
|
case 0x340: /*MIDI status*/
|
|
val = gus->midi_status;
|
|
// pclog("Read MIDI status %02X\n", val);
|
|
break;
|
|
|
|
case 0x341: /*MIDI data*/
|
|
val = gus->midi_data;
|
|
gus->midi_status &= ~MIDI_INT_RECEIVE;
|
|
gus_midi_update_int_status(gus);
|
|
break;
|
|
|
|
case 0x240: return 0;
|
|
case 0x246: /*IRQ status*/
|
|
val = gus->irqstatus & ~0x10;
|
|
if (gus->ad_status & 0x19)
|
|
val |= 0x10;
|
|
// pclog("Read IRQ status %02X\n", val);
|
|
return val;
|
|
|
|
case 0x24F: return 0;
|
|
case 0x342: return gus->voice;
|
|
case 0x343: return gus->global;
|
|
case 0x344: /*Global low*/
|
|
// /*if (gus->global!=0x43 && gus->global!=0x44) */printf("Reading register %02X %02X\n",gus->global,gus->voice);
|
|
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;
|
|
// pclog("Read IRQ status - %02X\n",val);
|
|
gus->rampirqs[gus->irqstatus2&0x1F]=0;
|
|
gus->waveirqs[gus->irqstatus2&0x1F]=0;
|
|
pollgusirqs(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;
|
|
|
|
// default:
|
|
// fatal("Bad GUS global low read %02X\n",gus->global);
|
|
}
|
|
break;
|
|
case 0x345: /*Global high*/
|
|
// /*if (gus->global!=0x43 && gus->global!=0x44) */printf("HReading register %02X %02X\n",gus->global,gus->voice);
|
|
switch (gus->global)
|
|
{
|
|
case 0x80: /*Voice control*/
|
|
// pclog("Read voice control %02i %02X\n", gus->voice, gus->ctrl[gus->voice]|(gus->waveirqs[gus->voice]?0x80:0));
|
|
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*/
|
|
// pclog("Read current volume %i\n", gus->rcur[gus->voice] >> 14);
|
|
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 0x8D:
|
|
// pclog("Read ramp control %02X %04X %08X %08X %08X\n",gus->rctrl[gus->voice]|(gus->rampirqs[gus->voice]?0x80:0),gus->rcur[gus->voice] >> 14,gus->rfreq[gus->voice],gus->rstart[gus->voice],gus->rend[gus->voice]);
|
|
return gus->rctrl[gus->voice]|(gus->rampirqs[gus->voice]?0x80:0);
|
|
|
|
case 0x8F: /*IRQ status*/
|
|
// pclog("Read IRQ 1\n");
|
|
val=gus->irqstatus2;
|
|
gus->rampirqs[gus->irqstatus2&0x1F]=0;
|
|
gus->waveirqs[gus->irqstatus2&0x1F]=0;
|
|
pollgusirqs(gus);
|
|
// pclog("Read IRQ status - %02X %i %i\n",val, gus->waveirqs[gus->irqstatus2&0x1F], gus->rampirqs[gus->irqstatus2&0x1F]);
|
|
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;
|
|
|
|
// default:
|
|
// fatal("Bad GUS global high read %02X\n",gus->global);
|
|
}
|
|
break;
|
|
case 0x346: return 0xff;
|
|
case 0x347: /*DRAM access*/
|
|
val=gus->ram[gus->addr];
|
|
// pclog("GUS RAM read %05X %02X\n",gus->addr,val);
|
|
gus->addr&=0xFFFFF;
|
|
return val;
|
|
case 0x349: return 0;
|
|
case 0x746: /*Revision level*/
|
|
return 0xff; /*Pre 3.7 - no mixer*/
|
|
|
|
case 0x24b:
|
|
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 0x24c:
|
|
val = gus->sb_2xc;
|
|
if (gus->reg_ctrl & 0x20)
|
|
gus->sb_2xc &= 0x80;
|
|
break;
|
|
case 0x24e:
|
|
/* gus->ad_status |= 0x10;
|
|
if (gus->reg_ctrl & 0x80)
|
|
{
|
|
gus->reg_ctrl_r |= 0x80;
|
|
if (gus->sb_nmi)
|
|
nmi = 1;
|
|
else
|
|
picint(1 << gus->irq);
|
|
}*/
|
|
return gus->sb_2xe;
|
|
|
|
case 0x248: case 0x388:
|
|
// pclog("Read ad_status %02X\n", gus->ad_status);
|
|
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 0x249:
|
|
gus->ad_status &= ~0x01;
|
|
nmi = 0;
|
|
case 0x389:
|
|
val = gus->ad_data;
|
|
break;
|
|
|
|
case 0x24A:
|
|
val = gus->adcommand;
|
|
// pclog("Read ad command %02X %02X %p\n", gus->adcommand, val, &gus->adcommand);
|
|
break;
|
|
|
|
}
|
|
// printf("Bad GUS read %04X! %02X\n",addr,gus->global);
|
|
// exit(-1);
|
|
return val;
|
|
}
|
|
|
|
void gus_poll_timer_1(void *p)
|
|
{
|
|
gus_t *gus = (gus_t *)p;
|
|
|
|
gus->timer_1 += (TIMER_USEC * 80);
|
|
// pclog("gus_poll_timer_1 %i %i %i %i %02X\n", gustime, gus->t1on, gus->t1, gus->t1l, gus->tctrl);
|
|
if (gus->t1on)
|
|
{
|
|
gus->t1++;
|
|
if (gus->t1 > 0xFF)
|
|
{
|
|
// gus->t1on=0;
|
|
gus->t1=gus->t1l;
|
|
gus->ad_status |= 0x40;
|
|
if (gus->tctrl&4)
|
|
{
|
|
if (gus->irq != -1)
|
|
picint(1 << gus->irq);
|
|
gus->ad_status |= 0x04;
|
|
gus->irqstatus |= 0x04;
|
|
// pclog("GUS T1 IRQ!\n");
|
|
}
|
|
}
|
|
}
|
|
if (gus->irqnext)
|
|
{
|
|
// pclog("Take IRQ\n");
|
|
gus->irqnext=0;
|
|
gus->irqstatus|=0x80;
|
|
if (gus->irq != -1)
|
|
picint(1 << gus->irq);
|
|
}
|
|
gus_midi_update_int_status(gus);
|
|
}
|
|
|
|
void gus_poll_timer_2(void *p)
|
|
{
|
|
gus_t *gus = (gus_t *)p;
|
|
|
|
gus->timer_2 += (TIMER_USEC * 320);
|
|
// pclog("pollgus2 %i %i %i %i %02X\n", gustime, gus->t2on, gus->t2, gus->t2l, gus->tctrl);
|
|
if (gus->t2on)
|
|
{
|
|
gus->t2++;
|
|
if (gus->t2 > 0xFF)
|
|
{
|
|
// gus->t2on=0;
|
|
gus->t2=gus->t2l;
|
|
gus->ad_status |= 0x20;
|
|
if (gus->tctrl&8)
|
|
{
|
|
if (gus->irq != -1)
|
|
picint(1 << gus->irq);
|
|
gus->ad_status |= 0x02;
|
|
gus->irqstatus |= 0x08;
|
|
// pclog("GUS T2 IRQ!\n");
|
|
}
|
|
}
|
|
}
|
|
if (gus->irqnext)
|
|
{
|
|
// pclog("Take IRQ\n");
|
|
gus->irqnext=0;
|
|
gus->irqstatus|=0x80;
|
|
if (gus->irq != -1)
|
|
picint(1 << gus->irq);
|
|
}
|
|
}
|
|
|
|
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->samp_timer += gus->samp_latch;
|
|
|
|
gus->out_l = gus->out_r = 0;
|
|
|
|
if ((gus->reset & 3) != 3)
|
|
return;
|
|
//pclog("gus_poll_wave\n");
|
|
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);
|
|
}
|
|
|
|
// pclog("Voice %i : %04X %05X %04X ", d, v, gus->cur[d] >> 9, gus->rcur[d] >> 10);
|
|
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];
|
|
// pclog("%f %04X\n", vol16bit[(gus->rcur[d]>>10) & 4095], v);
|
|
|
|
gus->out_l += v;
|
|
gus->out_r += v;
|
|
|
|
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->rctrl[d]&4))
|
|
{
|
|
if (!(gus->ctrl[d]&8))
|
|
{
|
|
gus->ctrl[d] |= 1;
|
|
gus->cur[d] = (gus->ctrl[d] & 0x40) ? gus->end[d] : gus->start[d];
|
|
}
|
|
else
|
|
{
|
|
if (gus->ctrl[d]&0x10) gus->ctrl[d]^=0x40;
|
|
gus->cur[d] = (gus->ctrl[d] & 0x40) ? (gus->end[d] - diff) : (gus->start[d] + diff);
|
|
}
|
|
}
|
|
if ((gus->ctrl[d] & 0x20) && !gus->waveirqs[d])
|
|
{
|
|
gus->waveirqs[d] = 1;
|
|
update_irqs = 1;
|
|
// pclog("Causing wave IRQ %02X %i\n", gus->ctrl[d], d);
|
|
}
|
|
}
|
|
}
|
|
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->rctrl[d]&4))
|
|
{
|
|
if (!(gus->ctrl[d]&8))
|
|
{
|
|
gus->ctrl[d] |= 1;
|
|
gus->cur[d] = (gus->ctrl[d] & 0x40) ? gus->end[d] : gus->start[d];
|
|
}
|
|
else
|
|
{
|
|
if (gus->ctrl[d]&0x10) gus->ctrl[d]^=0x40;
|
|
gus->cur[d] = (gus->ctrl[d] & 0x40) ? (gus->end[d] - diff) : (gus->start[d] + diff);
|
|
}
|
|
}
|
|
if ((gus->ctrl[d] & 0x20) && !gus->waveirqs[d])
|
|
{
|
|
gus->waveirqs[d] = 1;
|
|
update_irqs = 1;
|
|
// pclog("Causing wave IRQ %02X %i\n", gus->ctrl[d], d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
// pclog("Causing ramp IRQ %02X %i\n",gus->rctrl[d], d);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gus->rcur[d] += gus->rfreq[d];
|
|
// if (d == 1) printf("RCUR+ %i %08X %08X %08X %08X\n",d,gus->rfreq[d],gus->rcur[d],gus->rstart[d],gus->rend[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;
|
|
// pclog("Causing ramp IRQ %02X %i\n",gus->rctrl[d], d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (update_irqs)
|
|
pollgusirqs(gus);
|
|
}
|
|
|
|
void gus_poll(void *p)
|
|
{
|
|
gus_t *gus = (gus_t *)p;
|
|
|
|
if (gus->pos >= SOUNDBUFLEN)
|
|
return;
|
|
|
|
//pclog("gus_poll\n");
|
|
gus->buffer[0][gus->pos] = gus->out_l;
|
|
gus->buffer[1][gus->pos] = gus->out_r;
|
|
|
|
gus->pos++;
|
|
}
|
|
|
|
static void gus_get_buffer(int16_t *buffer, int len, void *p)
|
|
{
|
|
gus_t *gus = (gus_t *)p;
|
|
int c;
|
|
|
|
for (c = 0; c < len * 2; c++)
|
|
{
|
|
buffer[c] += gus->buffer[c & 1][c >> 1];
|
|
}
|
|
|
|
gus->pos = 0;
|
|
}
|
|
|
|
|
|
void *gus_init()
|
|
{
|
|
int c;
|
|
double out = 1.0;
|
|
gus_t *gus = malloc(sizeof(gus_t));
|
|
memset(gus, 0, sizeof(gus_t));
|
|
|
|
gus->ram = malloc(1 << 20);
|
|
memset(gus->ram, 0, 1 << 20);
|
|
|
|
pclog("gus_init\n");
|
|
|
|
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;//(float)c/4095.0;//out;
|
|
out/=1.002709201; /* 0.0235 dB Steps */
|
|
}
|
|
|
|
printf("Top volume %f %f %f %f\n",vol16bit[4095],vol16bit[3800],vol16bit[3000],vol16bit[2048]);
|
|
gus->voices=14;
|
|
|
|
gus->samp_timer = gus->samp_latch = (int)(TIMER_USEC * (1000000.0 / 44100.0));
|
|
|
|
gus->t1l = gus->t2l = 0xff;
|
|
|
|
io_sethandler(0x0240, 0x0010, readgus, NULL, NULL, writegus, NULL, NULL, gus);
|
|
io_sethandler(0x0340, 0x0010, readgus, NULL, NULL, writegus, NULL, NULL, gus);
|
|
io_sethandler(0x0746, 0x0001, readgus, NULL, NULL, writegus, NULL, NULL, gus);
|
|
io_sethandler(0x0388, 0x0002, readgus, NULL, NULL, writegus, NULL, NULL, gus);
|
|
timer_add(gus_poll_wave, &gus->samp_timer, TIMER_ALWAYS_ENABLED, gus);
|
|
timer_add(gus_poll_timer_1, &gus->timer_1, TIMER_ALWAYS_ENABLED, gus);
|
|
timer_add(gus_poll_timer_2, &gus->timer_2, TIMER_ALWAYS_ENABLED, gus);
|
|
|
|
sound_add_handler(gus_poll, gus_get_buffer, 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 = (int)(TIMER_USEC * (1000000.0 / 44100.0));
|
|
else
|
|
gus->samp_latch = (int)(TIMER_USEC * (1000000.0 / gusfreqs[gus->voices - 14]));
|
|
}
|
|
|
|
device_t gus_device =
|
|
{
|
|
"Gravis UltraSound",
|
|
0,
|
|
gus_init,
|
|
gus_close,
|
|
NULL,
|
|
gus_speed_changed,
|
|
NULL,
|
|
NULL
|
|
};
|