Add the Gravis UltraSound revision 3.7
Implement the ICS-2101 mixer chip that it has
This commit is contained in:
@@ -205,6 +205,7 @@ extern const device_t ct5880_onboard_device;
|
|||||||
|
|
||||||
/* Gravis UltraSound family */
|
/* Gravis UltraSound family */
|
||||||
extern const device_t gus_device;
|
extern const device_t gus_device;
|
||||||
|
extern const device_t gus_v37_device;
|
||||||
extern const device_t gus_max_device;
|
extern const device_t gus_max_device;
|
||||||
extern const device_t gus_ace_device;
|
extern const device_t gus_ace_device;
|
||||||
|
|
||||||
|
|||||||
@@ -44,11 +44,33 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GUS_CLASSIC = 0,
|
GUS_CLASSIC = 0,
|
||||||
GUS_MAX = 1,
|
GUS_CLASSIC_37 = 1,
|
||||||
GUS_ACE = 2,
|
GUS_MAX = 2,
|
||||||
|
GUS_ACE = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GUS_ICS2101_MIC_IN = 0,
|
||||||
|
GUS_ICS2101_LINE_IN = 1,
|
||||||
|
GUS_ICS2101_CD_IN = 2,
|
||||||
|
GUS_ICS2101_GF1_OUT = 3,
|
||||||
|
GUS_ICS2101_UNUSED = 4,
|
||||||
|
GUS_ICS2101_MASTER = 5,
|
||||||
|
GUS_ICS2101_MAX = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ics2101_chan_t {
|
||||||
|
uint8_t ctrl[2];
|
||||||
|
double level[2];
|
||||||
|
uint8_t pan;
|
||||||
|
} ics2101_chan_t;
|
||||||
|
|
||||||
|
typedef struct ics2101_t {
|
||||||
|
uint8_t addr;
|
||||||
|
ics2101_chan_t channels[GUS_ICS2101_MAX];
|
||||||
|
} ics2101_t;
|
||||||
|
|
||||||
typedef struct gus_t {
|
typedef struct gus_t {
|
||||||
int reset;
|
int reset;
|
||||||
|
|
||||||
@@ -152,6 +174,8 @@ typedef struct gus_t {
|
|||||||
uint8_t max_ctrl;
|
uint8_t max_ctrl;
|
||||||
|
|
||||||
ad1848_t ad1848;
|
ad1848_t ad1848;
|
||||||
|
|
||||||
|
ics2101_t ics2101;
|
||||||
} gus_t;
|
} gus_t;
|
||||||
|
|
||||||
static int gus_gf1_irqs[8] = { -1, 2, 5, 3, 7, 11, 12, 15 };
|
static int gus_gf1_irqs[8] = { -1, 2, 5, 3, 7, 11, 12, 15 };
|
||||||
@@ -165,6 +189,12 @@ int gusfreqs[] = {
|
|||||||
|
|
||||||
double vol16bit[4096];
|
double vol16bit[4096];
|
||||||
|
|
||||||
|
double ics2101_att[128];
|
||||||
|
|
||||||
|
double ics2101_pan[] = { 0.35481, 0.35481, 0.35481, 0.37584, 0.47315, 0.53088, 0.59566, 0.66834,
|
||||||
|
0.70795,
|
||||||
|
0.74989, 0.79433, 0.84140, 0.89125, 0.94406, 1.00000, 1.00000, 1.00000 };
|
||||||
|
|
||||||
void gus_write(uint16_t addr, uint8_t val, void *priv);
|
void gus_write(uint16_t addr, uint8_t val, void *priv);
|
||||||
uint8_t gus_read(uint16_t addr, void *priv);
|
uint8_t gus_read(uint16_t addr, void *priv);
|
||||||
|
|
||||||
@@ -264,6 +294,10 @@ gus_write(uint16_t addr, uint8_t val, void *priv)
|
|||||||
uint16_t port;
|
uint16_t port;
|
||||||
uint16_t csioport;
|
uint16_t csioport;
|
||||||
|
|
||||||
|
ics2101_t *ics2101 = &gus->ics2101;
|
||||||
|
uint8_t mixer_ch;
|
||||||
|
uint8_t mixer_lr;
|
||||||
|
|
||||||
if ((addr == 0x388) || (addr == 0x389))
|
if ((addr == 0x388) || (addr == 0x389))
|
||||||
port = addr;
|
port = addr;
|
||||||
else
|
else
|
||||||
@@ -705,8 +739,43 @@ gus_write(uint16_t addr, uint8_t val, void *priv)
|
|||||||
gus->reg_ctrl = val;
|
gus->reg_ctrl = val;
|
||||||
break;
|
break;
|
||||||
case 0x306:
|
case 0x306:
|
||||||
|
if (gus->type == GUS_CLASSIC_37) {
|
||||||
|
mixer_ch = (ics2101->addr >> 3) & 0x7; /* current attenuator */
|
||||||
|
mixer_lr = ics2101->addr & 1; /* left or right channel */
|
||||||
|
switch (ics2101->addr & 0x6) {
|
||||||
|
case 0: /* Set control */
|
||||||
|
ics2101->channels[mixer_ch].ctrl[mixer_lr] = val & 0xF;
|
||||||
|
if ((mixer_lr == 0) && (val & 0xC)) /* copy to right channel if not normal mode */
|
||||||
|
ics2101->channels[mixer_ch].ctrl[1] = val & 0xF;
|
||||||
|
break;
|
||||||
|
case 2: /* Set attenuator */
|
||||||
|
switch (ics2101->channels[mixer_ch].ctrl[mixer_lr] & 0xC) {
|
||||||
|
case 0: /* Normal mode */
|
||||||
|
ics2101->channels[mixer_ch].level[mixer_lr] = ics2101_att[val & 0x7F];
|
||||||
|
break;
|
||||||
|
case 4: /* Stereo mode */
|
||||||
|
ics2101->channels[mixer_ch].level[0] = ics2101_att[val & 0x7F];
|
||||||
|
ics2101->channels[mixer_ch].level[1] = ics2101_att[val & 0x7F];
|
||||||
|
break;
|
||||||
|
case 8: /* Balance/Pan mode */
|
||||||
|
ics2101->channels[mixer_ch].level[0] = ics2101_att[val & 0x7F] * ics2101_pan[ics2101->channels[mixer_ch].pan + 1];
|
||||||
|
ics2101->channels[mixer_ch].level[1] = ics2101_att[val & 0x7F] * ics2101_pan[16 - ics2101->channels[mixer_ch].pan];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: /* Set panning */
|
||||||
|
ics2101->channels[mixer_ch].pan = val & 0xF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fallthrough;
|
||||||
case 0x706:
|
case 0x706:
|
||||||
if (gus->type == GUS_MAX) {
|
if (gus->type == GUS_CLASSIC_37) {
|
||||||
|
gus->ics2101.addr = val & 0x3F;
|
||||||
|
} else if (gus->type == GUS_MAX) {
|
||||||
if (gus->dma >= 4)
|
if (gus->dma >= 4)
|
||||||
val |= 0x10;
|
val |= 0x10;
|
||||||
if (gus->dma2 >= 4)
|
if (gus->dma2 >= 4)
|
||||||
@@ -900,12 +969,14 @@ gus_read(uint16_t addr, void *priv)
|
|||||||
break;
|
break;
|
||||||
case 0x306:
|
case 0x306:
|
||||||
case 0x706:
|
case 0x706:
|
||||||
if (gus->type == GUS_MAX)
|
if (gus->type == GUS_CLASSIC_37)
|
||||||
|
val = 0x06; /* 3.7x - mixer, no reverse channels bug */
|
||||||
|
else if (gus->type == GUS_MAX)
|
||||||
val = 0x0a; /* GUS MAX */
|
val = 0x0a; /* GUS MAX */
|
||||||
else if (gus->type == GUS_ACE)
|
else if (gus->type == GUS_ACE)
|
||||||
val = 0x30; /* GUS ACE */
|
val = 0x30; /* GUS ACE */
|
||||||
else
|
else
|
||||||
val = 0xff; /*Pre 3.7 - no mixer*/
|
val = 0xff; /* Pre 3.7 - no mixer */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x307: /*DRAM access*/
|
case 0x307: /*DRAM access*/
|
||||||
@@ -1215,12 +1286,54 @@ gus_get_buffer(int32_t *buffer, int len, void *priv)
|
|||||||
ad1848_update(&gus->ad1848);
|
ad1848_update(&gus->ad1848);
|
||||||
|
|
||||||
gus_update(gus);
|
gus_update(gus);
|
||||||
|
if (gus->type == GUS_CLASSIC_37)
|
||||||
for (int c = 0; c < len * 2; c++) {
|
for (int c = 0; c < len * 2; c += 2) {
|
||||||
if ((gus->type == GUS_MAX) && (gus->max_ctrl))
|
double temp_l = 0.0;
|
||||||
buffer[c] += (int32_t) (gus->ad1848.buffer[c] / 2);
|
double temp_r = 0.0;
|
||||||
buffer[c] += (int32_t) gus->buffer[c & 1][c >> 1];
|
/* GF1 out */
|
||||||
}
|
uint8_t ctrl_l = gus->ics2101.channels[GUS_ICS2101_GF1_OUT].ctrl[0];
|
||||||
|
uint8_t ctrl_r = gus->ics2101.channels[GUS_ICS2101_GF1_OUT].ctrl[1];
|
||||||
|
if (!(ctrl_l & 0xC)) { /* Normal mode */
|
||||||
|
if (ctrl_l & 1)
|
||||||
|
temp_l += (double) gus->buffer[0][c >> 1] * gus->ics2101.channels[GUS_ICS2101_GF1_OUT].level[0];
|
||||||
|
if (ctrl_l & 2)
|
||||||
|
temp_r += (double) gus->buffer[0][c >> 1] * gus->ics2101.channels[GUS_ICS2101_GF1_OUT].level[0];
|
||||||
|
if (ctrl_r & 1)
|
||||||
|
temp_l += (double) gus->buffer[1][c >> 1] * gus->ics2101.channels[GUS_ICS2101_GF1_OUT].level[1];
|
||||||
|
if (ctrl_r & 2)
|
||||||
|
temp_r += (double) gus->buffer[1][c >> 1] * gus->ics2101.channels[GUS_ICS2101_GF1_OUT].level[1];
|
||||||
|
} else { /* Stereo or Balance/Pan mode */
|
||||||
|
if (ctrl_l & 2) { /* Mono/Pan */
|
||||||
|
temp_l += ((double) gus->buffer[0][c >> 1] + (double) gus->buffer[1][c >> 1]) * 0.5 * gus->ics2101.channels[GUS_ICS2101_GF1_OUT].level[(ctrl_l & 1)];
|
||||||
|
temp_r += ((double) gus->buffer[0][c >> 1] + (double) gus->buffer[1][c >> 1]) * 0.5 * gus->ics2101.channels[GUS_ICS2101_GF1_OUT].level[!(ctrl_l & 1)];
|
||||||
|
} else { /* Stereo/Balance */
|
||||||
|
temp_l += (double) gus->buffer[(ctrl_l & 1)][c >> 1] * gus->ics2101.channels[GUS_ICS2101_GF1_OUT].level[(ctrl_l & 1)];
|
||||||
|
temp_r += (double) gus->buffer[!(ctrl_l & 1)][c >> 1] * gus->ics2101.channels[GUS_ICS2101_GF1_OUT].level[!(ctrl_l & 1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Master */
|
||||||
|
ctrl_l = gus->ics2101.channels[GUS_ICS2101_MASTER].ctrl[0];
|
||||||
|
ctrl_r = gus->ics2101.channels[GUS_ICS2101_MASTER].ctrl[1];
|
||||||
|
if (!(ctrl_l & 0xC)) { /* Normal mode */
|
||||||
|
if (ctrl_l & 1)
|
||||||
|
buffer[c] += (int32_t) (temp_l * gus->ics2101.channels[GUS_ICS2101_MASTER].level[0]);
|
||||||
|
if (ctrl_l & 2)
|
||||||
|
buffer[c + 1] += (int32_t) (temp_r * gus->ics2101.channels[GUS_ICS2101_MASTER].level[0]);
|
||||||
|
if (ctrl_r & 1)
|
||||||
|
buffer[c] += (int32_t) (temp_l * gus->ics2101.channels[GUS_ICS2101_MASTER].level[1]);
|
||||||
|
if (ctrl_r & 2)
|
||||||
|
buffer[c + 1] += (int32_t) (temp_r * gus->ics2101.channels[GUS_ICS2101_MASTER].level[1]);
|
||||||
|
} else { /* Stereo or Balance mode - no mono/pan for master */
|
||||||
|
buffer[c] += (int32_t) (((ctrl_l & 1) ? temp_l : temp_r) * gus->ics2101.channels[GUS_ICS2101_MASTER].level[(ctrl_l & 1)]);
|
||||||
|
buffer[c + 1] += (int32_t) (((ctrl_l & 1) ? temp_r : temp_l) * gus->ics2101.channels[GUS_ICS2101_MASTER].level[!(ctrl_l & 1)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
for (int c = 0; c < len * 2; c++) {
|
||||||
|
if ((gus->type == GUS_MAX) && (gus->max_ctrl))
|
||||||
|
buffer[c] += (int32_t) (gus->ad1848.buffer[c] / 2);
|
||||||
|
buffer[c] += (int32_t) gus->buffer[c & 1][c >> 1];
|
||||||
|
}
|
||||||
|
|
||||||
if ((gus->type == GUS_MAX) && (gus->max_ctrl))
|
if ((gus->type == GUS_MAX) && (gus->max_ctrl))
|
||||||
gus->ad1848.pos = 0;
|
gus->ad1848.pos = 0;
|
||||||
@@ -1228,6 +1341,17 @@ gus_get_buffer(int32_t *buffer, int len, void *priv)
|
|||||||
gus->pos = 0;
|
gus->pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gus_filter_cd_audio(int channel, double *buffer, void *priv)
|
||||||
|
{
|
||||||
|
const gus_t *gus = (gus_t *) priv;
|
||||||
|
/* FIXME: No channel remapping possible with the current architecture */
|
||||||
|
if (gus->ics2101.channels[GUS_ICS2101_CD_IN].ctrl[channel] && gus->ics2101.channels[GUS_ICS2101_MASTER].ctrl[channel])
|
||||||
|
*buffer *= gus->ics2101.channels[GUS_ICS2101_CD_IN].level[channel] * gus->ics2101.channels[GUS_ICS2101_MASTER].level[channel];
|
||||||
|
else
|
||||||
|
*buffer *= 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gus_input_msg(void *priv, uint8_t *msg, uint32_t len)
|
gus_input_msg(void *priv, uint8_t *msg, uint32_t len)
|
||||||
{
|
{
|
||||||
@@ -1363,6 +1487,13 @@ gus_reset(void *priv)
|
|||||||
gus->irq_state = 0;
|
gus->irq_state = 0;
|
||||||
gus->midi_irq_state = 0;
|
gus->midi_irq_state = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < GUS_ICS2101_MAX; i++) {
|
||||||
|
gus->ics2101.channels[i].level[0] = gus->ics2101.channels[i].level[1] = 1.0;
|
||||||
|
gus->ics2101.channels[i].ctrl[0] = 1;
|
||||||
|
gus->ics2101.channels[i].ctrl[1] = 2;
|
||||||
|
gus->ics2101.channels[i].pan = 7;
|
||||||
|
}
|
||||||
|
|
||||||
gus_update_int_status(gus);
|
gus_update_int_status(gus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1371,6 +1502,7 @@ gus_init(UNUSED(const device_t *info))
|
|||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
double out = 1.0;
|
double out = 1.0;
|
||||||
|
double gain;
|
||||||
uint8_t gus_ram = device_get_config_int("gus_ram");
|
uint8_t gus_ram = device_get_config_int("gus_ram");
|
||||||
gus_t *gus = calloc(1, sizeof(gus_t));
|
gus_t *gus = calloc(1, sizeof(gus_t));
|
||||||
|
|
||||||
@@ -1400,6 +1532,13 @@ gus_init(UNUSED(const device_t *info))
|
|||||||
|
|
||||||
gus->jumper = 0x06;
|
gus->jumper = 0x06;
|
||||||
|
|
||||||
|
for (int i = 0; i < GUS_ICS2101_MAX; i++) {
|
||||||
|
gus->ics2101.channels[i].level[0] = gus->ics2101.channels[i].level[1] = 1.0;
|
||||||
|
gus->ics2101.channels[i].ctrl[0] = 1;
|
||||||
|
gus->ics2101.channels[i].ctrl[1] = 2;
|
||||||
|
gus->ics2101.channels[i].pan = 7;
|
||||||
|
}
|
||||||
|
|
||||||
gus->base = device_get_config_hex16("base");
|
gus->base = device_get_config_hex16("base");
|
||||||
|
|
||||||
io_sethandler(gus->base, 0x0010, gus_read, NULL, NULL, gus_write, NULL, NULL, gus);
|
io_sethandler(gus->base, 0x0010, gus_read, NULL, NULL, gus_write, NULL, NULL, gus);
|
||||||
@@ -1415,6 +1554,19 @@ gus_init(UNUSED(const device_t *info))
|
|||||||
gameport_remap(gus->gameport, 0x201);
|
gameport_remap(gus->gameport, 0x201);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gus->type == GUS_CLASSIC_37) {
|
||||||
|
/* Precalculate the attenuation table for ICS2101 */
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
gain = (127 - i) * -0.5;
|
||||||
|
if (i < 16)
|
||||||
|
for (int j = 0; j < (16 - i); j++)
|
||||||
|
gain += -0.5 - 0.13603 * (j + 1);
|
||||||
|
ics2101_att[i] = pow(10.0, gain / 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sound_set_cd_audio_filter(gus_filter_cd_audio, gus);
|
||||||
|
}
|
||||||
|
|
||||||
if (gus->type == GUS_MAX) {
|
if (gus->type == GUS_MAX) {
|
||||||
ad1848_init(&gus->ad1848, AD1848_TYPE_CS4231);
|
ad1848_init(&gus->ad1848, AD1848_TYPE_CS4231);
|
||||||
ad1848_setirq(&gus->ad1848, 5);
|
ad1848_setirq(&gus->ad1848, 5);
|
||||||
@@ -1521,6 +1673,58 @@ static const device_config_t gus_config[] = {
|
|||||||
// clang-format off
|
// clang-format off
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const device_config_t gus_v37_config[] = {
|
||||||
|
// clang-format off
|
||||||
|
{
|
||||||
|
.name = "base",
|
||||||
|
.description = "Address",
|
||||||
|
.type = CONFIG_HEX16,
|
||||||
|
.default_string = NULL,
|
||||||
|
.default_int = 0x220,
|
||||||
|
.file_filter = NULL,
|
||||||
|
.spinner = { 0 },
|
||||||
|
.selection = {
|
||||||
|
{ .description = "210H", .value = 0x210 },
|
||||||
|
{ .description = "220H", .value = 0x220 },
|
||||||
|
{ .description = "230H", .value = 0x230 },
|
||||||
|
{ .description = "240H", .value = 0x240 },
|
||||||
|
{ .description = "250H", .value = 0x250 },
|
||||||
|
{ .description = "260H", .value = 0x260 },
|
||||||
|
{ NULL }
|
||||||
|
},
|
||||||
|
.bios = { { 0 } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "gus_ram",
|
||||||
|
.description = "Memory size",
|
||||||
|
.type = CONFIG_SELECTION,
|
||||||
|
.default_string = NULL,
|
||||||
|
.default_int = 0,
|
||||||
|
.file_filter = NULL,
|
||||||
|
.spinner = { 0 },
|
||||||
|
.selection = {
|
||||||
|
{ .description = "256 KB", .value = 0 },
|
||||||
|
{ .description = "512 KB", .value = 1 },
|
||||||
|
{ .description = "1 MB", .value = 2 },
|
||||||
|
{ NULL }
|
||||||
|
},
|
||||||
|
.bios = { { 0 } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "receive_input",
|
||||||
|
.description = "Receive MIDI input",
|
||||||
|
.type = CONFIG_BINARY,
|
||||||
|
.default_string = NULL,
|
||||||
|
.default_int = 1,
|
||||||
|
.file_filter = NULL,
|
||||||
|
.spinner = { 0 },
|
||||||
|
.selection = { { 0 } },
|
||||||
|
.bios = { { 0 } }
|
||||||
|
},
|
||||||
|
{ .name = "", .description = "", .type = CONFIG_END }
|
||||||
|
// clang-format off
|
||||||
|
};
|
||||||
|
|
||||||
static const device_config_t gus_max_config[] = {
|
static const device_config_t gus_max_config[] = {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
{
|
{
|
||||||
@@ -1637,6 +1841,20 @@ const device_t gus_device = {
|
|||||||
.config = gus_config
|
.config = gus_config
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const device_t gus_v37_device = {
|
||||||
|
.name = "Gravis UltraSound (rev 3.7)",
|
||||||
|
.internal_name = "gusv37",
|
||||||
|
.flags = DEVICE_ISA16,
|
||||||
|
.local = GUS_CLASSIC_37,
|
||||||
|
.init = gus_init,
|
||||||
|
.close = gus_close,
|
||||||
|
.reset = gus_reset,
|
||||||
|
.available = NULL,
|
||||||
|
.speed_changed = gus_speed_changed,
|
||||||
|
.force_redraw = NULL,
|
||||||
|
.config = gus_v37_config
|
||||||
|
};
|
||||||
|
|
||||||
const device_t gus_max_device = {
|
const device_t gus_max_device = {
|
||||||
.name = "Gravis UltraSound MAX",
|
.name = "Gravis UltraSound MAX",
|
||||||
.internal_name = "gusmax",
|
.internal_name = "gusmax",
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ static const SOUND_CARD sound_cards[] = {
|
|||||||
{ &cs4235_device },
|
{ &cs4235_device },
|
||||||
{ &cs4236b_device },
|
{ &cs4236b_device },
|
||||||
{ &gus_device },
|
{ &gus_device },
|
||||||
|
{ &gus_v37_device },
|
||||||
{ &gus_max_device },
|
{ &gus_max_device },
|
||||||
{ &gus_ace_device },
|
{ &gus_ace_device },
|
||||||
{ &mirosound_pcm10_device },
|
{ &mirosound_pcm10_device },
|
||||||
|
|||||||
Reference in New Issue
Block a user