pcem/src/sound_sn76489.c
2013-05-27 19:56:33 +01:00

198 lines
6.7 KiB
C

#include <stdlib.h>
#include "ibm.h"
#include "device.h"
#include "io.h"
#include "sound.h"
#include "sound_sn76489.h"
static float volslog[16]=
{
0.00000f,0.59715f,0.75180f,0.94650f,
1.19145f,1.50000f,1.88835f,2.37735f,
2.99295f,3.76785f,4.74345f,5.97165f,
7.51785f,9.46440f,11.9194f,15.0000f
};
typedef struct sn76489_t
{
int stat[4];
int latch[4], count[4];
int freqlo[4], freqhi[4];
int vol[4];
uint32_t shift;
uint8_t noise;
int lasttone;
uint8_t firstdat;
int16_t buffer[SOUNDBUFLEN];
int pos;
} sn76489_t;
#define PSGCONST ((3579545.0 / 64.0) / 48000.0)
void sn76489_poll(void *p)
{
sn76489_t *sn76489 = (sn76489_t *)p;
int c;
int16_t result = 0;
if (sn76489->pos >= SOUNDBUFLEN)
return;
for (c = 1; c < 4; c++)
{
if (sn76489->latch[c] > 256) result += (int16_t) (volslog[sn76489->vol[c]] * sn76489->stat[c]);
else result += (int16_t) (volslog[sn76489->vol[c]] * 127);
sn76489->count[c] -= (256 * PSGCONST);
while ((int)sn76489->count[c] < 0 && sn76489->latch[c])
{
sn76489->count[c] += sn76489->latch[c];
sn76489->stat[c] = -sn76489->stat[c];
}
}
result += (((sn76489->shift & 1) ^ 1) * 127 * volslog[sn76489->vol[0]] * 2);
sn76489->count[0] -= (512 * PSGCONST);
while ((int)sn76489->count[0] < 0 && sn76489->latch[0])
{
sn76489->count[0] += (sn76489->latch[0] * 2);
if (!(sn76489->noise & 4))
{
if (sn76489->shift & 1)
sn76489->shift |= 0x8000;
sn76489->shift >>= 1;
}
else
{
if ((sn76489->shift & 1) ^ ((sn76489->shift >> 1) & 1))
sn76489->shift |= 0x8000;
sn76489->shift >>= 1;
}
}
sn76489->buffer[sn76489->pos++] = result;
}
void sn76489_get_buffer(int16_t *buffer, int len, void *p)
{
sn76489_t *sn76489 = (sn76489_t *)p;
int c;
for (c = 0; c < len * 2; c++)
buffer[c] += sn76489->buffer[c >> 1];
sn76489->pos = 0;
}
void sn76489_write(uint16_t addr, uint8_t data, void *p)
{
sn76489_t *sn76489 = (sn76489_t *)p;
int freq;
if (data & 0x80)
{
sn76489->firstdat = data;
switch (data & 0x70)
{
case 0:
sn76489->freqlo[3] = data & 0xf;
sn76489->latch[3] = (sn76489->freqlo[3] | (sn76489->freqhi[3] << 4)) << 6;
sn76489->lasttone = 3;
break;
case 0x10:
data &= 0xf;
sn76489->vol[3] = 0xf - data;
break;
case 0x20:
sn76489->freqlo[2] = data & 0xf;
sn76489->latch[2] = (sn76489->freqlo[2] | (sn76489->freqhi[2] << 4)) << 6;
sn76489->lasttone = 2;
break;
case 0x30:
data &= 0xf;
sn76489->vol[2] = 0xf - data;
break;
case 0x40:
sn76489->freqlo[1] = data & 0xf;
sn76489->latch[1] = (sn76489->freqlo[1] | (sn76489->freqhi[1] << 4)) << 6;
sn76489->lasttone = 1;
break;
case 0x50:
data &= 0xf;
sn76489->vol[1] = 0xf - data;
break;
case 0x60:
sn76489->shift = 0x4000;
if ((data & 3) != (sn76489->noise & 3)) sn76489->count[0] = 0;
sn76489->noise = data & 0xf;
if ((data & 3) == 3) sn76489->latch[0] = sn76489->latch[1];
else sn76489->latch[0] = 0x400 << (data & 3);
break;
case 0x70:
data &= 0xf;
sn76489->vol[0] = 0xf - data;
break;
}
}
else
{
if ((sn76489->firstdat & 0x70) == 0x60)
{
sn76489->shift = 0x4000;
if ((data & 3) != (sn76489->noise & 3))
sn76489->count[0] = 0;
sn76489->noise = data & 0xf;
if ((data & 3) == 3) sn76489->latch[0] = sn76489->latch[1];
else sn76489->latch[0] = 0x400 << (data & 3);
}
else
{
sn76489->freqhi[sn76489->lasttone] = data & 0x3F;
freq = sn76489->freqlo[sn76489->lasttone] | (sn76489->freqhi[sn76489->lasttone] << 4);
if ((sn76489->noise & 3) == 3 && sn76489->lasttone == 1)
sn76489->latch[0] = freq << 6;
sn76489->latch[sn76489->lasttone] = freq << 6;
}
}
}
void *sn76489_init()
{
sn76489_t *sn76489 = malloc(sizeof(sn76489_t));
memset(sn76489, 0, sizeof(sn76489_t));
io_sethandler(0x00C0, 0x0001, NULL, NULL, NULL, sn76489_write, NULL, NULL, sn76489);
sound_add_handler(sn76489_poll, sn76489_get_buffer, sn76489);
sn76489->latch[0] = sn76489->latch[1] = sn76489->latch[2] = sn76489->latch[3] = 0x3FF << 6;
sn76489->vol[0] = 0;
sn76489->vol[1] = sn76489->vol[2] = sn76489->vol[3] = 8;
sn76489->stat[0] = sn76489->stat[1] = sn76489->stat[2] = sn76489->stat[3] = 127;
srand(time(NULL));
sn76489->count[0] = 0;
sn76489->count[1] = (rand()&0x3FF)<<6;
sn76489->count[2] = (rand()&0x3FF)<<6;
sn76489->count[3] = (rand()&0x3FF)<<6;
sn76489->noise = 3;
sn76489->shift = 0x4000;
return sn76489;
}
void sn76489_close(void *p)
{
sn76489_t *sn76489 = (sn76489_t *)p;
free(sn76489);
}
device_t sn76489_device =
{
"TI SN74689 PSG",
sn76489_init,
sn76489_close,
NULL
};