Initial implementation of C-Media CMI8x38

This commit is contained in:
RichardG867
2022-02-25 19:22:45 -03:00
parent 09066358c4
commit b07570696f
9 changed files with 905 additions and 12 deletions

View File

@@ -155,6 +155,8 @@ extern const device_t mpu401_mca_device;
extern uint8_t MPU401_ReadData(mpu_t *mpu);
extern void mpu401_write(uint16_t addr, uint8_t val, void *priv);
extern uint8_t mpu401_read(uint16_t addr, void *priv);
extern void mpu401_setirq(mpu_t *mpu, int irq);
extern void mpu401_change_addr(mpu_t *mpu, uint16_t addr);
extern void mpu401_init(mpu_t *mpu, uint16_t addr, int irq, int mode, int receive_input);

View File

@@ -109,6 +109,8 @@ typedef struct sb_ct1745_mixer_t
uint8_t index;
uint8_t regs[256];
int output_filter; /* for clones */
} sb_ct1745_mixer_t;
typedef struct sb_t
@@ -137,8 +139,13 @@ extern void sb_ct1345_mixer_write(uint16_t addr, uint8_t val, void *p);
extern uint8_t sb_ct1345_mixer_read(uint16_t addr, void *p);
extern void sb_ct1345_mixer_reset(sb_t* sb);
extern void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *p);
extern uint8_t sb_ct1745_mixer_read(uint16_t addr, void *p);
extern void sb_ct1745_mixer_reset(sb_t* sb);
extern void sb_get_buffer_sbpro(int32_t *buffer, int len, void *p);
extern void sbpro_filter_cd_audio(int channel, double *buffer, void *p);
extern void sb16_awe32_filter_cd_audio(int channel, double *buffer, void *p);
extern void sb_close(void *p);
extern void sb_speed_changed(void *p);

View File

@@ -120,6 +120,7 @@ extern const device_t sb_pro_mcv_device;
extern const device_t sb_pro_compat_device;
extern const device_t sb_16_device;
extern const device_t sb_16_pnp_device;
extern const device_t sb_16_compat_device;
extern const device_t sb_32_pnp_device;
extern const device_t sb_awe32_device;
extern const device_t sb_awe32_pnp_device;
@@ -140,6 +141,10 @@ extern const device_t cs4235_onboard_device;
extern const device_t cs4236b_device;
extern const device_t cs4237b_device;
extern const device_t cs4238b_device;
/* C-Media CMI8x38 */
extern const device_t cmi8338_device;
extern const device_t cmi8738_device;
#endif
#endif /*EMU_SOUND_H*/

View File

@@ -16,7 +16,7 @@
add_library(snd OBJECT sound.c snd_opl.c snd_opl_nuked.c snd_resid.cc
midi.c midi_rtmidi.cpp snd_speaker.c snd_pssj.c snd_lpt_dac.c snd_ac97_codec.c snd_ac97_via.c
snd_lpt_dss.c snd_ps1.c snd_adlib.c snd_adlibgold.c snd_ad1848.c snd_audiopci.c
snd_azt2316a.c snd_cms.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c
snd_azt2316a.c snd_cms.c snd_cmi8x38.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c
snd_emu8k.c snd_mpu401.c snd_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c)
if(OPENAL)

830
src/sound/snd_cmi8x38.c Normal file
View File

