Split off AD1848 CODEC from main Windows Sound System emulation.

This commit is contained in:
TomW 2013-11-05 19:59:35 +00:00
commit c2d2e64a11
3 changed files with 249 additions and 196 deletions

204
src/sound_ad1848.c Normal file
View file

@ -0,0 +1,204 @@
/*PCem v0.8 by Tom Walker
AD1848 CODEC emulation (Windows Sound System compatible)*/
#include "ibm.h"
#include "sound_ad1848.h"
static int ad1848_vols[64];
void ad1848_setirq(ad1848_t *ad1848, int irq)
{
ad1848->irq = irq;
}
void ad1848_setdma(ad1848_t *ad1848, int dma)
{
ad1848->dma = dma;
}
uint8_t ad1848_read(uint16_t addr, void *p)
{
ad1848_t *ad1848 = (ad1848_t *)p;
uint8_t temp = 0xff;
// pclog("ad1848_read - addr %04X %04X(%08X):%08X ", addr, CS, cs, pc);
switch (addr & 3)
{
case 0: /*Index*/
temp = ad1848->index | ad1848->trd | ad1848->mce;
break;
case 1:
temp = ad1848->regs[ad1848->index];
break;
case 2:
temp = ad1848->status;
break;
}
// pclog("return %02X\n", temp);
return temp;
}
void ad1848_write(uint16_t addr, uint8_t val, void *p)
{
ad1848_t *ad1848 = (ad1848_t *)p;
double freq;
// pclog("ad1848_write - addr %04X val %02X %04X(%08X):%08X\n", addr, val, CS, cs, pc);
switch (addr & 3)
{
case 0: /*Index*/
ad1848->index = val & 0xf;
ad1848->trd = val & 0x20;
ad1848->mce = val & 0x40;
break;
case 1:
switch (ad1848->index)
{
case 8:
freq = (val & 1) ? 16934400 : 24576000;
switch ((val >> 1) & 7)
{
case 0: freq /= 3072; break;
case 1: freq /= 1536; break;
case 2: freq /= 896; break;
case 3: freq /= 768; break;
case 4: freq /= 448; break;
case 5: freq /= 384; break;
case 6: freq /= 512; break;
case 7: freq /= 2560; break;
}
ad1848->inc = (int)((freq * 65536) / 48000);
break;
case 9:
if (!ad1848->enable)
ad1848->interp_count = 0;
ad1848->enable = ((val & 0x41) == 0x01);
break;
case 12:
return;
case 14:
ad1848->count = ad1848->regs[15] | (val << 8);
break;
}
ad1848->regs[ad1848->index] = val;
break;
case 2:
ad1848->status &= 0xfe;
break;
}
}
void ad1848_poll(void *p, int16_t *l, int16_t *r)
{
ad1848_t *ad1848 = (ad1848_t *)p;
// opl3_poll(&ad1848->opl, &ad1848->opl_buffer[ad1848->pos * 2], &ad1848->opl_buffer[(ad1848->pos * 2) + 1]);
if (ad1848->enable)
{
int32_t temp;
if (ad1848->regs[6] & 0x80)
*l = 0;
else
*l = (ad1848->out_l * ad1848_vols[ad1848->regs[6] & 0x3f]) >> 16;
if (ad1848->regs[7] & 0x80)
*r = 0;
else
*r = (ad1848->out_r * ad1848_vols[ad1848->regs[7] & 0x3f]) >> 16;
ad1848->interp_count += ad1848->inc;
if (ad1848->interp_count >= 0x10000)
{
ad1848->interp_count -= 0x10000;
if (ad1848->count < 0)
{
ad1848->count = ad1848->regs[15] | (ad1848->regs[14] << 8);
if (!(ad1848->status & 0x01))
{
ad1848->status |= 0x01;
if (ad1848->regs[0xa] & 2)
picint(1 << ad1848->irq);
}
}
switch (ad1848->regs[8] & 0x70)
{
case 0x00: /*Mono, 8-bit PCM*/
ad1848->out_l = ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) * 256;
break;
case 0x10: /*Stereo, 8-bit PCM*/
ad1848->out_l = (dma_channel_read(ad1848->dma) ^ 0x80) * 256;
ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) * 256;
break;
case 0x40: /*Mono, 16-bit PCM*/
temp = dma_channel_read(ad1848->dma);
ad1848->out_l = ad1848->out_r = dma_channel_read(ad1848->dma) | (temp << 8);
break;
case 0x50: /*Stereo, 16-bit PCM*/
temp = dma_channel_read(ad1848->dma);
ad1848->out_l = dma_channel_read(ad1848->dma) | (temp << 8);
temp = dma_channel_read(ad1848->dma);
ad1848->out_r = dma_channel_read(ad1848->dma) | (temp << 8);
break;
}
ad1848->count--;
}
// pclog("ad1848_poll : enable %X %X %X %X %X %X\n", ad1848->pcm_buffer[0][ad1848->pos], ad1848->pcm_buffer[1][ad1848->pos], ad1848->out_l[0], ad1848->out_r[0], ad1848->out_l[1], ad1848->out_r[1]);
}
else
{
*l = *r = 0;
// pclog("ad1848_poll : not enable\n");
}
}
void ad1848_init(ad1848_t *ad1848)
{
int c;
double attenuation;
ad1848->enable = 0;
ad1848->status = 0xcc;
ad1848->index = ad1848->trd = 0;
ad1848->mce = 0x40;
ad1848->regs[0] = ad1848->regs[1] = 0;
ad1848->regs[2] = ad1848->regs[3] = 0x80;
ad1848->regs[4] = ad1848->regs[5] = 0x80;
ad1848->regs[6] = ad1848->regs[7] = 0x80;
ad1848->regs[8] = 0;
ad1848->regs[9] = 0x08;
ad1848->regs[10] = ad1848->regs[11] = 0;
ad1848->regs[12] = 0xa;
ad1848->regs[13] = 0;
ad1848->regs[14] = ad1848->regs[15] = 0;
ad1848->out_l = 0;
ad1848->out_r = 0;
ad1848->interp_count = 0;
for (c = 0; c < 64; c++)
{
attenuation = 0.0;
if (c & 0x01) attenuation -= 1.5;
if (c & 0x02) attenuation -= 3.0;
if (c & 0x04) attenuation -= 6.0;
if (c & 0x08) attenuation -= 12.0;
if (c & 0x10) attenuation -= 24.0;
if (c & 0x20) attenuation -= 48.0;
attenuation = pow(10, attenuation / 10);
ad1848_vols[c] = (int)(attenuation * 65536);
// pclog("ad1848_vols %i = %f %i\n", c, attenuation, ad1848_vols[c]);
}
}

