Rebase/reimplement SB16/WSS support on latest main

This commit is contained in:
Ian Scott committed 2026-03-15 23:51:31 -06:00
1 parent d3ac864d9c
commit fee4b40d76
8 files changed
+709 -236

No files matched your search

+9 -2
View File
@@ -252,21 +252,28 @@ endfunction()
################################################################################
# Build SB firmware
option(SOUND_WSS "Build SB firmware with WSS (AD1848) support instead of SB16 DSP" OFF)
function(build_sb TARGET_NAME MULTIFW)
set(USB_JOYSTICK TRUE)
set(SOUND_MPU TRUE)
set(SOUND_MPU FALSE)
set(SOUND_OPL TRUE)
set(CDROM TRUE)
config_target(${TARGET_NAME} ${MULTIFW})
pico_set_program_name(${TARGET_NAME} "picogus-sb")
target_sources(${TARGET_NAME} PRIVATE
audio/volctrl.cpp
sbdsp/sbdsp.cpp
sbplay.cpp
isa/isa_dma.c
audio/audio_fifo.c
audio/audio_i2s_minimal.c
)
if(SOUND_WSS)
target_sources(${TARGET_NAME} PRIVATE ad1848/ad1848.cpp)
target_compile_definitions(${TARGET_NAME} PRIVATE SOUND_WSS=1)
else()
target_sources(${TARGET_NAME} PRIVATE sbdsp/sbdsp.cpp)
endif()
target_compile_definitions(${TARGET_NAME} PRIVATE
SOUND_SB=1
SOUND_DSP=1
+20
View File
@@ -37,3 +37,23 @@ dma_inst_t DMA_init(PIO pio, uint sm, irq_handler_t dma_isr) {
return dma;
};
dma_inst_t DMA_multi_init(PIO pio, uint sm, irq_handler_t dma_isr) {
dma_inst_t dma;
dma.offset = pio_add_program(pio, &dma_write_multi_program);
pio_sm_claim(pio, sm);
dma.sm = sm;
dma.pio = pio;
dma_write_multi_program_init(pio, dma.sm, dma.offset);
if (dma_isr) {
uint pio_irq = (pio == pio0) ? PIO0_IRQ_0 : PIO1_IRQ_0;
irq_set_enabled(pio_irq, false);
pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + dma.sm, true);
irq_set_priority(pio_irq, PICO_HIGHEST_IRQ_PRIORITY);
irq_set_exclusive_handler(pio_irq, dma_isr);
irq_set_enabled(pio_irq, true);
}
return dma;
};
+42 -1
View File
@@ -41,6 +41,7 @@ typedef struct dma_inst {
} dma_inst_t;
dma_inst_t DMA_init(PIO pio, uint sm, irq_handler_t dma_isr);
dma_inst_t DMA_multi_init(PIO pio, uint sm, irq_handler_t dma_isr);
// __force_inline size_t DMA_Write(dma_inst_t* dma, uint32_t dmaaddr, bool invert_msb, bool is_16bit, uint32_t delay, bool* dma_active) {
__force_inline extern void DMA_Start_Write(dma_inst_t* dma) {
@@ -55,7 +56,47 @@ __force_inline extern uint32_t DMA_Complete_Write(dma_inst_t* dma) {
}
__force_inline extern void DMA_Cancel_Write(dma_inst_t* dma) {
pio_sm_exec(dma->pio, dma->sm, pio_encode_jmp(dma->offset));
if (pio_sm_get_pc(dma->pio, dma->sm) != dma->offset+1) {
pio_sm_exec(dma->pio, dma->sm, pio_encode_jmp(dma->offset));
}
}
// xfer_count: number of DMA transfers for each push. DRQ is held high for all transfers
__force_inline extern void DMA_Multi_Start_Write(dma_inst_t* dma, uint32_t xfer_count) {
pio_sm_put_blocking(dma->pio, dma->sm, xfer_count - 1);
}
// push_thresh: set number of bits for the DMA pio to autopush
__force_inline extern void DMA_Multi_Set_Push_Threshold(dma_inst_t* dma, uint32_t push_thresh) {
hw_write_masked(&dma->pio->sm[dma->sm].shiftctrl,
push_thresh << PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB,
PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS);
}
// Nanosecond-accurate DMA interval dithering.
// Alternates between floor and ceil microsecond intervals so the average
// converges on the exact nanosecond-precision sample period.
typedef struct dma_dither {
uint32_t avg;
uint32_t target;
uint32_t rate;
uint32_t trim;
} dma_dither_t;
// target_ns: exact period in nanoseconds (e.g. 1000000000 / sample_rate)
// interval_us: floor'd microsecond period (target_ns / 1000)
__force_inline extern void DMA_Dither_Init(dma_dither_t* d, uint32_t target_ns, uint32_t interval_us) {
d->target = target_ns;
d->rate = interval_us * 1000;
d->avg = target_ns;
d->trim = 0;
}
// Advance the dither filter and return the interval to use (base_interval or base_interval+1).
__force_inline extern uint32_t DMA_Dither_Step(dma_dither_t* d, uint32_t base_interval) {
d->avg = (d->rate + d->trim + 31 * d->avg) >> 5; // α=1/32 EMA
d->trim = (d->avg < d->target) ? 1000 : 0;
return base_interval + (d->trim ? 1 : 0);
}
#ifdef __cplusplus
+60
View File
@@ -86,3 +86,63 @@ static inline void dma_write_program_init(PIO pio, uint sm, uint offset) {
}
%}
; Multi-transfer DMA: keeps DRQ asserted for multiple transfers in a single frame.
; Used by WSS (AD1848) which transfers multi-byte audio frames (1-4 bytes) per DMA request.
; 12 instructions
.program dma_write_multi
.side_set 1 opt ; sideset bit is ADS
restart:
set pins, 0 side 0b0 ; deassert DRQ, muxes back to addr if canceled
.wrap_target
out x, 32 ; wait to trigger DMA operation - X has number of bytes to xfer minus 1
set pins, 1 ; assert DRQ
dma_loop:
wait 1 gpio DACK_PIN ; DACK assert - no sideset up to this point as it could interfere with isa_io.pio
wait 0 gpio IOW_PIN side 0b1 ; wait for IOW assert, muxes to data
jmp !x deassert_drq ; if last loop, deassert DRQ
continue:
wait 1 gpio IOW_PIN side 0b1 ; wait for IOW deassert
in pins, 8 side 0b0 ; input data, muxes back to addr
wait 0 gpio DACK_PIN ; wait for DACK deassert - necessary, otherwise could still be asserted next dma_loop
jmp x-- dma_loop ; loop until (X+1) bytes have been transferred
.wrap
deassert_drq:
set pins, 0 ; deassert DRQ
jmp continue
% c-sdk {
static inline void dma_write_multi_program_init(PIO pio, uint sm, uint offset) {
pio_sm_config c = dma_write_multi_program_get_default_config(offset);
// Set up AD0 bus as input
sm_config_set_in_pins(&c, AD0_PIN);
// Autopush - default 8 bits (1 byte), reconfigurable at runtime for frame size
sm_config_set_in_shift(&c, true, true, 8);
// Autopull 32 bits (loop count)
sm_config_set_out_shift(&c, false, true /* autopull */, 32);
// Set the pin direction for IOW and AD0 bus as input at the PIO
pio_sm_set_consecutive_pindirs(pio, sm, IOW_PIN, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, AD0_PIN, 8, false);
// Config DMA pins
sm_config_set_set_pins(&c, DRQ_PIN, 1);
pio_gpio_init(pio, DRQ_PIN);
gpio_set_drive_strength(DRQ_PIN, GPIO_DRIVE_STRENGTH_12MA);
pio_sm_set_consecutive_pindirs(pio, sm, DRQ_PIN, 1, true);
pio_sm_set_consecutive_pindirs(pio, sm, DACK_PIN, 1, false);
// set up ADS
sm_config_set_sideset_pins(&c, ADS_PIN);
pio_gpio_init(pio, ADS_PIN);
pio_sm_set_consecutive_pindirs(pio, sm, ADS_PIN, 1, true);
// Load our configuration, and jump to the start of the program
pio_sm_init(pio, sm, offset, &c);
// Set the state machine running
pio_sm_set_enabled(pio, sm, true);
}
%}
+51 -6
View File
@@ -61,7 +61,13 @@ M62429* m62429;
#ifdef SOUND_SB
#ifdef SOUND_WSS
#include "ad1848/ad1848.h"
static constexpr uint16_t wss_port_test = 0x13;
static uint8_t wss_config = 0b00001010; // DMA 1 and IRQ 7
#else
#include "sbdsp/sbdsp.h"
#endif
static uint16_t sb_port_test;
#endif
#ifdef SOUND_OPL
@@ -726,6 +732,20 @@ __force_inline void handle_iow(void) {
} else // if follows down below
#endif // SOUND_GUS
#ifdef SOUND_SB
#ifdef SOUND_WSS
if ((port >> 4) == wss_port_test) {
pio_sm_put(pio0, IOW_PIO_SM, IO_WAIT);
switch (port & 0xf) {
// WSS config ports
case 0 ... 3:
break;
// AD1848 ports
default:
ad1848_write(port & 0x3, iow_read & 0xFF);
break;
}
} else // if follows down below
#else
if ((port >> 4) == sb_port_test) {
switch (port - settings.SB.basePort) {
// OPL ports
@@ -751,13 +771,14 @@ __force_inline void handle_iow(void) {
break;
// DSP ports
default:
pio_sm_put(pio0, IOW_PIO_SM, IO_WAIT);
pio_sm_put(pio0, IOW_PIO_SM, IO_WAIT);
sbdsp_process();
sbdsp_write(port & 0xF, iow_read & 0xFF);
sbdsp_process();
sbdsp_write(port & 0xF,iow_read & 0xFF);
sbdsp_process();
break;
}
}
} else // if follows down below
#endif // SOUND_WSS
#endif // SOUND_SB
#ifdef CDROM
if ((port >> 4) == cdrom_port_test) {
@@ -921,6 +942,25 @@ __force_inline void handle_ior(void) {
} else // if follows down below
#endif
#if defined(SOUND_SB)
#ifdef SOUND_WSS
if ((port >> 4) == wss_port_test) {
pio_sm_put(pio0, IOR_PIO_SM, IO_WAIT);
switch (port & 0xf) {
case 0: // interface register
pio_sm_put(pio0, IOR_PIO_SM, IOR_SET_VALUE | wss_config);
break;
case 1 ... 2:
pio_sm_put(pio0, IOR_PIO_SM, IOR_SET_VALUE | 0xff);
break;
case 3: // chip ID register
pio_sm_put(pio0, IOR_PIO_SM, IOR_SET_VALUE | 0x04);
break;
default:
pio_sm_put(pio0, IOR_PIO_SM, IOR_SET_VALUE | ad1848_read(port & 0x3));
break;
}
} else // if follows down below
#else
if ((port >> 4) == sb_port_test) {
pio_sm_put(pio0, IOR_PIO_SM, IO_WAIT);
switch (port - settings.SB.basePort) {
@@ -935,11 +975,12 @@ __force_inline void handle_ior(void) {
break;
default:
sbdsp_process();
pio_sm_put(pio0, IOR_PIO_SM, IOR_SET_VALUE | sbdsp_read(port & 0xF));
pio_sm_put(pio0, IOR_PIO_SM, IOR_SET_VALUE | sbdsp_read(port & 0xF));
sbdsp_process();
break;
}
} else // if follows down below
#endif // SOUND_WSS
#endif
#if defined(CDROM)
if ((port >> 4) == cdrom_port_test) {
@@ -1199,8 +1240,12 @@ int main()
#ifdef SOUND_SB
#ifdef SOUND_WSS
puts("Initializing WSS AD1848");
#else
puts("Initializing SoundBlaster DSP");
// sbdsp_init();
#endif
// init actually happens on core 1 in play_adlib()
#endif // SOUND_SB
#ifdef SOUND_OPL
puts("Creating OPL");
+487 -217
View File
@@ -7,7 +7,7 @@
#include "sbdsp.h"
/*
Title : SoundBlaster DSP Emulation
Title : SoundBlaster DSP Emulation
Date : 2023-12-30
Author : Kevin Moonlight <me@yyzkevin.com>
@@ -47,8 +47,8 @@ static irq_handler_t SBDSP_DMA_isr_pt;
static dma_inst_t dma_config;
#define DMA_PIO_SM 2
#define DSP_VERSION_MAJOR 2
#define DSP_VERSION_MINOR 1
#define DSP_VERSION_MAJOR 4
#define DSP_VERSION_MINOR 5
// Sound Blaster DSP I/O port offsets
#define DSP_RESET 0x6
@@ -56,8 +56,11 @@ static dma_inst_t dma_config;
#define DSP_WRITE 0xC
#define DSP_WRITE_STATUS 0xC
#define DSP_READ_STATUS 0xE
#define DSP_IRQ_16_STATUS 0xF
#define MIXER_COMMAND 0x4
#define MIXER_DATA 0x5
#define OUTPUT_SAMPLERATE 49716ul
#define OUTPUT_SAMPLERATE 49716ul
// Sound Blaster DSP commands.
#define DSP_DMA_HS_SINGLE 0x91
@@ -66,35 +69,68 @@ static dma_inst_t dma_config;
#define DSP_DMA_SINGLE 0x14 //follosed by length
#define DSP_DMA_AUTO 0X1C //length based on 48h
#define DSP_DMA_BLOCK_SIZE 0x48 //block size for highspeed/dma
// SB16 DSP commands
#define DSP_DMA_IO_START 0xB0
#define DSP_DMA_IO_END 0xCF
#define DSP_PAUSE_DMA_16 0xD5
#define DSP_CONTINUE_DMA_16 0x47
#define DSP_CONTINUE_DMA_8 0x45
#define DSP_EXIT_DMA_16 0xD9
#define DSP_EXIT_DMA_8 0xDA
//#define DSP_DMA_DAC 0x14
#define DSP_DIRECT_DAC 0x10
#define DSP_DIRECT_ADC 0x20
#define DSP_MIDI_READ_POLL 0x30
#define DSP_MIDI_WRITE_POLL 0x38
#define DSP_SET_TIME_CONSTANT 0x40
#define DSP_SET_SAMPLING_RATE 0x41
#define DSP_DMA_PAUSE 0xD0
#define DSP_EXIT_DMA_8 0xDA
#define DSP_DAC_PAUSE_DURATION 0x80 // Pause DAC for a duration, then generate an interrupt. Used by Tyrian.
#define DSP_ENABLE_SPEAKER 0xD1
#define DSP_DISABLE_SPEAKER 0xD3
#define DSP_DMA_RESUME 0xD4
#define DSP_SPEAKER_STATUS 0xD8
// SB16 ASP commands
#define DSP_ASP_SET_CODEC_PARAM 0x05
#define DSP_ASP_SET_REGISTER 0x0E
#define DSP_ASP_GET_REGISTER 0x0F
#define DSP_IDENT 0xE0
#define DSP_VERSION 0xE1
#define DSP_COPYRIGHT 0xE3
#define DSP_WRITETEST 0xE4
#define DSP_READTEST 0xE8
#define DSP_SINE 0xF0
#define DSP_IRQ 0xF2
#define DSP_IRQ_8 0xF2
#define DSP_IRQ_16 0xF3
#define DSP_CHECKSUM 0xF4
#define DSP_8051_READ 0xF9
#define DSP_8051_WRITE 0xFA
#define MIXER_INTERRUPT 0x80
#define MIXER_DMA 0x81
#define MIXER_IRQ_STATUS 0x82
#define MIXER_STEREO 0xE
// #define DSP_DMA_FIFO_SIZE 256
// constexpr uint16_t DSP_DMA_FIFO_BITS = DSP_DMA_FIFO_SIZE - 1;
#include "audio/audio_fifo.h"
#define DSP_UNUSED_STATUS_BITS_PULLED_HIGH 0x7F
static char const copyright_string[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
union sample32 {
uint32_t data32;
int16_t data16[2];
uint8_t data8[4];
};
sbdsp_t sbdsp;
static uint8_t mixer_state[256] = { 0 };
static uint8_t sb_8051_ram[256] = { 0 };
#ifndef SB_BUFFERLESS
constexpr uint32_t AUDIO_FIFO_SIZE_HALF = AUDIO_FIFO_SIZE >> 1;
@@ -113,7 +149,7 @@ void __force_inline sbdsp_fifo_rx(uint8_t byte) {
putchar('O');
}
}
void __force_inline sbdsp_fifo_clear() {
void __force_inline sbdsp_fifo_clear() {
fifo_reset(&sbdsp.audio_fifo);
}
audio_fifo_t* sbdsp_fifo_peek() {
@@ -127,61 +163,128 @@ static PIC_TimerEvent DSP_DMA_Event = {
.handler = DSP_DMA_EventHandler,
};
static __force_inline void sbdsp_dma_disable() {
sbdsp.dma_enabled=false;
PIC_RemoveEvent(&DSP_DMA_Event);
static __force_inline void sbdsp_dma_disable(bool pause) {
sbdsp.dma_enabled = false;
PIC_RemoveEvent(&DSP_DMA_Event);
#ifdef SB_BUFFERLESS
sbdsp.cur_sample = 0; // zero current sample
#endif
if (!pause) {
sbdsp.dma_16bit = false;
sbdsp.dma_signed = false;
sbdsp.dma_stereo = false;
}
}
static __force_inline void sbdsp_dma_enable() {
if(!sbdsp.dma_enabled) {
sbdsp.dma_enabled=true;
static dma_dither_t dither;
static __force_inline void sbdsp_set_dma_interval() {
if (sbdsp.sample_rate) {
uint32_t target_ns = 1000000000ul / sbdsp.sample_rate;
sbdsp.dma_interval = target_ns / 1000;
DMA_Dither_Init(&dither, target_ns, sbdsp.dma_interval);
} else {
sbdsp.dma_interval = 256 - sbdsp.time_constant;
DMA_Dither_Init(&dither, sbdsp.dma_interval * 1000, sbdsp.dma_interval);
}
// No halving for stereo/16-bit: dma_write_multi transfers a complete
// frame per event, so the interval is always one sample period.
}
static __force_inline void sbdsp_dma_enable() {
if (!sbdsp.dma_enabled) {
sbdsp.dma_enabled = true;
// Set autopush bits to number of bits per audio frame.
// 32 will get masked to 0 by this operation which is correct behavior.
DMA_Multi_Set_Push_Threshold(&dma_config, sbdsp.dma_bytes_per_frame << 3);
PIC_AddEvent(&DSP_DMA_Event, sbdsp.dma_interval, 0);
}
// else {
// printf("INFO: DMA Already Enabled");
// }
}
static uint32_t DSP_DMA_EventHandler(Bitu val) {
DMA_Start_Write(&dma_config);
DMA_Multi_Start_Write(&dma_config, sbdsp.dma_bytes_per_frame);
uint32_t current_interval;
sbdsp.dma_sample_count_rx++;
sbdsp.dma_xfer_count_left--;
#ifdef SB_BUFFERLESS
current_interval = sbdsp.dma_interval;
current_interval = DMA_Dither_Step(&dither, sbdsp.dma_interval);
#else
current_interval = dma_interval_calc();
#endif
// printf("%u\n", current_interval);
if(sbdsp.dma_sample_count_rx <= sbdsp.dma_sample_count) {
if (sbdsp.dma_xfer_count_left) {
return current_interval;
} else {
} else {
sbdsp.dma_done = true;
if (sbdsp.dma_16bit) {
sbdsp.irq_16_pending = true;
} else {
sbdsp.irq_8_pending = true;
}
PIC_ActivateIRQ();
if(sbdsp.autoinit) {
sbdsp.dma_sample_count_rx=0;
if (sbdsp.autoinit) {
sbdsp.dma_xfer_count_left = sbdsp.dma_xfer_count;
return current_interval;
} else {
sbdsp_dma_disable(false);
}
else {
sbdsp_dma_disable();
}
}
}
return 0;
}
static void sbdsp_dma_isr(void) {
const uint32_t dma_data = DMA_Complete_Write(&dma_config);
// dma_write_multi uses right-shift: bytes fill from MSB down.
// Complete frame arrives as one push thanks to autopush threshold.
const uint32_t dma_data = DMA_Complete_Write(&dma_config);
#ifdef SB_BUFFERLESS
sbdsp.cur_sample = scale_sample(((int16_t)(int8_t)((dma_data & 0xFF) ^ 0x80)) << 8, sb_volume, 0);
int16_t l, r;
if (sbdsp.dma_stereo) {
if (sbdsp.dma_16bit) {
// 16-bit stereo (4 bytes): right-shift packs as little-endian stereo pair
uint32_t s = sbdsp.dma_signed ? dma_data : dma_data ^ 0x80008000;
l = scale_sample((int16_t)(s & 0xFFFF), sb_volume, 0);
r = scale_sample((int16_t)(s >> 16), sb_volume, 0);
} else {
// 8-bit stereo (2 bytes): L in bits [23:16], R in bits [15:8] via right-shift
l = (int16_t)(dma_data >> 8) & 0xFF00;
r = (int16_t)(dma_data >> 16) & 0xFF00;
if (!sbdsp.dma_signed) {
l ^= (int16_t)0x8000;
r ^= (int16_t)0x8000;
}
l = scale_sample(l, sb_volume, 0);
r = scale_sample(r, sb_volume, 0);
}
} else {
if (sbdsp.dma_16bit) {
// 16-bit mono (2 bytes): right-shift packs as int16 in upper bits
l = (int16_t)(dma_data >> 16);
if (!sbdsp.dma_signed) {
l ^= (int16_t)0x8000;
}
} else {
// 8-bit mono (1 byte): byte in bits [31:24] via right-shift
l = (int16_t)(dma_data >> 16) & 0xFF00;
if (!sbdsp.dma_signed) {
l ^= (int16_t)0x8000;
}
}
l = scale_sample(l, sb_volume, 0);
r = l;
}
sbdsp.cur_sample = (uint32_t)(uint16_t)l | ((uint32_t)(uint16_t)r << 16);
#else
sbdsp_fifo_rx(dma_data & 0xFF);
#endif
}
static uint32_t DSP_DAC_Resume_eventHandler(Bitu val) {
if (sbdsp.dma_16bit) {
sbdsp.irq_16_pending = true;
} else {
sbdsp.irq_8_pending = true;
}
PIC_ActivateIRQ();
sbdsp.dac_resume_pending = false;
return 0;
@@ -198,12 +301,20 @@ uint16_t sbdsp_sample_rate() {
return sbdsp.sample_rate;
}
void sbdsp_init() {
puts("Initing ISA DMA PIO...");
void sbdsp_init() {
puts("Initing ISA DMA PIO...");
SBDSP_DMA_isr_pt = sbdsp_dma_isr;
sbdsp.dma = 0x2; // force DMA 1 for now
sbdsp.interrupt = 0x2; // force IRQ 5 for now
sbdsp.outbox = 0xAA;
dma_config = DMA_init(pio0, DMA_PIO_SM, SBDSP_DMA_isr_pt);
dma_config = DMA_multi_init(pio0, DMA_PIO_SM, SBDSP_DMA_isr_pt);
// Initialize 8051 RAM with SB16 default values (per DOSBox-X)
sb_8051_ram[0x0e] = 0xff;
sb_8051_ram[0x0f] = 0x07;
sb_8051_ram[0x37] = 0x38;
#ifndef SB_BUFFERLESS
fifo_init(&sbdsp.audio_fifo);
@@ -212,238 +323,349 @@ void sbdsp_init() {
static __force_inline void sbdsp_output(uint8_t value) {
sbdsp.outbox=value;
sbdsp.dav_pc=1;
sbdsp.outbox = value;
sbdsp.dav_pc = 1;
}
void sbdsp_process(void) {
if(sbdsp.reset_state) return;
sbdsp.dsp_busy=1;
void sbdsp_process(void) {
if (sbdsp.reset_state) return;
sbdsp.dsp_busy = 1;
if(sbdsp.dav_dsp) {
if(!sbdsp.current_command) {
if (sbdsp.dav_dsp) {
if (!sbdsp.current_command) {
sbdsp.current_command = sbdsp.inbox;
sbdsp.current_command_index=0;
sbdsp.dav_dsp=0;
sbdsp.current_command_index = 0;
sbdsp.dav_dsp = 0;
}
}
switch(sbdsp.current_command) {
switch (sbdsp.current_command) {
case DSP_DMA_PAUSE:
sbdsp.current_command=0;
sbdsp_dma_disable();
//printf("(0xD0)DMA PAUSE\n\r");
case DSP_PAUSE_DMA_16:
sbdsp.current_command = 0;
sbdsp_dma_disable(true);
break;
case DSP_DMA_RESUME:
sbdsp.current_command=0;
sbdsp_dma_enable();
//printf("(0xD4)DMA RESUME\n\r");
sbdsp.current_command = 0;
sbdsp.dma_done = false;
sbdsp_dma_enable();
break;
case DSP_CONTINUE_DMA_16:
case DSP_CONTINUE_DMA_8:
sbdsp.autoinit = 1;
sbdsp.current_command = 0;
break;
case DSP_EXIT_DMA_16:
case DSP_EXIT_DMA_8:
sbdsp.autoinit = 0;
sbdsp.current_command = 0;
break;
case DSP_DMA_AUTO:
// printf("(0x1C)DMA_AUTO\n\r");
sbdsp.autoinit=1;
sbdsp.dma_sample_count = sbdsp.dma_block_size;
sbdsp.dma_sample_count_rx=0;
sbdsp_dma_enable();
sbdsp.current_command=0;
break;
case DSP_DMA_AUTO:
sbdsp.autoinit = 1;
sbdsp.dma_bytes_per_frame = sbdsp.dma_stereo ? 2 : 1;
sbdsp.dma_xfer_count = (sbdsp.dma_block_size + 1) / sbdsp.dma_bytes_per_frame;
sbdsp.dma_xfer_count_left = sbdsp.dma_xfer_count;
sbdsp_set_dma_interval();
sbdsp.dma_done = false;
sbdsp_dma_enable();
sbdsp.current_command = 0;
break;
case DSP_DMA_HS_AUTO:
// printf("(0x90) DMA_HS_AUTO\n\r");
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
sbdsp.autoinit=1;
sbdsp.dma_sample_count = sbdsp.dma_block_size;
sbdsp.dma_sample_count_rx=0;
sbdsp_dma_enable();
break;
sbdsp.dav_dsp = 0;
sbdsp.autoinit = 1;
sbdsp.dma_bytes_per_frame = sbdsp.dma_stereo ? 2 : 1;
sbdsp.dma_xfer_count = (sbdsp.dma_block_size + 1) / sbdsp.dma_bytes_per_frame;
sbdsp.dma_xfer_count_left = sbdsp.dma_xfer_count;
sbdsp_set_dma_interval();
sbdsp.dma_done = false;
sbdsp_dma_enable();
sbdsp.current_command = 0;
break;
case DSP_SET_TIME_CONSTANT:
if(sbdsp.dav_dsp) {
if(sbdsp.current_command_index==1) {
//printf("(0x40) DSP_SET_TIME_CONSTANT\n\r");
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.time_constant = sbdsp.inbox;
sbdsp.dma_interval = 256 - sbdsp.time_constant;
sbdsp.sample_rate = 1000000ul / sbdsp.dma_interval;
sbdsp.dma_interval_trim = MAX(1, sbdsp.dma_interval >> 1);
// printf("interval: %u rate: %u, trim: %u\n", sbdsp.dma_interval, sbdsp.sample_rate, sbdsp.dma_interval_trim);
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
}
sbdsp.sample_rate = 0; // Rate of 0 indicates time constant drives DMA timing
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
}
sbdsp.current_command_index++;
}
break;
case DSP_DMA_BLOCK_SIZE:
if(sbdsp.dav_dsp) {
if(sbdsp.current_command_index==1) {
sbdsp.dma_block_size=sbdsp.inbox;
sbdsp.dav_dsp=0;
case DSP_SET_SAMPLING_RATE:
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) { // wSamplingRate.HighByte
sbdsp.sample_rate = sbdsp.inbox << 8;
sbdsp.dav_dsp = 0;
} else if (sbdsp.current_command_index == 2) { // wSamplingRate.LowByte
sbdsp.sample_rate |= sbdsp.inbox;
sbdsp.time_constant = 0;
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
}
else if(sbdsp.current_command_index==2) {
sbdsp.current_command_index++;
}
break;
case DSP_DMA_BLOCK_SIZE:
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.dma_block_size = sbdsp.inbox;
sbdsp.dav_dsp = 0;
} else if (sbdsp.current_command_index == 2) {
sbdsp.dma_block_size += (sbdsp.inbox << 8);
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
//printf("(0x48) Set BlockSize:%u\n\r",sbdsp.dma_block_size);
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
}
sbdsp.current_command_index++;
}
break;
break;
case DSP_DMA_HS_SINGLE:
// printf("(0x91) DMA_HS_SINGLE\n\r");
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
sbdsp.autoinit=0;
sbdsp.dma_sample_count = sbdsp.dma_block_size;
sbdsp.dma_sample_count_rx=0;
sbdsp_dma_enable();
break;
case DSP_DMA_SINGLE:
if(sbdsp.dav_dsp) {
if(sbdsp.current_command_index==1) {
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
sbdsp.autoinit = 0;
sbdsp.dma_bytes_per_frame = sbdsp.dma_stereo ? 2 : 1;
sbdsp.dma_xfer_count = (sbdsp.dma_block_size + 1) / sbdsp.dma_bytes_per_frame;
sbdsp.dma_xfer_count_left = sbdsp.dma_xfer_count;
sbdsp_set_dma_interval();
sbdsp.dma_done = false;
sbdsp_dma_enable();
break;
case DSP_DMA_SINGLE:
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.dma_sample_count = sbdsp.inbox;
sbdsp.dav_dsp=0;
}
else if(sbdsp.current_command_index==2) {
// printf("(0x14)DMA_SINGLE\n\r");
sbdsp.dav_dsp = 0;
} else if (sbdsp.current_command_index == 2) {
sbdsp.dma_sample_count += (sbdsp.inbox << 8);
sbdsp.dma_sample_count_rx=0;
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
sbdsp.autoinit=0;
//printf("Sample Count:%u\n",sbdsp.dma_sample_count);
sbdsp_dma_enable();
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
sbdsp.autoinit = 0;
sbdsp.dma_bytes_per_frame = sbdsp.dma_stereo ? 2 : 1;
sbdsp.dma_xfer_count = (sbdsp.dma_sample_count + 1) / sbdsp.dma_bytes_per_frame;
sbdsp.dma_xfer_count_left = sbdsp.dma_xfer_count;
sbdsp_set_dma_interval();
sbdsp.dma_done = false;
sbdsp_dma_enable();
}
sbdsp.current_command_index++;
}
break;
case DSP_IRQ:
sbdsp.current_command=0;
PIC_ActivateIRQ();
break;
case DSP_VERSION:
if(sbdsp.current_command_index==0) {
sbdsp.current_command_index=1;
sbdsp_output(DSP_VERSION_MAJOR);
}
else {
if(!sbdsp.dav_pc) {
sbdsp.current_command=0;
break;
case DSP_IRQ_8:
sbdsp.current_command = 0;
sbdsp.irq_8_pending = true;
PIC_ActivateIRQ();
break;
case DSP_IRQ_16:
sbdsp.current_command = 0;
sbdsp.irq_16_pending = true;
PIC_ActivateIRQ();
break;
case DSP_VERSION:
if (sbdsp.current_command_index == 0) {
sbdsp.current_command_index = 1;
sbdsp_output(DSP_VERSION_MAJOR);
} else {
if (!sbdsp.dav_pc) {
sbdsp.current_command = 0;
sbdsp_output(DSP_VERSION_MINOR);
}
}
break;
case DSP_IDENT:
if(sbdsp.dav_dsp) {
if(sbdsp.current_command_index==1) {
//printf("(0xE0) DSP_IDENT\n\r");
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
sbdsp_output(~sbdsp.inbox);
}
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
sbdsp_output(~sbdsp.inbox);
}
sbdsp.current_command_index++;
}
}
break;
case DSP_COPYRIGHT:
if (sbdsp.current_command_index == 0) {
sbdsp.current_command_index = 1;
sbdsp_output(copyright_string[0]);
} else {
if (!sbdsp.dav_pc) {
if (copyright_string[sbdsp.current_command_index] == '\0') {
sbdsp_output('\0');
sbdsp.current_command = 0;
} else {
sbdsp_output(copyright_string[sbdsp.current_command_index]);
}
sbdsp.current_command_index++;
}
}
break;
case DSP_ENABLE_SPEAKER:
//printf("ENABLE SPEAKER\n");
sbdsp.speaker_on = true;
sbdsp.current_command=0;
sbdsp.current_command = 0;
break;
case DSP_DISABLE_SPEAKER:
//printf("DISABLE SPEAKER\n");
sbdsp.speaker_on = false;
sbdsp.current_command=0;
sbdsp.current_command = 0;
break;
case DSP_SPEAKER_STATUS:
if(sbdsp.current_command_index==0) {
sbdsp.current_command=0;
if (sbdsp.current_command_index == 0) {
sbdsp.current_command = 0;
sbdsp_output(sbdsp.speaker_on ? 0xff : 0x00);
}
break;
case DSP_DIRECT_DAC:
if(sbdsp.dav_dsp) {
if(sbdsp.current_command_index==1) {
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
#ifdef SB_BUFFERLESS
sbdsp.cur_sample = scale_sample(((int16_t)(int8_t)(sbdsp.inbox ^ 0x80)) << 8, sb_volume, 0);
int16_t s = scale_sample(((int16_t)(int8_t)(sbdsp.inbox ^ 0x80)) << 8, sb_volume, 0);
sbdsp.cur_sample = (uint32_t)(uint16_t)s | ((uint32_t)(uint16_t)s << 16);
#endif
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
}
sbdsp.current_command_index++;
}
break;
//case DSP_DIRECT_ADC:
//case DSP_MIDI_READ_POLL:
//case DSP_MIDI_WRITE_POLL:
//case DSP_HALT_DMA:
case DSP_WRITETEST:
if(sbdsp.dav_dsp) {
if(sbdsp.current_command_index==1) {
//printf("(0xE4) DSP_WRITETEST\n\r");
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.test_register = sbdsp.inbox;
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
}
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
}
sbdsp.current_command_index++;
}
}
break;
case DSP_READTEST:
if(sbdsp.current_command_index==0) {
sbdsp.current_command=0;
if (sbdsp.current_command_index == 0) {
sbdsp.current_command = 0;
sbdsp_output(sbdsp.test_register);
}
break;
case DSP_DAC_PAUSE_DURATION:
if(sbdsp.dav_dsp) {
if(sbdsp.current_command_index==1) {
sbdsp.dac_pause_duration_low=sbdsp.inbox;
sbdsp.dav_dsp=0;
}
else if(sbdsp.current_command_index==2) {
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.dac_pause_duration_low = sbdsp.inbox;
sbdsp.dav_dsp = 0;
} else if (sbdsp.current_command_index == 2) {
sbdsp.dac_pause_duration = sbdsp.dac_pause_duration_low + (sbdsp.inbox << 8);
sbdsp.dac_resume_pending = true;
// When the specified duration elapses, the DSP generates an interrupt.
PIC_AddEvent(&DSP_DAC_Resume_event, sbdsp.dma_interval * sbdsp.dac_pause_duration, 0);
sbdsp.dav_dsp=0;
sbdsp.current_command=0;
//printf("(0x80) Pause Duration:%u\n\r",sbdsp.dac_pause_duration);
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
}
sbdsp.current_command_index++;
}
break;
//case DSP_SINE:
//case DSP_CHECKSUM:
break;
case DSP_8051_READ:
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
sbdsp_output(sb_8051_ram[sbdsp.inbox]);
}
sbdsp.current_command_index++;
}
break;
case DSP_8051_WRITE:
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sb_8051_ram[0xff] = sbdsp.inbox; // stash address in unused location
sbdsp.dav_dsp = 0;
} else if (sbdsp.current_command_index == 2) {
sb_8051_ram[sb_8051_ram[0xff]] = sbdsp.inbox;
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
}
sbdsp.current_command_index++;
}
break;
case DSP_DMA_IO_START ... DSP_DMA_IO_END:
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 0) { // bCommand + bMode
sbdsp.dma_16bit = (sbdsp.current_command & 0xf0) == 0xb0;
sbdsp.autoinit = sbdsp.current_command & 0x4;
sbdsp.dma_signed = sbdsp.inbox & 0x10;
sbdsp.dma_stereo = sbdsp.inbox & 0x20;
sbdsp.dav_dsp = 0;
} else if (sbdsp.current_command_index == 1) { // wLength.LowByte
sbdsp.dma_sample_count = sbdsp.inbox;
sbdsp.dav_dsp = 0;
} else if (sbdsp.current_command_index == 2) { // wLength.HighByte
sbdsp.dma_sample_count |= (sbdsp.inbox << 8);
// SB16: sample_count is per-channel samples minus 1.
// With dma_write_multi, each event transfers one complete frame,
// so xfer_count counts frames (= sample_count + 1).
sbdsp.dma_bytes_per_frame = 1;
if (sbdsp.dma_16bit) {
sbdsp.dma_bytes_per_frame <<= 1;
}
if (sbdsp.dma_stereo) {
sbdsp.dma_bytes_per_frame <<= 1;
}
sbdsp.dma_xfer_count = sbdsp.dma_sample_count + 1;
sbdsp.dma_xfer_count_left = sbdsp.dma_xfer_count;
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
sbdsp.dma_done = false;
sbdsp.speaker_on = true;
sbdsp_set_dma_interval();
sbdsp_dma_enable();
}
sbdsp.current_command_index++;
}
break;
case DSP_ASP_SET_CODEC_PARAM:
case DSP_ASP_SET_REGISTER:
// 2 data bytes, accept and ignore (no ASP on ViBRA)
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.dav_dsp = 0;
} else if (sbdsp.current_command_index == 2) {
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
}
sbdsp.current_command_index++;
}
break;
case DSP_ASP_GET_REGISTER:
// 1 data byte. No ASP present: 0x83 returns 0xFF, others 0x00.
if (sbdsp.dav_dsp) {
if (sbdsp.current_command_index == 1) {
sbdsp.dav_dsp = 0;
sbdsp.current_command = 0;
sbdsp_output(sbdsp.inbox == 0x83 ? 0xFF : 0x00);
}
sbdsp.current_command_index++;
}
break;
case 0:
//not in a command
break;
default:
printf("Unknown Command: %x\n",sbdsp.current_command);
sbdsp.current_command=0;
break;
}
sbdsp.dsp_busy=0;
default:
printf("Unknown Command: %x\n", sbdsp.current_command);
sbdsp.current_command = 0;
break;
}
sbdsp.dsp_busy = 0;
}
static uint32_t DSP_Reset_EventHandler(Bitu val) {
sbdsp.reset_state=0;
sbdsp.reset_state = 0;
sbdsp.outbox = 0xAA;
sbdsp.dav_pc=1;
sbdsp.current_command=0;
sbdsp.current_command_index=0;
sbdsp.dav_pc = 1;
sbdsp.current_command = 0;
sbdsp.current_command_index = 0;
sbdsp.dma_block_size=0x7FF; //default per 2.01
sbdsp.dma_sample_count=0;
sbdsp.dma_sample_count_rx=0;
sbdsp.dma_block_size = 0x7FF; //default per 2.01
sbdsp.dma_xfer_count = 0;
sbdsp.dma_xfer_count_left = 0;
sbdsp.dma_stereo = false;
sbdsp.dma_signed = false;
sbdsp.speaker_on = false;
sbdsp.dma_done = false;
sbdsp.dac_resume_pending = false;
return 0;
}
@@ -452,23 +674,21 @@ static PIC_TimerEvent DSP_Reset_Event = {
};
static __force_inline void sbdsp_reset(uint8_t value) {
//TODO: COLDBOOT ? WARMBOOT ?
value &= 1; // Some games may write unknown data for bits other than the LSB.
switch(value) {
case 1:
PIC_RemoveEvent(&DSP_Reset_Event);
sbdsp.autoinit=0;
sbdsp_dma_disable();
switch (value) {
case 1:
PIC_RemoveEvent(&DSP_Reset_Event);
sbdsp.autoinit = 0;
sbdsp_dma_disable(false);
#ifndef SB_BUFFERLESS
sbdsp_fifo_clear();
#endif
sbdsp.reset_state=1;
sbdsp.reset_state = 1;
break;
case 0:
if(sbdsp.reset_state==1) {
sbdsp.dav_pc=0;
// sbdsp.outbox = 0xAA;
PIC_RemoveEvent(&DSP_Reset_Event);
if (sbdsp.reset_state == 1) {
sbdsp.dav_pc = 0;
PIC_RemoveEvent(&DSP_Reset_Event);
PIC_AddEvent(&DSP_Reset_Event, 100, 0);
sbdsp.reset_state = 2;
}
@@ -478,35 +698,85 @@ static __force_inline void sbdsp_reset(uint8_t value) {
}
}
uint8_t sbdsp_read(uint8_t address) {
uint8_t x;
switch(address) {
case DSP_READ:
sbdsp.dav_pc=0;
return sbdsp.outbox;
case DSP_READ_STATUS: //e
PIC_DeActivateIRQ();
return sbdsp.dav_pc << 7 | DSP_UNUSED_STATUS_BITS_PULLED_HIGH;
case DSP_WRITE_STATUS://c
return (sbdsp.dav_dsp | sbdsp.dsp_busy | sbdsp.dac_resume_pending) << 7 | DSP_UNUSED_STATUS_BITS_PULLED_HIGH;
static __force_inline uint8_t sbmixer_read(void) {
switch (sbdsp.mixer_command) {
case MIXER_INTERRUPT:
return sbdsp.interrupt;
case MIXER_DMA:
return sbdsp.dma;
case MIXER_IRQ_STATUS:
// Bits 0-1: 8/16-bit IRQ pending. Bit 5: SB16 type identifier.
return (sbdsp.irq_8_pending ? 0x01 : 0) | (sbdsp.irq_16_pending ? 0x02 : 0) | 0x20;
default:
//printf("SB READ: %x\n\r",address);
return 0xFF;
return mixer_state[sbdsp.mixer_command];
}
}
void sbdsp_write(uint8_t address, uint8_t value) {
switch(address) {
static __force_inline void sbmixer_write(uint8_t value) {
switch (sbdsp.mixer_command) {
case 0x00: // Mixer reset
memset(mixer_state, 0, sizeof(mixer_state));
break;
case MIXER_INTERRUPT:
// sbdsp.interrupt = value;
break;
case MIXER_DMA:
// sbdsp.dma = value;
break;
case MIXER_STEREO:
sbdsp.dma_stereo = value & 2;
// no break
default:
mixer_state[sbdsp.mixer_command] = value;
break;
}
}
uint8_t sbdsp_read(uint8_t address) {
switch (address) {
case DSP_READ:
sbdsp.dav_pc = 0;
return sbdsp.outbox;
case DSP_READ_STATUS: //e
if (sbdsp.irq_8_pending) {
sbdsp.irq_8_pending = false;
PIC_DeActivateIRQ();
}
return sbdsp.dav_pc << 7 | DSP_UNUSED_STATUS_BITS_PULLED_HIGH;
case DSP_IRQ_16_STATUS:
if (sbdsp.irq_16_pending) {
sbdsp.irq_16_pending = false;
PIC_DeActivateIRQ();
}
return 0xff;
case DSP_WRITE_STATUS://c
if (sbdsp.reset_state)
return 0xFF; // busy during reset
return (sbdsp.dav_dsp | sbdsp.dsp_busy | sbdsp.dac_resume_pending) << 7 | DSP_UNUSED_STATUS_BITS_PULLED_HIGH;
case MIXER_DATA:
return sbmixer_read();
default:
return 0xFF;
}
}
void sbdsp_write(uint8_t address, uint8_t value) {
switch (address) {
case DSP_WRITE://c
if(sbdsp.dav_dsp) printf("WARN - DAV_DSP OVERWRITE\n");
if (sbdsp.dav_dsp) printf("WARN - DAV_DSP OVERWRITE\n");
sbdsp.inbox = value;
sbdsp.dav_dsp = 1;
break;
sbdsp.dav_dsp = 1;
break;
case DSP_RESET:
sbdsp_reset(value);
break;
break;
case MIXER_COMMAND:
sbdsp.mixer_command = value;
break;
case MIXER_DATA:
sbmixer_write(value);
break;
default:
//printf("SB WRITE: %x => %x \n\r",value,address);
break;
}
}
+23 -9
View File
@@ -41,7 +41,9 @@ typedef struct sbdsp_t {
uint8_t current_command;
uint8_t current_command_index;
uint16_t dma_interval;
uint8_t mixer_command;
uint16_t dma_interval;
int16_t dma_interval_trim;
audio_fifo_t audio_fifo;
@@ -49,26 +51,38 @@ typedef struct sbdsp_t {
uint8_t dac_pause_duration_low;
uint16_t dma_block_size;
uint32_t dma_sample_count;
uint32_t dma_bytes_per_frame;
uint16_t dma_sample_count;
uint32_t dma_xfer_count;
uint32_t dma_xfer_count_left;
uint32_t dma_sample_count_rx;
uint8_t time_constant;
uint16_t sample_rate;
bool autoinit;
bool autoinit;
bool dma_enabled;
bool dma_16bit;
bool dma_stereo;
bool dma_signed;
volatile bool dma_done;
bool speaker_on;
volatile bool dav_pc;
volatile bool dav_dsp;
volatile bool dsp_busy;
bool dac_resume_pending;
volatile bool irq_8_pending;
volatile bool irq_16_pending;
uint8_t interrupt;
uint8_t dma;
uint8_t reset_state;
uint8_t reset_state;
#ifdef SB_BUFFERLESS
volatile int16_t cur_sample;
volatile uint32_t cur_sample; // packed stereo pair: L in low 16, R in high 16
#endif
} sbdsp_t;
@@ -80,7 +94,7 @@ uint16_t sbdsp_sample_rate();
int16_t sbdsp_muted();
#ifdef SB_BUFFERLESS
static inline int16_t sbdsp_sample() {
static inline uint32_t sbdsp_sample_stereo() {
extern sbdsp_t sbdsp;
return (sbdsp.speaker_on & ~sbdsp.dac_resume_pending) ? sbdsp.cur_sample : 0;
}
+17 -1
View File
@@ -37,7 +37,11 @@
#include "audio/audio_fifo.h"
#if SOUND_SB
#ifdef SOUND_WSS
#include "ad1848/ad1848.h"
#else
#include "sbdsp/sbdsp.h"
#endif
#endif // SOUND_SB
#if defined(SOUND_SB) || defined(USB_MOUSE) || defined(SOUND_MPU)
#include "system/pico_pic.h"
@@ -127,7 +131,15 @@ void audio_sample_handler(void) {
int32_t sample_l = 0, sample_r = 0;
#ifdef SOUND_SB
sample_l = sample_r = sbdsp_sample();
{
#ifdef SOUND_WSS
uint32_t card_stereo = ad1848_sample_stereo();
#else
uint32_t card_stereo = sbdsp_sample_stereo();
#endif
sample_l = (int16_t)(card_stereo & 0xFFFF);
sample_r = (int16_t)(card_stereo >> 16);
}
#endif
#ifdef CDROM
@@ -181,7 +193,11 @@ void play_adlib() {
#endif
#if SOUND_SB
#ifdef SOUND_WSS
ad1848_init();
#else
sbdsp_init();
#endif
#endif
init_audio();