Skip to content

Commit c6f67b9

Browse files
committed
GUS: Implement recording sample rate and sampling control registers, fixes SB DMA failure in MegaEM 3.x
1 parent 8500cf3 commit c6f67b9

1 file changed

Lines changed: 74 additions & 14 deletions

File tree

‎src/sound/snd_gus.c‎

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,8 @@
1717
* Copyright 2026 win2kgamer
1818
*/
1919

20-
/*
21-
* Known issues:
22-
* - MegaEM 3.x has nonfunctional SoundBlaster emulation and appears to
23-
* rely on undocumented hardware behavior. This manifests as silent
24-
* digital audio in MegaEM 3.03 and as an IRQ conflict error on MegaEM
25-
* 3.04 and later.
26-
*/
27-
2820
/*
2921
* TODO:
30-
* - Find the undocumented hardware behavior MegaEM 3.x relies on for emulating
31-
* a SoundBlaster.
3222
* - Implement the 16-bit recording daughterboard for the GUS Classic: this has
3323
* a CS4231 codec and can be jumpered for the following addresses: 530h, 604h,
3424
* E80h or F40h. IRQ (3/4/5/6/7/9) and DMA (1/2/3) are also jumpered.
@@ -246,6 +236,14 @@ typedef struct gus_t {
246236
uint16_t cur_codec_addr;
247237
uint8_t dmaover;
248238

239+
/* GUS ADC stub */
240+
uint8_t adc_srate;
241+
uint8_t adc_ctrl;
242+
uint16_t adc_freq;
243+
uint8_t adc_irq;
244+
double inputlatch;
245+
pc_timer_t sample_timer;
246+
249247
void * log; /* New logging system */
250248
} gus_t;
251249

@@ -301,6 +299,8 @@ gus_update_int_status(gus_t *gus)
301299
irq_pending = 1; /*Timer 2 interrupt pending*/
302300
if ((gus->irqstatus & 0x80) && (gus->dmactrl & 0x20))
303301
irq_pending = 1; /*DMA TC interrupt pending*/
302+
if ((gus->irqstatus & 0x80) && (gus->adc_ctrl & 0x20))
303+
irq_pending = 1; /*ADC DMA TC interrupt pending*/
304304

305305
midi_irq_pending = gus->midi_status & MIDI_INT_MASTER;
306306

@@ -355,6 +355,40 @@ gus_midi_update_int_status(gus_t *gus)
355355
gus_update_int_status(gus);
356356
}
357357