@@ -0,0 +1,830 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* C-Media CMI8x38 PCI audio controller emulation.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2022 RichardG.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/mem.h>
#include <86box/pic.h>
#include <86box/timer.h>
#include <86box/pci.h>
#include <86box/sound.h>
#include <86box/snd_sb.h>
enum {
CMEDIA_CMI8338 = 0x00,
CMEDIA_CMI8738 = 0x11
};
typedef struct {
uint8_t id, reg, always_run, playback_enabled;
struct _cmi8x38_ *dev;
uint32_t sample_ptr, fifo_pos, fifo_end;
int32_t frame_count_dma, frame_count_fragment, sample_count_out;
uint8_t fifo[32], restart;
int16_t out_l, out_r;
int vol_l, vol_r, pos;
int32_t buffer[SOUNDBUFLEN * 2];
uint64_t timer_latch;
pc_timer_t dma_timer, poll_timer;
} cmi8x38_dma_t;
typedef struct _cmi8x38_ {
uint16_t io_base;
uint8_t type, pci_regs[256], io_regs[256], mixer_ext_regs[16];
int slot;
sb_t *sb;
cmi8x38_dma_t dma[2];
int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r;
} cmi8x38_t;
#define ENABLE_CMI8X38_LOG 1
#ifdef ENABLE_CMI8X38_LOG
int cmi8x38_do_log = ENABLE_CMI8X38_LOG;
static void
cmi8x38_log(const char *fmt, ...)
{
va_list ap;
if (cmi8x38_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define cmi8x38_log(fmt, ...)
#endif
static const double freqs[] = {5512.0, 11025.0, 22050.0, 44100.0, 8000.0, 16000.0, 32000.0, 48000.0};
static void cmi8x38_dma_process(void *priv);
static void cmi8x38_speed_changed(void *priv);
static void
cmi8x38_update_irqs(cmi8x38_t *dev)
{
/* Calculate and use the any interrupt flag. */
if (*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) {
dev->io_regs[0x13] |= 0x80;
pci_set_irq(dev->slot, PCI_INTA);
cmi8x38_log("CMI8x38: Raising IRQ\n");
} else {
dev->io_regs[0x13] &= ~0x80;
pci_clear_irq(dev->slot, PCI_INTA);
}
}
static void
cmi8x38_start_playback(cmi8x38_t *dev, uint8_t val)
{
uint8_t i;
i = !(val & 0x01);
if (!dev->dma[0].playback_enabled && i)
timer_advance_u64(&dev->dma[0].poll_timer, dev->dma[0].timer_latch);
dev->dma[0].playback_enabled = i;
i = !(val & 0x02);
if (!dev->dma[1].playback_enabled && i)
timer_advance_u64(&dev->dma[1].poll_timer, dev->dma[1].timer_latch);
dev->dma[1].playback_enabled = i;
}
static uint8_t
cmi8x38_read(uint16_t addr, void *priv)
{
cmi8x38_t *dev = (cmi8x38_t *) priv;
addr &= 0xff;
uint8_t ret;
switch (addr) {
case 0x22:
sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16;
if (mixer->index >= 0xf0)
ret = dev->mixer_ext_regs[mixer->index & 0x0f];
else
ret = sb_ct1745_mixer_read(1, dev->sb);
break;
case 0x23:
ret = sb_ct1745_mixer_read(0, dev->sb);
break;
case 0x40 ... 0x4f:
if (dev->type == CMEDIA_CMI8338)
goto io_reg;
else
ret = mpu401_read(addr, val, dev->sb->mpu);
break;
case 0x50 ... 0x5f:
if (dev->type == CMEDIA_CMI8338)
goto io_reg;
else
ret = opl3_read(addr, &dev->sb->opl);
break;
case 0x80: case 0x88:
ret = dev->dma[(addr & 0x78) >> 3].sample_ptr;
break;
case 0x81: case 0x89:
ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 8;
break;
case 0x82: case 0x8a:
ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 16;
break;
case 0x83: case 0x8b:
ret = dev->dma[(addr & 0x78) >> 3].sample_ptr >> 24;
break;
case 0x84: case 0x8c:
ret = dev->dma[(addr & 0x78) >> 3].frame_count_dma;
break;
case 0x85: case 0x8d:
ret = dev->dma[(addr & 0x78) >> 3].frame_count_dma >> 8;
break;
case 0x86: case 0x8e:
ret = dev->dma[(addr & 0x78) >> 3].sample_count_out >> 2;
break;
case 0x87: case 0x8f:
ret = dev->dma[(addr & 0x78) >> 3].sample_count_out >> 10;
break;
default:
io_reg: ret = dev->io_regs[addr];
break;
}
cmi8x38_log("CMI8x38: read(%02X) = %02X\n", addr, ret);
return ret;
}
static void
cmi8x38_write(uint16_t addr, uint8_t val, void *priv)
{
cmi8x38_t *dev = (cmi8x38_t *) priv;
addr &= 0xff;
cmi8x38_log("CMI8x38: write(%02X, %02X)\n", addr, val);
switch (addr) {
case 0x00:
val &= 0x0f;
/* Don't care about recording DMA. */
dev->dma[0].always_run = val & 0x01;
dev->dma[1].always_run = val & 0x02;
/* Start playback if requested. */
cmi8x38_start_playback(dev, val);
break;
case 0x02:
/* Reset DMA channels if requested. */
if (val & 0x04)
val &= ~0x01;
if (val & 0x08)
val &= ~0x02;
val &= 0x03;
dev->io_regs[addr] = val;
/* Start DMA channels if requested. */
if (val & 0x01) {
cmi8x38_log("CMI8x38: DMA 0 trigger\n");
dev->dma[0].restart = 1;
cmi8x38_dma_process(&dev->dma[0]);
}
if (val & 0x02) {
cmi8x38_log("CMI8x38: DMA 1 trigger\n");
dev->dma[1].restart = 1;
cmi8x38_dma_process(&dev->dma[1]);
}
/* Start playback along with DMA channels. */
if (val & 0x03)
cmi8x38_start_playback(dev, dev->io_regs[0x00]);
break;
case 0x05:
dev->io_regs[addr] = val;
cmi8x38_speed_changed(dev);
break;
case 0x08:
if (dev->type == CMEDIA_CMI8338)
val &= 0x0f;
break;
case 0x09:
#if 0 /* actual CMI8338 behavior unconfirmed; this register is required for the Windows XP driver which outputs 96K */
if (dev->type == CMEDIA_CMI8338)
return;
#endif
cmi8x38_speed_changed(dev);
break;
case 0x0a: case 0x0b:
if (dev->type == CMEDIA_CMI8338)
return;
else
val &= 0xe0;
if (addr == 0x0a)
dev->pci_regs[0x0d] = (val & 0x80) ? 0x48 : 0x20; /* clearing SETLAT48 is undefined */
break;
case 0x0e:
val &= 0x07;
/* Clear interrupts. */
dev->io_regs[0x10] &= val | 0xfc;
if (!(val & 0x04))
dev->io_regs[0x11] &= ~0xc0;
cmi8x38_update_irqs(dev);
break;
case 0x15:
if (dev->type == CMEDIA_CMI8338)
return;
else
val &= 0xf0;
break;
case 0x16:
if (dev->type == CMEDIA_CMI8338)
val &= 0xa0;
break;
case 0x17:
if (dev->type == CMEDIA_CMI8338) {
val &= 0xf3;
/* Force IRQ if requested. Clearing this bit is undefined. */
if (val & 0x10)
pci_set_irq(dev->slot, PCI_INTA);
else if ((dev->io_regs[0x17] & 0x10) && !(val & 0x10))
pci_clear_irq(dev->slot, PCI_INTA);
}
break;
case 0x18:
if (dev->type == CMEDIA_CMI8338)
val &= 0x0f;
else
val &= 0xdf;
break;
case 0x19:
if (dev->type == CMEDIA_CMI8338)
return;
else
val &= 0xe0;
break;
case 0x1a:
val &= 0xfd;
break;
case 0x1b:
if (dev->type == CMEDIA_CMI8338)
val &= 0xf0;
else
val &= 0xd7;
break;
case 0x20:
/* ??? */
break;
case 0x21:
if (dev->type != CMEDIA_CMI8338)
val &= 0x07;
break;
case 0x22:
sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16;
switch (mixer->index) {
case 0xf0:
if (dev->type == CMEDIA_CMI8338)
val &= 0xfe;
dev->mixer_ext_regs[dev->sb->mixer_sb16.index & 0x0f] = val;
break;
case 0xf8 ... 0xff:
if (dev->type == CMEDIA_CMI8338)
dev->mixer_ext_regs[dev->sb->mixer_sb16.index & 0x0f] = val;
/* fall-through */
case 0xf1 ... 0xf7:
break;
default:
sb_ct1745_mixer_write(1, val, dev->sb);
/* Our clone mixer lacks the [3F:47] controls. */
mixer->input_gain_L = 0;
mixer->input_gain_R = 0;
mixer->output_gain_L = (double) 1.0;
mixer->output_gain_R = (double) 1.0;
mixer->bass_l = 8;
mixer->bass_r = 8;
mixer->treble_l = 8;
mixer->treble_r = 8;
break;
}
return;
case 0x23:
sb_ct1745_mixer_write(0, val, dev->sb);
return;
case 0x24:
if (dev->type == CMEDIA_CMI8338)
val &= 0xcf;
break;
case 0x27:
if (dev->type == CMEDIA_CMI8338)
val &= 0x03;
else
val &= 0x27;
break;
case 0x40 ... 0x4f:
if (dev->type == CMEDIA_CMI8338)
break;
else
mpu401_write(addr, val, dev->sb->mpu);
return;
case 0x50 ... 0x5f:
if (dev->type == CMEDIA_CMI8338)
break;
else
opl3_write(addr, val, &dev->sb->opl);
return;
case 0x92:
if (dev->type == CMEDIA_CMI8338)
return;
else
val &= 0x1f;
break;
case 0x93:
if (dev->type == CMEDIA_CMI8338)
return;
else
val &= 0x10;
break;
case 0x04:
case 0x25: case 0x26:
case 0x70: case 0x71:
case 0x80 ... 0x8f:
break;
default:
return;
}
dev->io_regs[addr] = val;
}
static void
cmi8x38_remap(cmi8x38_t *dev, uint8_t io_msb, uint8_t enable)
{
if (dev->io_base)
io_removehandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev);
if (enable & 0x01)
dev->io_base = io_msb << 8;
else
dev->io_base = 0;
cmi8x38_log("CMI8x38: remap(%04X)\n", dev->io_base);
if (dev->io_base)
io_sethandler(dev->io_base, 256, cmi8x38_read, NULL, NULL, cmi8x38_write, NULL, NULL, dev);
}
static uint8_t
cmi8x38_pci_read(int func, int addr, void *priv)
{
cmi8x38_t *dev = (cmi8x38_t *) priv;
uint8_t ret = 0xff;
if (!func) {
ret = dev->pci_regs[addr];
cmi8x38_log("CMI8x38: pci_read(%02X) = %02X\n", addr, ret);
}
return ret;
}
static void
cmi8x38_pci_write(int func, int addr, uint8_t val, void *priv)
{
cmi8x38_t *dev = (cmi8x38_t *) priv;
if (func)
return;
cmi8x38_log("CMI8x38: pci_write(%02X, %02X)\n", addr, val);
switch (addr) {
case 0x04:
val &= 0x05;
cmi8x38_remap(dev, dev->pci_regs[0x11], val);
break;
case 0x05:
val &= 0x01;
break;
case 0x11:
cmi8x38_remap(dev, val, dev->pci_regs[0x04]);
break;
case 0x2c: case 0x2d: case 0x2e: case 0x2f:
if (!(dev->io_regs[0x1a] & 0x01))
return;
break;
case 0x40:
if (dev->type == CMEDIA_CMI8338)
val &= 0x0f;
else
return;
break;
case 0x0c: case 0x0d:
case 0x3c:
break;
default:
return;
}
dev->pci_regs[addr] = val;
}
static void
cmi8x38_update(cmi8x38_t *dev, cmi8x38_dma_t *dma)
{
sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16;
int32_t l = (dma->out_l * mixer->voice_l) * mixer->master_l,
r = (dma->out_r * mixer->voice_r) * mixer->master_r;
for (; dma->pos < sound_pos_global; dma->pos++) {
dma->buffer[dma->pos*2] = l;
dma->buffer[dma->pos*2 + 1] = r;
}
}
static void
cmi8x38_dma_process(void *priv)
{
cmi8x38_dma_t *dma = (cmi8x38_dma_t *) priv;
cmi8x38_t *dev = dma->dev;
/* Stop if this DMA channel is not active. */
uint8_t dma_bit = 0x01 << dma->id;
if (!(dev->io_regs[0x02] & dma_bit)) {
cmi8x38_log("CMI8x38: Stopping DMA %d due to inactive channel (%02X)\n", dma->id, dev->io_regs[0x02]);
return;
}
/* Schedule next run. */
timer_on_auto(&dma->dma_timer, 10.0);
/* Process DMA if it's active, and the FIFO has room or is disabled. */
uint8_t dma_status = dev->io_regs[0x00] >> dma->id;
if (!(dma_status & 0x04) && (dma->always_run || ((dma->fifo_end - dma->fifo_pos) <= (sizeof(dma->fifo) - 4)))) {
/* Start DMA if requested. */
if (dma->restart) {
/* Set up base address and counters.
I have no idea how sample_count_out is supposed to work,
nothing consumes it, so it's implemented as an assumption. */
dma->restart = 0;
dma->sample_ptr = *((uint32_t *) &dev->io_regs[dma->reg]);
dma->frame_count_dma = dma->sample_count_out = *((uint16_t *) &dev->io_regs[dma->reg | 0x4]);
dma->frame_count_fragment = *((uint16_t *) &dev->io_regs[dma->reg | 0x6]);
cmi8x38_log("CMI8x38: Starting DMA %d at %08X\n", dma->id, dma->sample_ptr);
}
if (dma_status & 0x01) {
/* Write channel: read data from FIFO. */
mem_writel_phys(dma->sample_ptr, *((uint32_t *) &dma->fifo[dma->fifo_end & (sizeof(dma->fifo) - 1)]));
} else {
/* Read channel: write data to FIFO. */
*((uint32_t *) &dma->fifo[dma->fifo_end & (sizeof(dma->fifo) - 1)]) = mem_readl_phys(dma->sample_ptr);
}
dma->fifo_end += 4;
dma->sample_ptr += 4;
/* Check if the fragment size was reached. */
if (--dma->frame_count_fragment <= 0) {
cmi8x38_log("CMI8x38: DMA %d fragment size reached at %04X frames left", dma->id, dma->frame_count_dma - 1);
/* Reset fragment counter. */
dma->frame_count_fragment = *((uint16_t *) &dev->io_regs[dma->reg | 0x6]);
/* Fire interrupt if requested. */
if (dev->io_regs[0x0e] & dma_bit) {
cmi8x38_log(", firing interrupt\n");
/* Set channel interrupt flag. */
dev->io_regs[0x10] |= dma_bit;
/* Fire interrupt. */
cmi8x38_update_irqs(dev);
} else {
cmi8x38_log("\n");
}
}
/* Check if the buffer's end was reached. */
if (--dma->frame_count_dma <= 0) {
cmi8x38_log("CMI8x38: DMA %d end reached, restarting\n", dma->id);
/* Restart DMA on the next run. */
dma->restart = 1;
}
}
}
static void
cmi8x38_poll(void *priv)
{
cmi8x38_dma_t *dma = (cmi8x38_dma_t *) priv;
cmi8x38_t *dev = dma->dev;
/* Schedule next run if playback is enabled. */
if (dev->io_regs[0x00] & (1 << dma->id))
dma->playback_enabled = 0;
else
timer_advance_u64(&dma->poll_timer, dma->timer_latch);
/* Update audio buffer. */
cmi8x38_update(dev, dma);
/* Feed next sample from the FIFO. */
switch ((dev->io_regs[0x08] >> (dma->id << 1)) & 0x03) {
case 0x00: /* Mono, 8-bit PCM */
if ((dma->fifo_end - dma->fifo_pos) >= 1) {
dma->out_l = dma->out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
dma->sample_count_out--;
return;
}
break;
case 0x01: /* Stereo, 8-bit PCM */
if ((dma->fifo_end - dma->fifo_pos) >= 2) {
dma->out_l = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
dma->out_r = (dma->fifo[dma->fifo_pos++ & (sizeof(dma->fifo) - 1)] ^ 0x80) << 8;
dma->sample_count_out -= 2;
return;
}
break;
case 0x02: /* Mono, 16-bit PCM */
if ((dma->fifo_end - dma->fifo_pos) >= 2) {
dma->out_l = dma->out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
dma->fifo_pos += 2;
dma->sample_count_out -= 2;
return;
}
break;
case 0x03: /* Stereo, 16-bit PCM */
if ((dma->fifo_end - dma->fifo_pos) >= 4) {
dma->out_l = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
dma->fifo_pos += 2;
dma->out_r = *((uint16_t *) &dma->fifo[dma->fifo_pos & (sizeof(dma->fifo) - 1)]);
dma->fifo_pos += 2;
dma->sample_count_out -= 4;
return;
}
break;
}
/* Feed silence if the FIFO is empty. */
dma->out_l = dma->out_r = 0;
}
static void
cmi8x38_get_buffer(int32_t *buffer, int len, void *priv)
{
cmi8x38_t *dev = (cmi8x38_t *) priv;
/* Update wave playback channels. */
cmi8x38_update(dev, &dev->dma[0]);
cmi8x38_update(dev, &dev->dma[1]);
/* Apply wave mute. */
if (!(dev->io_regs[0x24] & 0x40)) {
/* Fill buffer. */
for (int c = 0; c < len * 2; c++) {
buffer[c] += dev->dma[0].buffer[c];
buffer[c] += dev->dma[1].buffer[c];
}
}
dev->dma[0].pos = dev->dma[1].pos = 0;
}
static void
cmi8x38_speed_changed(void *priv)
{
cmi8x38_t *dev = (cmi8x38_t *) priv;
double freq;
uint8_t dsr = dev->io_regs[0x09], freqreg = dev->io_regs[0x05] >> 2;
/* CMI8338 claims the frequency controls are for DAC (playback) and ADC (recording)
respectively, while CMI8738 claims they're for channel 0 and channel 1. The Linux
driver just assumes the latter definition, so that's what we're going to use here. */
for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) {
/* More confusion. The Linux driver implies the sample rate doubling
bits take precedence over any configured sample rate. It also
supports 128K with both doubling bits set, which is undocumented. */
switch (dsr & 0x03) {
case 0x01: freq = 88200.0; break;
case 0x02: freq = 96000.0; break;
case 0x03: freq = 128000.0; break;
default: freq = freqs[freqreg & 0x07]; break;
}
dsr >>= 2;
freqreg >>= 3;
/* Set period. */
dev->dma[i].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq));
}
}
static void
cmi8x38_reset(void *priv)
{
cmi8x38_t *dev = (cmi8x38_t *) priv;
/* Reset PCI configuration registers. */
memset(dev->pci_regs, 0, sizeof(dev->pci_regs));
dev->pci_regs[0x00] = 0xf6; dev->pci_regs[0x01] = 0x13;
dev->pci_regs[0x02] = dev->type; dev->pci_regs[0x03] = 0x01;
dev->pci_regs[0x06] = (dev->type == CMEDIA_CMI8338) ? 0x80 : 0x10; dev->pci_regs[0x07] = 0x02;
dev->pci_regs[0x08] = 0x10;
dev->pci_regs[0x0a] = 0x01; dev->pci_regs[0x0b] = 0x04;
dev->pci_regs[0x0d] = 0x20;
dev->pci_regs[0x10] = 0x01;
dev->pci_regs[0x2c] = 0xf6; dev->pci_regs[0x2d] = 0x13;
if (dev->type == CMEDIA_CMI8338) {
dev->pci_regs[0x2e] = 0xff; dev->pci_regs[0x2f] = 0xff;
} else {
dev->pci_regs[0x2e] = dev->type; dev->pci_regs[0x2f] = 0x01;
dev->pci_regs[0x34] = 0x40;
}
dev->pci_regs[0x3d] = 0x01;
dev->pci_regs[0x3e] = 0x02;
dev->pci_regs[0x3f] = 0x18;
/* Reset I/O space registers. */
memset(dev->io_regs, 0, sizeof(dev->io_regs));
if (dev->type == CMEDIA_CMI8738)
dev->io_regs[0x0f] = 0x04; /* chip version 039 with 4-channel support */
/* Reset DMA channels. */
for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) {
dev->dma[i].playback_enabled = 0;
dev->dma[i].fifo_pos = dev->dma[i].fifo_end = 0;
memset(dev->dma[i].fifo, 0, sizeof(dev->dma[i].fifo));
}
/* Reset Sound Blaster 16 mixer. */
sb_ct1745_mixer_reset(dev->sb);
}
static void *
cmi8x38_init(const device_t *info)
{
cmi8x38_t *dev = malloc(sizeof(cmi8x38_t));
memset(dev, 0, sizeof(cmi8x38_t));
/* Set the chip type. */
dev->type = info->local;
cmi8x38_log("CMI8x38: init(%02X)\n", dev->type);
/* Initialize Sound Blaster 16. */
dev->sb = device_add_inst(&sb_16_compat_device, 1);
dev->sb->opl_enabled = 1; /* let snd_sb.c handle the OPL3 */
dev->sb->mixer_sb16.output_filter = 0; /* no output filtering */
/* Initialize DMA channels. */
for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) {
dev->dma[i].id = i;
dev->dma[i].reg = 0x80 + (8 * i);
dev->dma[i].dev = dev;
timer_add(&dev->dma[i].dma_timer, cmi8x38_dma_process, &dev->dma[i], 0);
timer_add(&dev->dma[i].poll_timer, cmi8x38_poll, &dev->dma[i], 0);
}
cmi8x38_speed_changed(dev);
/* Initialize playback handler and CD audio filter. */
sound_add_handler(cmi8x38_get_buffer, dev);
sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, dev->sb);
/* Add PCI card. */
dev->slot = pci_add_card((info->local & 0x100) ? PCI_ADD_SOUND : PCI_ADD_NORMAL, cmi8x38_pci_read, cmi8x38_pci_write, dev);
/* Perform initial reset. */
cmi8x38_reset(dev);
return dev;
}
static void
cmi8x38_close(void *priv)
{
cmi8x38_t *dev = (cmi8x38_t *) priv;
cmi8x38_log("CMI8x38: close()\n");
free(dev);
}
const device_t cmi8338_device =
{
"C-Media CMI8338",
"cmi8338",
DEVICE_PCI,
CMEDIA_CMI8338,
cmi8x38_init, cmi8x38_close, cmi8x38_reset,
{ NULL },
cmi8x38_speed_changed,
NULL,
NULL
};
const device_t cmi8738_device =
{
"C-Media CMI8738",
"cmi8738",
DEVICE_PCI,
CMEDIA_CMI8738,
cmi8x38_init, cmi8x38_close, cmi8x38_reset,
{ NULL },
cmi8x38_speed_changed,
NULL,
NULL
};

