1
0
Fork 0

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
This commit is contained in:
Sjoerd van der Berg 2019-10-31 20:00:08 +00:00
parent 0622d45e38
commit 95ac285db1

View file

@ -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();