358+
void
359+
gus_input_poll(void *priv)
360+
{
361+
gus_t *gus = (gus_t *) priv;
362+
int dma_result;
363+
364+
timer_advance_u64(&gus->sample_timer, (uint64_t) gus->inputlatch);
365+
366+
if (gus->adc_ctrl & 0x01) {
367+
if (gus->adc_ctrl & 0x02) {
368+
if (gus->adc_ctrl & 0x04)
369+
dma_result = dma_channel_write(gus->dma, (gus->adc_ctrl & 0x80) ? 0x0000 : 0x8080);
370+
else {
371+
dma_result = dma_channel_write(gus->dma, (gus->adc_ctrl & 0x80) ? 0x00 : 0x80);
372+
dma_result = dma_channel_write(gus->dma, (gus->adc_ctrl & 0x80) ? 0x00 : 0x80);
373+
}
374+
} else {
375+
if (gus->adc_ctrl & 0x04)
376+
dma_result = dma_channel_write(gus->dma, (gus->adc_ctrl & 0x80) ? 0x0000 : 0x0080);
377+
else
378+
dma_result = dma_channel_write(gus->dma, (gus->adc_ctrl & 0x80) ? 0x00 : 0x80);
379+
}
380+
if (dma_result & DMA_OVER) {
381+
gus->adc_ctrl &= 0xfe;
382+
gus->irqstatus |= 0x80;
383+
gus->adc_irq = 1;
384+
gus_log(gus->log, "ADC DMA complete, firing IRQ\n");
385+
timer_disable(&gus->sample_timer);
386+
}
387+
} else {
388+
timer_disable(&gus->sample_timer);
389+
}
390+
}
391+
358392
void
359393
gus_write(uint16_t addr, uint8_t val, void *priv)
360394
{
@@ -662,6 +696,25 @@ gus_write(uint16_t addr, uint8_t val, void *priv)
662696
gus->t2on = 1;
663697
break;
664698

699+
case 0x48: /*ADC Sample Rate*/
700+
gus->adc_srate = val;
701+
gus->adc_freq = 617400 / gus->adc_srate; /* (9878400 / (freq * 16) - 2 */
702+
double temp = 1000000.0 / gus->adc_freq;
703+
if (gus->adc_freq < 4000)
704+
gus->adc_freq = 4000;
705+
if (gus->adc_freq > 44100)
706+
gus->adc_freq = 44100;
707+
gus->inputlatch = ((double) TIMER_USEC * temp);
708+
gus_log(gus->log, "GUS ADC samplerate set to %i, val = %02X\n", gus->adc_freq, gus->adc_srate);
709+
break;
710+
case 0x49: /*ADC Sample Control*/
711+
/* This is the ADC equivalent of index 41h DMA Control and is relied on by MegaEM 3.x */
712+
gus->adc_ctrl = val;
713+
if (val & 1)
714+
timer_set_delay_u64(&gus->sample_timer, (uint64_t) gus->inputlatch);
715+
gus_log(gus->log, "GUS DMA Control write! new val = %02X\n", val);
716+
break;
717+
665718
case 0x4B: /*Joystick trim DAC*/
666719
gus->joy_trim = val;
667720
break;
@@ -950,8 +1003,11 @@ gus_read(uint16_t addr, void *priv)
9501003
/* Handling for undocumented NMI status bit, needed by SBOS */
9511004
if (((gus->ad_status & 0x18) && (gus->sb_ctrl & 0x20)) || ((gus->ad_status & 0x01) && (gus->sb_ctrl & 0x02)))
9521005
val |= 0x10;
1006+
/* Ensure the DMA TC bit *is* set if the ADC caused an IRQ */
1007+
if (gus->adc_irq)
1008+
val |= 0x80;
9531009
/* DMA Terminal Count bit is inhibited if DMA Control bit 5 is cleared */
954-
if (!(gus->dmactrl & 0x20))
1010+
if ((!(gus->dmactrl & 0x20)) && (!(gus->adc_ctrl & 0x20)))
9551011
val &= 0x7f;
9561012
gus_log(gus->log, "GUS read: port = %04X, val = %02X\n", addr, val);
9571013
return val;
@@ -1069,9 +1125,12 @@ gus_read(uint16_t addr, void *priv)
10691125
case 0x45: /*Timer control*/
10701126
gus_log(gus->log, "GUS read: port = %04X, val = %02X\n", addr, gus->tctrl);
10711127
return gus->tctrl;
1072-
case 0x49: /*Sampling control*/
1073-
gus_log(gus->log, "GUS read: port = %04X, val = %02X\n", addr, 0);
1074-
return 0;
1128+
case 0x49: /*ADC Sampling control*/
1129+
val = gus->adc_ctrl | (((gus->irqstatus & 0x80) || gus->adc_irq) ? 0x40 : 0);
1130+
gus->irqstatus &= ~0x80;
1131+
gus->adc_irq = 0;
1132+
gus_log(gus->log, "GUS read: port = %04X, val = %02X\n", addr, val);
1133+
return val;
10751134

10761135
case 0x4B: /*Joystick trim DAC*/
10771136
gus_log(gus->log, "GUS read: port = %04X, val = %02X\n", addr, gus->joy_trim);
@@ -1828,6 +1887,7 @@ gus_init(UNUSED(const device_t *info))
18281887
timer_add(&gus->samp_timer, gus_poll_wave, gus, 1);
18291888
timer_add(&gus->timer_1, gus_poll_timer_1, gus, 1);
18301889
timer_add(&gus->timer_2, gus_poll_timer_2, gus, 1);
1890+
timer_add(&gus->sample_timer, gus_input_poll, gus, 0);
18311891

18321892
sound_add_handler(gus_get_buffer, gus);
18331893

0 commit comments

Comments
 (0)