From 95ac285db1f2fc67138033c27616fb8d60d22407 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Thu, 31 Oct 2019 20:00:08 +0000 Subject: [PATCH] Fix sblaster autoinit restart and improve the auto into single cycle transfer handling Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@4278 --- src/hardware/sblaster.cpp | 92 ++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/src/hardware/sblaster.cpp b/src/hardware/sblaster.cpp index e4a9b029..9944e66f 100644 --- a/src/hardware/sblaster.cpp +++ b/src/hardware/sblaster.cpp @@ -90,7 +90,10 @@ struct SB_INFO { bool stereo,sign,autoinit; DMA_MODES mode; Bitu rate,mul; - Bitu total,left,min; + Bit32u singlesize; //size for single cycle transfers + Bit32u autosize; //size for auto init transfers + Bitu left; //Left in active cycle + Bitu min; Bit64u start; union { Bit8u b8[DMA_BUFSIZE]; @@ -298,8 +301,6 @@ static void DSP_DMA_CallBack(DmaChannel * chan, DMAEvent event) { // DSP_ChangeMode(MODE_DMA_MASKED); LOG(LOG_SB,LOG_NORMAL)("DMA masked,stopping output, left %d",chan->currcnt); } - } else if (event==DMA_TRANSFEREND) { - if (sb.mode==MODE_DMA) sb.mode=MODE_DMA_MASKED; } else if (event==DMA_UNMASKED) { if (sb.mode==MODE_DMA_MASKED && sb.dma.mode!=DSP_DMA_NONE) { DSP_ChangeMode(MODE_DMA); @@ -308,6 +309,9 @@ static void DSP_DMA_CallBack(DmaChannel * chan, DMAEvent event) { LOG(LOG_SB,LOG_NORMAL)("DMA unmasked,starting output, auto %d block %d",chan->autoinit,chan->basecnt); } } + else { + E_Exit("Unknown sblaster dma event"); + } } #define MIN_ADAPTIVE_STEP_SIZE 0 @@ -414,6 +418,7 @@ static void GenerateDMASound(Bitu size) { Bitu read=0;Bitu done=0;Bitu i=0; last_dma_callback = PIC_FullIndex(); + //Determine how much you should read if(sb.dma.autoinit) { if (sb.dma.left <= size) size = sb.dma.left; @@ -422,6 +427,7 @@ static void GenerateDMASound(Bitu size) { size = sb.dma.left; } + //Read the actual data, process it and send it off to the mixer switch (sb.dma.mode) { case DSP_DMA_2: read=sb.dma.chan->Read(size,sb.dma.buf.b8); @@ -523,6 +529,7 @@ static void GenerateDMASound(Bitu size) { sb.mode=MODE_NONE; return; } + //Check how many bytes were actually read sb.dma.left-=read; if (!sb.dma.left) { PIC_RemoveEvents(END_DMA_Event); @@ -530,24 +537,28 @@ static void GenerateDMASound(Bitu size) { SB_RaiseIRQ(SB_IRQ_16); else SB_RaiseIRQ(SB_IRQ_8); - //Copy the new size - sb.dma.left = sb.dma.total; + if (!sb.dma.autoinit) { - if (!sb.dma.left) { + //Not new single cycle transfer waiting? + if (!sb.dma.singlesize) { LOG(LOG_SB, LOG_NORMAL)("Single cycle transfer ended"); sb.mode = MODE_NONE; sb.dma.mode = DSP_DMA_NONE; } else { - //Copied this value as the count for the final single cycle - sb.dma.total = 0; + //A single size transfer is still waiting, handle that now + sb.dma.left = sb.dma.singlesize; + sb.dma.singlesize = 0; LOG(LOG_SB, LOG_NORMAL)("Switch to Single cycle transfer begun"); } } else { - if (!sb.dma.left) { + if (!sb.dma.autosize) { LOG(LOG_SB,LOG_NORMAL)("Auto-init transfer with 0 size"); sb.mode=MODE_NONE; } + //Continue with a new auto init transfer + sb.dma.left = sb.dma.autosize; + } } } @@ -577,8 +588,9 @@ static void DMA_Silent_Event(Bitu val) { if (!sb.dma.left) { if (sb.dma.mode >= DSP_DMA_16) SB_RaiseIRQ(SB_IRQ_16); else SB_RaiseIRQ(SB_IRQ_8); + //FIX, use the auto to single switch mechanics here as well or find a better way to silence if (sb.dma.autoinit) - sb.dma.left=sb.dma.total; + sb.dma.left=sb.dma.autosize; else { sb.mode=MODE_NONE; sb.dma.mode=DSP_DMA_NONE; @@ -589,7 +601,6 @@ static void DMA_Silent_Event(Bitu val) { float delay=(bigger*1000.0f)/sb.dma.rate; PIC_AddEvent(DMA_Silent_Event,delay,bigger); } - } static void END_DMA_Event(Bitu val) { @@ -660,29 +671,20 @@ static void DSP_DoDMATransfer(DMA_MODES mode,Bitu freq,bool autoinit, bool stere return; } -#if (C_DEBUG) - LOG(LOG_SB, LOG_NORMAL)("DMA Transfer:%s %s %s freq %d rate %d size %d", - type, - stereo ? "Stereo" : "Mono", - autoinit ? "Auto-Init" : "Single-Cycle", - freq, sb.dma.rate, sb.dma.total - ); -#endif //Going from an active autoinit into a single cycle if (sb.mode >= MODE_DMA && sb.dma.autoinit && !autoinit) { //Don't do anything, the total will flip over on the next transfer } //Just a normal single cycle transfer else if (!autoinit) { - sb.dma.left = sb.dma.total; - sb.dma.total = 0; + sb.dma.left = sb.dma.singlesize; + sb.dma.singlesize = 0; } //Going into an autoinit transfer else { //Transfer full cycle again - sb.dma.left = sb.dma.total; + sb.dma.left = sb.dma.autosize; } - sb.dma.autoinit = autoinit; sb.dma.mode = mode; sb.dma.stereo = stereo; @@ -697,26 +699,36 @@ static void DSP_DoDMATransfer(DMA_MODES mode,Bitu freq,bool autoinit, bool stere //Set to be masked, the dma call can change this again. sb.mode = MODE_DMA_MASKED; sb.dma.chan->Register_Callback(DSP_DMA_CallBack); + +#if (C_DEBUG) + LOG(LOG_SB, LOG_NORMAL)("DMA Transfer:%s %s %s freq %d rate %d size %d", + type, + stereo ? "Stereo" : "Mono", + autoinit ? "Auto-Init" : "Single-Cycle", + freq, sb.dma.rate, sb.dma.left + ); +#endif } static void DSP_PrepareDMA_Old(DMA_MODES mode,bool autoinit,bool sign) { sb.dma.sign=sign; - if (!autoinit) sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); + if (!autoinit) + sb.dma.singlesize=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); sb.dma.chan=GetDMAChannel(sb.hw.dma8); DSP_DoDMATransfer(mode,sb.freq / (sb.mixer.stereo ? 2 : 1), autoinit, sb.mixer.stereo); } static void DSP_PrepareDMA_New(DMA_MODES mode,Bitu length,bool autoinit,bool stereo) { Bitu freq=sb.freq; + //equal length if data format and dma channel are both 16-bit or 8-bit - sb.dma.total=length; if (mode==DSP_DMA_16) { if (sb.hw.dma16!=0xff) { sb.dma.chan=GetDMAChannel(sb.hw.dma16); if (sb.dma.chan==NULL) { sb.dma.chan=GetDMAChannel(sb.hw.dma8); mode=DSP_DMA_16_ALIASED; - sb.dma.total<<=1; + length *= 2; } } else { sb.dma.chan=GetDMAChannel(sb.hw.dma8); @@ -724,9 +736,16 @@ static void DSP_PrepareDMA_New(DMA_MODES mode,Bitu length,bool autoinit,bool ste //UNDOCUMENTED: //In aliased mode sample length is written to DSP as number of //16-bit samples so we need double 8-bit DMA buffer length - sb.dma.total<<=1; + length *= 2; } } else sb.dma.chan=GetDMAChannel(sb.hw.dma8); + //Set the length to the correct register depending on mode + if (autoinit) { + sb.dma.autosize = length; + } + else { + sb.dma.singlesize = length; + } DSP_DoDMATransfer(mode,freq,autoinit,stereo); } @@ -762,7 +781,8 @@ static void DSP_Reset(void) { PIC_RemoveEvents(DSP_FinishReset); sb.dma.left=0; - sb.dma.total=0; + sb.dma.singlesize=0; + sb.dma.autosize = 0; sb.dma.stereo=false; sb.dma.sign=false; sb.dma.autoinit=false; @@ -892,9 +912,10 @@ static void DSP_DoCommand(void) { } break; case 0x24: /* Singe Cycle 8-Bit DMA ADC */ - sb.dma.left=sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); + //Directly write to left? + sb.dma.left = 1 + sb.dsp.in.data[0] + (sb.dsp.in.data[1] << 8); sb.dma.sign=false; - LOG(LOG_SB,LOG_ERROR)("DSP:Faked ADC for %d bytes",sb.dma.total); + LOG(LOG_SB,LOG_ERROR)("DSP:Faked ADC for %d bytes",sb.dma.left); GetDMAChannel(sb.hw.dma8)->Register_Callback(DSP_ADC_CallBack); break; case 0x14: /* Singe Cycle 8-Bit DMA DAC */ @@ -922,8 +943,7 @@ static void DSP_DoCommand(void) { break; case 0x48: /* Set DMA Block Size */ DSP_SB2_ABOVE; - //TODO Maybe check limit for new irq? - sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); + sb.dma.autosize=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8); break; case 0x75: /* 075h : Single Cycle 4-bit ADPCM Reference */ sb.adpcm.haveref=true; @@ -967,6 +987,7 @@ static void DSP_DoCommand(void) { DSP_SB16_ONLY; case 0xd0: /* Halt 8-bit DMA */ // DSP_ChangeMode(MODE_NONE); + LOG(LOG_SB, LOG_NORMAL)("Halt DMA Command"); // Games sometimes already program a new dma before stopping, gives noise if (sb.mode==MODE_NONE) { // possibly different code here that does not switch to MODE_DMA_PAUSE @@ -989,6 +1010,7 @@ static void DSP_DoCommand(void) { case 0xd6: /* Continue DMA 16-bit */ DSP_SB16_ONLY; case 0xd4: /* Continue DMA 8-bit*/ + LOG(LOG_SB, LOG_NORMAL)("Continue DMA command"); if (sb.mode==MODE_DMA_PAUSE) { sb.mode=MODE_DMA_MASKED; if (sb.dma.chan!=NULL) sb.dma.chan->Register_Callback(DSP_DMA_CallBack); @@ -998,10 +1020,8 @@ static void DSP_DoCommand(void) { DSP_SB16_ONLY; case 0xda: /* Exit Autoinitialize 8-bit */ DSP_SB2_ABOVE; - /* Set mode to single transfer so it ends with current block */ + LOG(LOG_SB, LOG_NORMAL)("Exit Autoinit command"); sb.dma.autoinit=false; //Should stop itself - sb.dma.total = 0; //This will cancel the switch to single cycle mode - //Should really have some sb.dma.autoexit variable since we don't support continue autoinit dsp commands break; case 0xe0: /* DSP Identification - SB2.0+ */ DSP_FlushData(); @@ -1052,10 +1072,12 @@ static void DSP_DoCommand(void) { case 0xf2: /* Trigger 8bit IRQ */ //Small delay in order to emulate the slowness of the DSP, fixes Llamatron 2012 and Lemmings 3D PIC_AddEvent(&DSP_RaiseIRQEvent,0.01f); + LOG(LOG_SB, LOG_NORMAL)("Trigger 8bit IRQ command"); break; case 0xf3: /* Trigger 16bit IRQ */ DSP_SB16_ONLY; SB_RaiseIRQ(SB_IRQ_16); + LOG(LOG_SB, LOG_NORMAL)("Trigger 16bit IRQ command"); break; case 0xf8: /* Undocumented, pre-SB16 only */ DSP_FlushData();