CMI8x38: Implement TDMA update registers (nothing uses them)

This commit is contained in:
RichardG867
2022-03-17 15:47:25 -03:00
parent 7571c0b155
commit 90e9054f2c
3 changed files with 46 additions and 30 deletions

View File

@@ -15,9 +15,9 @@ typedef struct sb_dsp_t {
int sb_subtype; /* which clone */
void *parent; /* "sb_t *" if default subtype, "azt2316a_t *" if aztech. */
int sb_8_length, sb_8_format, sb_8_autoinit, sb_8_pause, sb_8_enable, sb_8_autolen, sb_8_output;
int sb_8_length, sb_8_origlength, sb_8_format, sb_8_autoinit, sb_8_pause, sb_8_enable, sb_8_autolen, sb_8_output;
int sb_8_dmanum;
int sb_16_length, sb_16_format, sb_16_autoinit, sb_16_pause, sb_16_enable, sb_16_autolen, sb_16_output;
int sb_16_length, sb_16_origlength, sb_16_format, sb_16_autoinit, sb_16_pause, sb_16_enable, sb_16_autolen, sb_16_output;
int sb_16_dmanum;
int sb_pausetime;
int (*dma_readb)(void *priv),

View File

@@ -1,18 +1,18 @@
/*
* 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.
* 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.
* This file is part of the 86Box distribution.
*
* C-Media CMI8x38 PCI audio controller emulation.
* C-Media CMI8x38 PCI audio controller emulation.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2022 RichardG.
* Copyright 2022 RichardG.
*/
#include <stdarg.h>
#include <stdio.h>
@@ -81,11 +81,12 @@ typedef struct _cmi8x38_ {
cmi8x38_dma_t dma[2];
uint16_t tdma_base_addr, tdma_base_count;
int tdma_8, tdma_16, tdma_mask, prev_mask;
int tdma_8, tdma_16, tdma_mask, prev_mask, tdma_irq_mask;
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;
@@ -178,6 +179,18 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel
dma[channel].cc = dma[channel].cb = *count;
}
/* Check TDMA position update interrupt if enabled. */
if (dev->io_regs[0x0e] & 0x04) {
/* Nothing uses this; I assume it goes by the SB DSP sample counter (forwards instead of backwards). */
int origlength = (channel & 4) ? dev->sb->dsp.sb_16_origlength : dev->sb->dsp.sb_8_origlength,
length = (channel & 4) ? dev->sb->dsp.sb_16_length : dev->sb->dsp.sb_8_length;
if ((origlength != length) && (((origlength - length) & dev->tdma_irq_mask) == 0)) { /* skip initial sample */
/* Fire the interrupt. */
dev->io_regs[0x11] |= (channel & 4) ? 0x40 : 0x80;
cmi8x38_update_irqs(dev);
}
}
/* Handle end of DMA. */
if (*count == 0xffff) {
if (dma[channel].mode & 0x10) { /* auto-init */
@@ -193,8 +206,8 @@ cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel
dev->tdma_mask |= 1 << channel;
}
/* Set the mysterious LHBTOG bit, assuming it corresponds to
the 8237 channel status bit. Nothing appears to read it. */
/* Set the mysterious LHBTOG bit, assuming it corresponds
to the 8237 channel status bit. Nothing reads it. */
dev->io_regs[0x10] |= 0x40;
return DMA_OVER;
@@ -330,8 +343,8 @@ cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv)
(channel & 4) ? ((dma[channel].ab & 0xfffe0000) | ((*daddr) << 1)) : ((dma[channel].ab & 0xffff0000) | *daddr),
*count);
/* Clear the mysterious LHBTOG bit, assuming it corresponds to
the 8237 channel status bit. Nothing appears to read it. */
/* Clear the mysterious LHBTOG bit, assuming it corresponds
to the 8237 channel status bit. Nothing reads it. */
dev->io_regs[0x10] &= ~0x40;
}
@@ -445,7 +458,7 @@ cmi8x38_sb_mixer_write(uint16_t addr, uint8_t val, void *priv)
if ((mixer->index == 0x00) || (mixer->index == 0x0e))
sb_dsp_set_stereo(&dev->sb->dsp, mixer->regs[0x0e] & 2);
/* Set TDMA channels if autodetection is enabled. */
/* Set TDMA channels if auto-detection is enabled. */
if ((dev->io_regs[0x27] & 0x01) && (mixer->index == 0x81)) {
dev->tdma_8 = dev->sb->dsp.sb_8_dmanum;
if (dev->sb->dsp.sb_type >= SB16)
@@ -712,8 +725,8 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv)
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;
if (dev->type == CMEDIA_CMI8338)
return;
#endif
/* Update sample rate. */
dev->io_regs[addr] = val;
@@ -794,6 +807,9 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv)
val &= 0x0f;
else
val &= 0xdf;
/* Update the TDMA position update interrupt's sample interval. */
dev->tdma_irq_mask = 2047 >> ((val >> 2) & 3);
break;
case 0x19:
@@ -1008,8 +1024,7 @@ cmi8x38_dma_process(void *priv)
/* 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. */
Nothing reads sample_count_out; 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]) + 1;
@@ -1321,6 +1336,7 @@ cmi8x38_reset(void *priv)
memset(dev->io_regs, 0, sizeof(dev->io_regs));
dev->io_regs[0x0b] = (dev->type >> 8) & 0x1f;
dev->io_regs[0x0f] = dev->type >> 16;
dev->tdma_irq_mask = 2047;
/* Reset I/O mappings. */
cmi8x38_remap(dev);
@@ -1422,8 +1438,8 @@ cmi8x38_close(void *priv)
static const device_config_t cmi8x38_config[] = {
// clang-format off
{"receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 },
{ "", "", -1 }
{ "receive_input", "Receive input (MPU-401)", CONFIG_BINARY, "", 1 },
{ "", "", -1 }
// clang-format on
};

View File

@@ -349,7 +349,7 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
dsp->sb_pausetime = -1;
if (dma8) {
dsp->sb_8_length = len;
dsp->sb_8_length = dsp->sb_8_origlength = len;
dsp->sb_8_format = format;
dsp->sb_8_autoinit = autoinit;
dsp->sb_8_pause = 0;
@@ -363,7 +363,7 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
dsp->sbleftright = dsp->sbleftright_default;
dsp->sbdacpos = 0;
} else {
dsp->sb_16_length = len;
dsp->sb_16_length = dsp->sb_16_origlength = len;
dsp->sb_16_format = format;
dsp->sb_16_autoinit = autoinit;
dsp->sb_16_pause = 0;
@@ -380,7 +380,7 @@ void
sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
{
if (dma8) {
dsp->sb_8_length = len;
dsp->sb_8_length = dsp->sb_8_origlength = len;
dsp->sb_8_format = format;
dsp->sb_8_autoinit = autoinit;
dsp->sb_8_pause = 0;
@@ -391,7 +391,7 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
if (!timer_is_enabled(&dsp->input_timer))
timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi);
} else {
dsp->sb_16_length = len;
dsp->sb_16_length = dsp->sb_16_origlength = len;
dsp->sb_16_format = format;
dsp->sb_16_autoinit = autoinit;
dsp->sb_16_pause = 0;
@@ -1383,7 +1383,7 @@ pollsb(void *p)
if (dsp->sb_8_length < 0) {
if (dsp->sb_8_autoinit)
dsp->sb_8_length = dsp->sb_8_autolen;
dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen;
else {
dsp->sb_8_enable = 0;
timer_disable(&dsp->output_timer);
@@ -1432,7 +1432,7 @@ pollsb(void *p)
if (dsp->sb_16_length < 0) {
sb_dsp_log("16DMA over %i\n", dsp->sb_16_autoinit);
if (dsp->sb_16_autoinit)
dsp->sb_16_length = dsp->sb_16_autolen;
dsp->sb_16_length = dsp->sb_16_origlength = dsp->sb_16_autolen;
else {
dsp->sb_16_enable = 0;
timer_disable(&dsp->output_timer);
@@ -1491,7 +1491,7 @@ sb_poll_i(void *p)
if (dsp->sb_8_length < 0) {
if (dsp->sb_8_autoinit)
dsp->sb_8_length = dsp->sb_8_autolen;
dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen;
else {
dsp->sb_8_enable = 0;
timer_disable(&dsp->input_timer);
@@ -1536,7 +1536,7 @@ sb_poll_i(void *p)
if (dsp->sb_16_length < 0) {
if (dsp->sb_16_autoinit)
dsp->sb_16_length = dsp->sb_16_autolen;
dsp->sb_16_length = dsp->sb_16_origlength = dsp->sb_16_autolen;
else {
dsp->sb_16_enable = 0;
timer_disable(&dsp->input_timer);