27
src/sound_ad1848.h Normal file
View file

@ -0,0 +1,27 @@
typedef struct ad1848_t
{
int index;
uint8_t regs[16];
uint8_t status;
int trd;
int mce;
int count;
int16_t out_l, out_r;
int interp_count, inc;
int enable;
int irq, dma;
} ad1848_t;
void ad1848_setirq(ad1848_t *ad1848, int irq);
void ad1848_setdma(ad1848_t *ad1848, int dma);
uint8_t ad1848_read(uint16_t addr, void *p);
void ad1848_write(uint16_t addr, uint8_t val, void *p);
void ad1848_poll(void *p, int16_t *l, int16_t *r);

View file

@ -1,6 +1,6 @@
/*PCem v0.8 by Tom Walker
Windows Sound System compatible CODEC emulation (AD1848)*/
Windows Sound System emulation*/
#include <math.h>
#include <stdlib.h>
@ -10,6 +10,7 @@
#include "dma.h"
#include "io.h"
#include "pic.h"
#include "sound_ad1848.h"
#include "sound_opl.h"
#include "sound_wss.h"
@ -31,24 +32,8 @@ static uint16_t wss_addr[4] = {0x530, 0x604, 0xe80, 0xf40};
typedef struct wss_t
{
uint8_t config;
int index;
uint8_t regs[16];
uint8_t status;
int trd;
int mce;
int count;
int16_t out_l, out_r;
int interp_count, inc;
int enable;
int irq, dma;
ad1848_t ad1848;
opl_t opl;
int16_t opl_buffer[SOUNDBUFLEN * 2];
@ -57,29 +42,12 @@ typedef struct wss_t
int pos;
} wss_t;
static int wss_vols[64];
uint8_t wss_read(uint16_t addr, void *p)
{
wss_t *wss = (wss_t *)p;
uint8_t temp = 0xff;
uint8_t temp;
// pclog("wss_read - addr %04X %04X(%08X):%08X ", addr, CS, cs, pc);
switch (addr & 7)
{
case 0: case 1: case 2: case 3: /*Version*/
temp = 4 | (wss->config & 0x40);
break;
case 4: /*Index*/
temp = wss->index | wss->trd | wss->mce;
break;
case 5:
temp = wss->regs[wss->index];
break;
case 6:
temp = wss->status;
break;
}
temp = 4 | (wss->config & 0x40);
// pclog("return %02X\n", temp);
return temp;
}
@ -87,60 +55,11 @@ uint8_t wss_read(uint16_t addr, void *p)
void wss_write(uint16_t addr, uint8_t val, void *p)
{
wss_t *wss = (wss_t *)p;
double freq;
pclog("wss_write - addr %04X val %02X %04X(%08X):%08X\n", addr, val, CS, cs, pc);
switch (addr & 7)
{
case 0: case 1: case 2: case 3: /*Config*/
wss->config = val;
wss->dma = wss_dma[val & 3];
wss->irq = wss_irq[(val >> 3) & 7];
break;
case 4: /*Index*/
wss->index = val & 0xf;
wss->trd = val & 0x20;
wss->mce = val & 0x40;
break;
case 5:
switch (wss->index)
{
case 8:
freq = (val & 1) ? 16934400 : 24576000;
switch ((val >> 1) & 7)
{
case 0: freq /= 3072; break;
case 1: freq /= 1536; break;
case 2: freq /= 896; break;
case 3: freq /= 768; break;
case 4: freq /= 448; break;
case 5: freq /= 384; break;
case 6: freq /= 512; break;
case 7: freq /= 2560; break;
}
wss->inc = (int)((freq * 65536) / 48000);
break;
case 9:
if (!wss->enable)
wss->interp_count = 0;
wss->enable = ((val & 0x41) == 0x01);
break;
case 12:
return;
case 14:
wss->count = wss->regs[15] | (val << 8);
break;
}
wss->regs[wss->index] = val;
break;
case 6:
wss->status &= 0xfe;
break;
}
// pclog("wss_write - addr %04X val %02X %04X(%08X):%08X\n", addr, val, CS, cs, pc);
wss->config = val;
ad1848_setdma(&wss->ad1848, wss_dma[val & 3]);
ad1848_setirq(&wss->ad1848, wss_irq[(val >> 3) & 7]);
}
static void wss_poll(void *p)
@ -151,68 +70,8 @@ static void wss_poll(void *p)
return;
opl3_poll(&wss->opl, &wss->opl_buffer[wss->pos * 2], &wss->opl_buffer[(wss->pos * 2) + 1]);
if (wss->enable)
{
int32_t temp;
if (wss->regs[6] & 0x80)
wss->pcm_buffer[1][wss->pos] = 0;
else
wss->pcm_buffer[1][wss->pos] = (wss->out_l * wss_vols[wss->regs[6] & 0x3f]) >> 16;
ad1848_poll(&wss->ad1848, &wss->pcm_buffer[0][wss->pos], &wss->pcm_buffer[1][wss->pos]);
if (wss->regs[7] & 0x80)
wss->pcm_buffer[0][wss->pos] = 0;
else
wss->pcm_buffer[0][wss->pos] = (wss->out_r * wss_vols[wss->regs[7] & 0x3f]) >> 16;
wss->interp_count += wss->inc;
if (wss->interp_count >= 0x10000)
{
wss->interp_count -= 0x10000;
if (wss->count < 0)
{
wss->count = wss->regs[15] | (wss->regs[14] << 8);
if (!(wss->status & 0x01))
{
wss->status |= 0x01;
if (wss->regs[0xa] & 2)
picint(1 << wss->irq);
}
}
switch (wss->regs[8] & 0x70)
{
case 0x00: /*Mono, 8-bit PCM*/
wss->out_l = wss->out_r = (dma_channel_read(wss->dma) ^ 0x80) * 256;
break;
case 0x10: /*Stereo, 8-bit PCM*/
wss->out_l = (dma_channel_read(wss->dma) ^ 0x80) * 256;
wss->out_r = (dma_channel_read(wss->dma) ^ 0x80) * 256;
break;
case 0x40: /*Mono, 16-bit PCM*/
temp = dma_channel_read(wss->dma);
wss->out_l = wss->out_r = dma_channel_read(wss->dma) | (temp << 8);
break;
case 0x50: /*Stereo, 16-bit PCM*/
temp = dma_channel_read(wss->dma);
wss->out_l = dma_channel_read(wss->dma) | (temp << 8);
temp = dma_channel_read(wss->dma);
wss->out_r = dma_channel_read(wss->dma) | (temp << 8);
break;
}
wss->count--;
}
// pclog("wss_poll : enable %X %X %X %X %X %X\n", wss->pcm_buffer[0][wss->pos], wss->pcm_buffer[1][wss->pos], wss->out_l[0], wss->out_r[0], wss->out_l[1], wss->out_r[1]);
}
else
{
wss->pcm_buffer[0][wss->pos] = wss->pcm_buffer[1][wss->pos] = 0;
// pclog("wss_poll : not enable\n");
}
wss->pos++;
}
@ -240,54 +99,17 @@ void *wss_init()
memset(wss, 0, sizeof(wss_t));
opl3_init(&wss->opl);
ad1848_init(&wss->ad1848);
pclog("wss_init - %i %i\n", sbtype, SND_WSS);
ad1848_setirq(&wss->ad1848, 7);
ad1848_setdma(&wss->ad1848, 3);
wss->enable = 0;
wss->status = 0xcc;
wss->index = wss->trd = 0;
wss->mce = 0x40;
wss->regs[0] = wss->regs[1] = 0;
wss->regs[2] = wss->regs[3] = 0x80;
wss->regs[4] = wss->regs[5] = 0x80;
wss->regs[6] = wss->regs[7] = 0x80;
wss->regs[8] = 0;
wss->regs[9] = 0x08;
wss->regs[10] = wss->regs[11] = 0;
wss->regs[12] = 0xa;
wss->regs[13] = 0;
wss->regs[14] = wss->regs[15] = 0;
wss->out_l = 0;
wss->out_r = 0;
wss->interp_count = 0;
wss->irq = 7;
wss->dma = 3;
io_sethandler(0x0388, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &wss->opl);
io_sethandler(0x0530, 0x0008, wss_read, NULL, NULL, wss_write, NULL, NULL, wss);
io_sethandler(0x0388, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &wss->opl);
io_sethandler(0x0530, 0x0004, wss_read, NULL, NULL, wss_write, NULL, NULL, wss);
io_sethandler(0x0534, 0x0004, ad1848_read, NULL, NULL, ad1848_write, NULL, NULL, &wss->ad1848);
sound_add_handler(wss_poll, wss_get_buffer, wss);
for (c = 0; c < 64; c++)
{
attenuation = 0.0;
if (c & 0x01) attenuation -= 1.5;
if (c & 0x02) attenuation -= 3.0;
if (c & 0x04) attenuation -= 6.0;
if (c & 0x08) attenuation -= 12.0;
if (c & 0x10) attenuation -= 24.0;
if (c & 0x20) attenuation -= 48.0;
attenuation = pow(10, attenuation / 10);
wss_vols[c] = (int)(attenuation * 65536);
pclog("wss_vols %i = %f %i\n", c, attenuation, wss_vols[c]);
}
return wss;
}