diff --git a/src/hardware/sblaster.cpp b/src/hardware/sblaster.cpp index 864e52c0..d2a44eb2 100644 --- a/src/hardware/sblaster.cpp +++ b/src/hardware/sblaster.cpp @@ -16,6 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include "dosbox.h" #include "inout.h" @@ -26,11 +27,12 @@ #include "setup.h" #include "programs.h" - #define SB_PIC_EVENTS 0 +#define SB_NEW_ADPCM 1 -#define DSP_MAJOR 2 -#define DSP_MINOR 0 + +#define DSP_MAJOR 3 +#define DSP_MINOR 1 #define MIXER_INDEX 0x04 #define MIXER_DATA 0x05 @@ -51,6 +53,8 @@ #define SB_BUF_SIZE 8096 enum {DSP_S_RESET,DSP_S_NORMAL,DSP_S_HIGHSPEED}; +enum SB_IRQS {SB_IRQ_8,SB_IRQ_16,SB_IRQ_MPU}; + enum DSP_MODES { MODE_NONE,MODE_DAC, @@ -63,6 +67,7 @@ enum DMA_MODES { DMA_8_SILENCE, DMA_4_SINGLE, DMA_8_SINGLE,DMA_8_AUTO, + DMA_16_SINGLE,DMA_16_AUTO, }; enum { @@ -72,7 +77,7 @@ enum { struct SB_INFO { Bit16u freq; struct { - bool active,stereo; + bool active,stereo,filtered; DMA_MODES mode; Bitu total,left; Bitu rate,rate_mul; @@ -87,6 +92,10 @@ struct SB_INFO { Bit8u time_constant; bool use_time_constant; DSP_MODES mode; + struct { + bool pending_8bit; + bool pending_16bit; + } irq; struct { Bit8u state; Bit8u cmd; @@ -107,11 +116,13 @@ struct SB_INFO { } dac; struct { Bit8u index; - Bit8u master; - Bit8u mic,voice,fm; + struct { + Bit8u left,right; + } dac,fm,cda,master,lin; + Bit8u mic; } mixer; struct { - Bits reference,scale; + Bits reference,stepsize; } adpcm; struct { Bitu base; @@ -189,6 +200,19 @@ static void DSP_SetSpeaker(bool how) { sb.speaker=how; } +static INLINE void SB_RaiseIRQ(SB_IRQS type) { + LOG(LOG_SB,LOG_NORMAL)("Raising IRQ"); + PIC_AddIRQ(sb.hw.irq,0); + switch (type) { + case SB_IRQ_8: + sb.irq.pending_8bit=true; + break; + case SB_IRQ_16: + sb.irq.pending_16bit=true; + break; + } +} + static INLINE void DSP_FlushData(void) { sb.dsp.out.used=0; sb.dsp.out.pos=0; @@ -202,15 +226,52 @@ static void DSP_StopDMA(void) { static void DMA_Enable(bool enable) { sb.dma.active=enable; if (sb.mode==MODE_DMA_WAIT && enable) { + LOG(LOG_SB,LOG_NORMAL)("DMA enabled,starting output"); DSP_ChangeMode(MODE_DMA); CheckDMAEnd(); + return; } if (sb.mode==MODE_DMA && !enable) { DSP_ChangeMode(MODE_DMA_WAIT); + LOG(LOG_SB,LOG_NORMAL)("DMA disabled,stopping output"); + return; } } -INLINE Bit8u decode_ADPCM_4_sample(Bit8u sample,Bits& reference,Bits& scale) { + +#define MIN_ADAPTIVE_STEP_SIZE 511 +#define MAX_ADAPTIVE_STEP_SIZE 32767 +#define DC_OFFSET_FADE 254 + +INLINE Bits Clip(Bits sample) { + if (sample>MAX_AUDIO) return MAX_AUDIO; + if (sample> 1)); + if (adpcm & 1 ) scale = Clip(scale + (stepsize >> 2)); + scale = Clip(stepsize >> 3); + if (adpcm & 8) scale = -scale; + + reference=Clip(scale+((reference * DC_OFFSET_FADE) >> 8)); + // compute the next step size + stepsize=(stepsize * quantize[adpcm & 0x7]) >> 8; + if (stepsize < MIN_ADAPTIVE_STEP_SIZE) stepsize=MIN_ADAPTIVE_STEP_SIZE; + else if (stepsize > MAX_ADAPTIVE_STEP_SIZE) stepsize=MAX_ADAPTIVE_STEP_SIZE; + + return (Bit16s)reference; +} + +#else +INLINE Bit16s decode_ADPCM_4_sample(Bit8u sample,Bits& reference,Bits& scale) { static Bits scaleMap[8] = { -2, -1, 0, 0, 1, 1, 1, 1 }; if (sample & 0x08) { @@ -219,61 +280,77 @@ INLINE Bit8u decode_ADPCM_4_sample(Bit8u sample,Bits& reference,Bits& scale) { reference = min(0xff, reference + ((sample & 0x07) << scale)); } scale = max(2, min(6, scaleMap[sample & 0x07])); - return (Bit8u)reference; + return (((Bit8s)reference)^0x80)<<8; } +#endif + static void GenerateDMASound(Bitu size) { - //TODO For stereo make sure it's multiple of 2 + /* Check some variables */ if (!size) return; - Bitu read,i; + if (!sb.dma.rate) return; + /* First check if this transfer is gonna end in the next 2 milliseconds */ + Bitu index=(Bitu)(((float)sb.dma.left*(float)1000000)/(float)sb.dma.rate); +#if (SB_PIC_EVENTS) + if (index-PIC_Index()<1000) PIC_AddEvent(END_DMA_Event,index); +#else + if (index<2000) size=sb.dma.left; +#endif + Bitu read,i; switch (sb.dma.mode) { case DMA_4_SINGLE: - if (sb.adpcm.reference<0 && sb.dma.left) { + if (sb.adpcm.reference==0x1000000 && sb.dma.left) { +//TODO Check this Bit8u ref; read=DMA_8_Read(sb.hw.dma8,&ref,1); if (!read) { sb.mode=MODE_NONE;return; } //TODO warnings? sb.dma.left--; - sb.adpcm.reference=ref; + sb.adpcm.reference=0; + sb.adpcm.stepsize=MIN_ADAPTIVE_STEP_SIZE; } if (sb.dma.left> 4,sb.adpcm.reference,sb.adpcm.scale)^0x80))<<8; - sb.tmp.buf.m[i*2+1]=((Bit8s)(decode_ADPCM_4_sample(sb.dma.buf.b8[i] & 0xf,sb.adpcm.reference,sb.adpcm.scale)^0x80))<<8; + sb.tmp.buf.m[i*2+0]=decode_ADPCM_4_sample(sb.dma.buf.b8[i] >> 4,sb.adpcm.reference,sb.adpcm.stepsize); + sb.tmp.buf.m[i*2+1]=decode_ADPCM_4_sample(sb.dma.buf.b8[i] & 0xf,sb.adpcm.reference,sb.adpcm.stepsize); } read*=2; break; case DMA_8_SINGLE: if (sb.dma.leftread) { sb.dma.left-=read; } else { sb.dma.left=(sb.dma.total+sb.dma.left)-read; - PIC_AddIRQ(sb.hw.irq,0); + SB_RaiseIRQ(SB_IRQ_8); +// LOG_MSG("SB DMA AUTO IRQ Raised"); } for (i=0;i(pos=sb.tmp.index>>16)) { - (*stream++)=sb.tmp.buf.s[pos][0]; - (*stream++)=sb.tmp.buf.s[pos][1]; - sb.tmp.index+=sb.tmp.add_index; - sb.out.pos++; + if (read&1){ + LOG_MSG("DMA Unaligned"); + } else { + Bitu pos;read>>=1;Bitu index_add=sb.tmp.add_index >> 1; + while (read>(pos=sb.tmp.index>>16)) { + (*stream++)=sb.tmp.buf.s[pos][1]; //SB default seems to be swapped + (*stream++)=sb.tmp.buf.s[pos][0]; + sb.tmp.index+=index_add; + sb.out.pos++; + } + sb.tmp.index&=0xffff; } - sb.tmp.index&=0xffff; } } @@ -337,6 +418,7 @@ static void GenerateSound(Bitu size) { Bitu len=size*sb.dma.rate_mul; if (len & 0xffff) len=1+(len>>16); else len>>=16; + if (sb.dma.stereo && (len & 1)) len++; GenerateDMASound(len); break; } @@ -351,15 +433,14 @@ static void GenerateSound(Bitu size) { static void END_DMA_Event(void) { GenerateDMASound(sb.dma.left); - sb.dma.left=0; - DSP_ChangeMode(MODE_NONE); } static void CheckDMAEnd(void) { if (!sb.dma.rate) return; Bitu index=(Bitu)(((float)sb.dma.left*(float)1000000)/(float)sb.dma.rate); if (index<(1000-PIC_Index())) { -#if SB_PIC_EVENTS +#if 1 + LOG(LOG_SB,LOG_NORMAL)("Sub millisecond transfer scheduling IRQ in %d microseconds",index); PIC_AddEvent(END_DMA_Event,index); #else GenerateDMASound(sb.dma.left); @@ -386,7 +467,6 @@ static void DSP_StartDMATranfser(DMA_MODES mode) { sb.dma.rate=(1000000 / (256 - sb.time_constant)); }; sb.dma.rate_mul=(sb.dma.rate<<16)/sb.hw.rate; - sb.dma.mode=mode; sb.tmp.index=0; switch (mode) { @@ -394,7 +474,6 @@ static void DSP_StartDMATranfser(DMA_MODES mode) { PIC_AddIRQ(sb.hw.irq,((1000000*sb.dma.left)/sb.dma.rate)); sb.dma.mode=DMA_NONE; return; - break; case DMA_8_SINGLE: type="8-Bit Single Cycle"; sb.tmp.add_index=(sb.dma.rate<<16)/sb.hw.rate; @@ -438,6 +517,7 @@ static void DSP_Reset(void) { sb.dsp.write_busy=0; sb.dma.left=0; sb.dma.total=0; + sb.dma.stereo=false; sb.freq=22050; sb.use_time_constant=true; sb.time_constant=45; @@ -445,12 +525,14 @@ static void DSP_Reset(void) { sb.dac.last=0; sb.e2.value=0xaa; sb.e2.count=0; + sb.irq.pending_8bit=false; + sb.irq.pending_16bit=false; DSP_SetSpeaker(false); } static void DSP_DoReset(Bit8u val) { - if (val&1!=0) { + if ((val&1)!=0) { //TODO Get out of highspeed mode DSP_Reset(); sb.dsp.state=DSP_S_RESET; @@ -467,12 +549,16 @@ static void DMA_E2_Enable(bool enable) { Bit8u val=sb.e2.value; DMA_8_Write(sb.hw.dma8,&val,1); DMA_SetEnableCallBack(sb.hw.dma8,0); -// PIC_AddIRQ(sb.hw.irq,0); } } static void DSP_DoCommand(void) { + //LOG_MSG("DSP Command %X",sb.dsp.cmd); switch (sb.dsp.cmd) { + case 0x04: /* DSP Statues SB 2.0/pro version */ + DSP_FlushData(); + DSP_AddData(0xff); //Everthing enabled + break; case 0x10: /* Direct DAC */ DSP_ChangeMode(MODE_DAC); if (sb.dac.used> 4) << 1; break; - case 0x22: /* Master Volume */ - sb.mixer.master=val; + case 0x04: /* DAC Volume (SBPRO) */ + sb.mixer.dac.left= (val & 0xf) << 1; + sb.mixer.dac.right=(val >> 4) << 1; + break; + case 0x06: /* FM output selection, Somewhat obsolete with dual OPL SBpro */ + sb.mixer.fm.left= (val & 0xf) << 1; + sb.mixer.fm.right=(val & 0xf) << 1; + //TODO Change FM Mode if only 1 fm channel is selected + break; + case 0x0a: /* Mic Level */ + sb.mixer.mic=(val & 0xf) << 1; + break; + case 0x0e: /* Output/Stereo Select */ + sb.dma.stereo=(val & 0x2) > 0; + sb.dma.filtered=(val & 0x20) > 0; + LOG(LOG_SB,LOG_WARN)("Mixer set to %s",sb.dma.stereo ? "STEREO" : "MONO"); + break; + case 0x26: /* FM Volume (SBPRO) */ + sb.mixer.fm.left= (val & 0xf) << 1; + sb.mixer.fm.right=(val >> 4) << 1; + break; + case 0x28: /* CD Audio Volume (SBPRO) */ + sb.mixer.cda.left= (val & 0xf) << 1; + sb.mixer.cda.right=(val >> 4) << 1; + break; + case 0x2e: /* Line-IN Volume (SBPRO) */ + sb.mixer.lin.left= (val & 0xf) << 1; + sb.mixer.lin.right=(val >> 4) << 1; break; default: - LOG(LOG_SB,LOG_ERROR)("MIXER:Write to unhandled index %X",sb.mixer.index); + LOG(LOG_SB,LOG_WARN)("MIXER:Write %X to unhandled index %X",val,sb.mixer.index); } } static Bit8u MIXER_Read(void) { Bit8u ret; switch (sb.mixer.index) { + case 0x00: /* RESET */ + return 0x00; + case 0x02: /* Master Voulme (SBPRO) Obsolete? */ + case 0x22: /* Master Volume (SBPRO) */ + return ((sb.mixer.master.left & 0x1e) >> 1) | + ((sb.mixer.master.right & 0x1e) << 3); + case 0x04: /* DAC Volume (SBPRO) */ + return ((sb.mixer.dac.left & 0x1e) >> 1) | + ((sb.mixer.dac.right & 0x1e) << 3); +// case 0x06: /* FM output selection, Somewhat obsolete with dual OPL SBpro */ case 0x0a: /* Mic Level */ - ret=sb.mixer.mic; - case 0x22: /* Master Volume */ - ret=sb.mixer.master; - break; - default: - LOG(LOG_SB,LOG_ERROR)("MIXER:Read from unhandled index %X",sb.mixer.index); - ret=0xff; + return sb.mixer.mic; + case 0x0e: /* Output/Stereo Select */ + return 0x11|(sb.dma.stereo ? 0x02 : 0x00)|(sb.dma.filtered ? 0x20 : 0x00); + case 0x26: /* FM Volume (SBPRO) */ + return ((sb.mixer.fm.left & 0x1e) >> 1) | + ((sb.mixer.fm.right & 0x1e) << 3); + case 0x28: /* CD Audio Volume (SBPRO) */ + return ((sb.mixer.cda.left & 0x1e) >> 1) | + ((sb.mixer.cda.right & 0x1e) << 3); + case 0x2e: /* Line-IN Volume (SBPRO) */ + return ((sb.mixer.lin.left & 0x1e) >> 1) | + ((sb.mixer.lin.right & 0x1e) << 3); + case 0x82: + return (sb.irq.pending_8bit ? 0x1 : 0) | + (sb.irq.pending_16bit ? 0x2 : 0); + default: /* IRQ Status */ + LOG(LOG_SB,LOG_WARN)("MIXER:Read from unhandled index %X",sb.mixer.index); + ret=0xa; } return ret; } @@ -639,8 +773,8 @@ static Bit8u read_sb(Bit32u port) { case DSP_READ_DATA: return DSP_ReadData(); case DSP_READ_STATUS: - //TODO Acknowledge 8bit irq //TODO See for high speed dma :) + sb.irq.pending_8bit=false; if (sb.dsp.out.used) return 0xff; else return 0x7f; case DSP_WRITE_STATUS: @@ -730,5 +864,5 @@ void SBLASTER_Init(Section* sec) { PIC_RegisterIRQ(sb.hw.irq,0,"SB"); DSP_Reset(); - SHELL_AddAutoexec("SET BLASTER=A%3X I%d D%d T3",sb.hw.base,sb.hw.irq,sb.hw.dma8); + SHELL_AddAutoexec("SET BLASTER=A%3X I%d D%d T4",sb.hw.base,sb.hw.irq,sb.hw.dma8); }