View File

@@ -1213,7 +1213,7 @@ MPU401_ReadData(mpu_t *mpu)
}
static void
void
mpu401_write(uint16_t addr, uint8_t val, void *priv)
{
mpu_t *mpu = (mpu_t *)priv;
@@ -1233,7 +1233,7 @@ mpu401_write(uint16_t addr, uint8_t val, void *priv)
}
static uint8_t
uint8_t
mpu401_read(uint16_t addr, void *priv)
{
mpu_t *mpu = (mpu_t *)priv;

View File

@@ -384,9 +384,14 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *p)
in_r = (mixer->input_selector_right & INPUT_MIDI_L) ? ((int32_t) out_l) :
0 + (mixer->input_selector_right & INPUT_MIDI_R) ? ((int32_t) out_r) : 0;
/* We divide by 3 to get the volume down to normal. */
out_l += (low_fir_sb16(0, 0, (double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0;
out_r += (low_fir_sb16(0, 1, (double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0;
if (mixer->output_filter) {
/* We divide by 3 to get the volume down to normal. */
out_l += (low_fir_sb16(0, 0, (double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0;
out_r += (low_fir_sb16(0, 1, (double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0;
} else {
out_l += (((double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.0;
out_r += (((double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0;
}
out_l *= mixer->master_l;
out_r *= mixer->master_r;
@@ -468,7 +473,7 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *p)
}
static void
void
sb16_awe32_filter_cd_audio(int channel, double *buffer, void *p)
{
sb_t *sb = (sb_t *)p;
@@ -481,7 +486,10 @@ sb16_awe32_filter_cd_audio(int channel, double *buffer, void *p)
double bass_treble;
double output_gain = (channel ? mixer->output_gain_R : mixer->output_gain_L);
c = (low_fir_sb16(1, channel, *buffer) * cd) / 3.0;
if (mixer->output_filter)
c = (low_fir_sb16(1, channel, *buffer) * cd) / 3.0;
else
c = ((*buffer) * cd) / 3.0;
c *= master;
/* This is not exactly how one does bass/treble controls, but the end result is like it.
@@ -691,7 +699,7 @@ sb_ct1345_mixer_reset(sb_t* sb)
}
static void
void
sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *p)
{
sb_t *sb = (sb_t *) p;
@@ -862,7 +870,7 @@ sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *p)
}
static uint8_t
uint8_t
sb_ct1745_mixer_read(uint16_t addr, void *p)
{
sb_t *sb = (sb_t *) p;
@@ -1013,7 +1021,7 @@ sb_ct1745_mixer_read(uint16_t addr, void *p)
}
static void
void
sb_ct1745_mixer_reset(sb_t* sb)
{
sb_ct1745_mixer_write(4, 0, sb);
@@ -1655,6 +1663,7 @@ sb_16_init(const device_t *info)
}
sb->mixer_enabled = 1;
sb->mixer_sb16.output_filter = 1;
io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL,
sb_ct1745_mixer_write, NULL, NULL, sb);
sound_add_handler(sb_get_buffer_sb16_awe32, sb);
@@ -1688,6 +1697,7 @@ sb_16_pnp_init(const device_t *info)
sb_ct1745_mixer_reset(sb);
sb->mixer_enabled = 1;
sb->mixer_sb16.output_filter = 1;
sound_add_handler(sb_get_buffer_sb16_awe32, sb);
sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb);
@@ -1709,6 +1719,29 @@ sb_16_pnp_init(const device_t *info)
}
static void *
sb_16_compat_init(const device_t *info)
{
sb_t *sb = malloc(sizeof(sb_t));
memset(sb, 0, sizeof(sb_t));
opl3_init(&sb->opl);
sb_dsp_init(&sb->dsp, SB16, SB_SUBTYPE_DEFAULT, sb);
sb_ct1745_mixer_reset(sb);
sb->mixer_enabled = 1;
sound_add_handler(sb_get_buffer_sb16_awe32, sb);
sb->mpu = (mpu_t *) malloc(sizeof(mpu_t));
memset(sb->mpu, 0, sizeof(mpu_t));
mpu401_init(sb->mpu, 0, 0, M_UART, 1);
sb_dsp_set_mpu(&sb->dsp, sb->mpu);
return sb;
}
static int
sb_awe32_available()
{
@@ -1783,6 +1816,7 @@ sb_awe32_init(const device_t *info)
}
sb->mixer_enabled = 1;
sb->mixer_sb16.output_filter = 1;
io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL,
sb_ct1745_mixer_write, NULL, NULL, sb);
sound_add_handler(sb_get_buffer_sb16_awe32, sb);
@@ -1820,6 +1854,7 @@ sb_awe32_pnp_init(const device_t *info)
sb_ct1745_mixer_reset(sb);
sb->mixer_enabled = 1;
sb->mixer_sb16.output_filter = 1;
sound_add_handler(sb_get_buffer_sb16_awe32, sb);
sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb);
@@ -2870,6 +2905,18 @@ const device_t sb_16_pnp_device =
sb_16_pnp_config
};
const device_t sb_16_compat_device =
{
"Sound Blaster 16 (Compatibility)",
"sb16_compat",
DEVICE_ISA | DEVICE_AT,
0,
sb_16_compat_init, sb_close, NULL, { NULL },
sb_speed_changed,
NULL,
NULL
};
const device_t sb_32_pnp_device =
{
"Sound Blaster 32 PnP",

View File

@@ -133,6 +133,8 @@ static const SOUND_CARD sound_cards[] =
{ &ncr_business_audio_device },
{ &sb_mcv_device },
{ &sb_pro_mcv_device },
{ &cmi8338_device },
{ &cmi8738_device },
{ &es1371_device },
{ &ad1881_device },
{ &cs4297a_device },

View File

@@ -657,7 +657,7 @@ SNDOBJ := sound.o \
snd_lpt_dac.o snd_lpt_dss.o \
snd_adlib.o snd_adlibgold.o snd_ad1848.o snd_audiopci.o \
snd_ac97_codec.o snd_ac97_via.o \
snd_azt2316a.o snd_cs423x.o \
snd_azt2316a.o snd_cs423x.o snd_cmi8x38.o \
snd_cms.o \
snd_gus.o \
snd_sb.o snd_sb_dsp.o \