dosbox-wii/src/hardware/sblaster.cpp

1439 lines
40 KiB
C++
Raw Normal View History

2009-05-02 23:03:37 +02:00
/*
2009-05-03 00:28:34 +02:00
* Copyright (C) 2002-2007 The DOSBox Team
2009-05-02 23:03:37 +02:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2009-05-03 00:02:15 +02:00
* GNU General Public License for more details.
2009-05-02 23:03:37 +02:00
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
2009-05-03 00:44:30 +02:00
/* $Id: sblaster.cpp,v 1.66 2007/08/08 08:03:48 qbix79 Exp $ */
2009-05-03 00:18:08 +02:00
2009-05-03 00:28:34 +02:00
#include <iomanip>
#include <sstream>
2009-05-02 23:03:37 +02:00
#include <string.h>
2009-05-03 00:02:15 +02:00
#include <math.h>
2009-05-02 23:03:37 +02:00
#include "dosbox.h"
#include "inout.h"
#include "mixer.h"
#include "dma.h"
#include "pic.h"
#include "hardware.h"
2009-05-02 23:20:05 +02:00
#include "setup.h"
2009-05-03 00:02:15 +02:00
#include "support.h"
2009-05-03 00:18:08 +02:00
#include "shell.h"
2009-05-03 00:28:34 +02:00
using namespace std;
2009-05-03 00:18:08 +02:00
void MIDI_RawOutByte(Bit8u data);
bool MIDI_Available(void);
2009-05-02 23:03:37 +02:00
2009-05-02 23:35:44 +02:00
#define SB_PIC_EVENTS 0
2009-05-02 23:43:00 +02:00
#define DSP_MAJOR 3
#define DSP_MINOR 1
2009-05-02 23:35:44 +02:00
#define MIXER_INDEX 0x04
#define MIXER_DATA 0x05
2009-05-02 23:03:37 +02:00
#define DSP_RESET 0x06
#define DSP_READ_DATA 0x0A
#define DSP_WRITE_DATA 0x0C
#define DSP_WRITE_STATUS 0x0C
#define DSP_READ_STATUS 0x0E
2009-05-03 00:02:15 +02:00
#define DSP_ACK_16BIT 0x0f
2009-05-02 23:03:37 +02:00
#define DSP_NO_COMMAND 0
2009-05-03 00:02:15 +02:00
#define DMA_BUFSIZE 1024
2009-05-02 23:03:37 +02:00
#define DSP_BUFSIZE 64
2009-05-03 00:02:15 +02:00
#define DSP_DACSIZE 512
2009-05-02 23:03:37 +02:00
2009-05-02 23:35:44 +02:00
//Should be enough for sound generated in millisecond blocks
#define SB_BUF_SIZE 8096
2009-05-03 00:02:15 +02:00
#define SB_SH 14
#define SB_SH_MASK ((1 << SB_SH)-1)
2009-05-02 23:35:44 +02:00
2009-05-02 23:03:37 +02:00
enum {DSP_S_RESET,DSP_S_NORMAL,DSP_S_HIGHSPEED};
2009-05-03 00:02:15 +02:00
enum SB_TYPES {SBT_NONE=0,SBT_1=1,SBT_PRO1=2,SBT_2=3,SBT_PRO2=4,SBT_16=6};
2009-05-02 23:43:00 +02:00
enum SB_IRQS {SB_IRQ_8,SB_IRQ_16,SB_IRQ_MPU};
2009-05-02 23:35:44 +02:00
enum DSP_MODES {
2009-05-03 00:02:15 +02:00
MODE_NONE,
MODE_DAC,
MODE_DMA,
MODE_DMA_PAUSE,
MODE_DMA_MASKED
2009-05-02 23:03:37 +02:00
};
2009-05-02 23:35:44 +02:00
enum DMA_MODES {
2009-05-03 00:02:15 +02:00
DSP_DMA_NONE,
DSP_DMA_2,DSP_DMA_3,DSP_DMA_4,DSP_DMA_8,
DSP_DMA_16,DSP_DMA_16_ALIASED,
2009-05-02 23:35:44 +02:00
};
2009-05-02 23:03:37 +02:00
2009-05-02 23:35:44 +02:00
enum {
PLAY_MONO,PLAY_STEREO,
};
2009-05-02 23:03:37 +02:00
struct SB_INFO {
2009-05-03 00:02:15 +02:00
Bitu freq;
2009-05-02 23:35:44 +02:00
struct {
2009-05-03 00:02:15 +02:00
bool stereo,sign,autoinit;
2009-05-02 23:35:44 +02:00
DMA_MODES mode;
2009-05-03 00:02:15 +02:00
Bitu rate,mul;
Bitu total,left,min;
2009-05-02 23:35:44 +02:00
Bit64u start;
union {
Bit8u b8[DMA_BUFSIZE];
Bit16s b16[DMA_BUFSIZE];
} buf;
2009-05-03 00:02:15 +02:00
Bitu bits;
DmaChannel * chan;
Bitu remain_size;
2009-05-02 23:35:44 +02:00
} dma;
2009-05-02 23:03:37 +02:00
bool speaker;
2009-05-03 00:18:08 +02:00
bool midi;
2009-05-02 23:03:37 +02:00
Bit8u time_constant;
2009-05-02 23:35:44 +02:00
DSP_MODES mode;
2009-05-03 00:02:15 +02:00
SB_TYPES type;
2009-05-02 23:43:00 +02:00
struct {
bool pending_8bit;
bool pending_16bit;
} irq;
2009-05-02 23:12:18 +02:00
struct {
2009-05-02 23:35:44 +02:00
Bit8u state;
Bit8u cmd;
Bit8u cmd_len;
Bit8u cmd_in_pos;
Bit8u cmd_in[DSP_BUFSIZE];
struct {
Bit8u data[DSP_BUFSIZE];
Bitu pos,used;
} in,out;
Bit8u test_register;
Bitu write_busy;
} dsp;
struct {
2009-05-03 00:02:15 +02:00
Bit16s data[DSP_DACSIZE+1];
2009-05-02 23:12:18 +02:00
Bitu used;
2009-05-02 23:35:44 +02:00
Bit16s last;
2009-05-02 23:12:18 +02:00
} dac;
2009-05-02 23:35:44 +02:00
struct {
Bit8u index;
2009-05-03 00:02:15 +02:00
Bit8u dac[2],fm[2],cda[2],master[2],lin[2];
2009-05-02 23:43:00 +02:00
Bit8u mic;
2009-05-03 00:02:15 +02:00
bool stereo;
bool enabled;
bool filtered;
2009-05-03 00:18:08 +02:00
Bit8u unhandled[0x48];
2009-05-02 23:35:44 +02:00
} mixer;
struct {
2009-05-03 00:02:15 +02:00
Bit8u reference;
Bits stepsize;
bool haveref;
2009-05-02 23:35:44 +02:00
} adpcm;
struct {
Bitu base;
2009-05-03 00:28:34 +02:00
Bitu irq;
Bitu dma8,dma16;
2009-05-02 23:35:44 +02:00
} hw;
struct {
Bits value;
Bitu count;
} e2;
2009-05-03 00:02:15 +02:00
MixerChannel * chan;
2009-05-02 23:03:37 +02:00
};
2009-05-02 23:35:44 +02:00
2009-05-02 23:03:37 +02:00
static SB_INFO sb;
2009-05-03 00:37:32 +02:00
static char const * const copyright_string="COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
2009-05-02 23:03:37 +02:00
static Bit8u DSP_cmd_len[256] = {
2009-05-03 00:28:34 +02:00
// 0,0,0,0, 1,2,0,0, 0,0,0,0, 0,0,2,1, // 0x00 for SB16. but breaks sbpro
0,0,0,0, 0,2,0,0, 0,0,0,0, 0,0,2,1, // 0x00
2009-05-02 23:03:37 +02:00
1,0,0,0, 2,0,2,2, 0,0,0,0, 0,0,0,0, // 0x10
0,0,0,0, 2,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
2009-05-03 00:18:08 +02:00
0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0, // 0x30
2009-05-02 23:03:37 +02:00
1,2,2,0, 0,0,0,0, 2,0,0,0, 0,0,0,0, // 0x40
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x50
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x60
0,0,0,0, 2,2,2,2, 0,0,0,0, 0,0,0,0, // 0x70
2,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x80
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x90
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xa0
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xb0
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xc0
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xd0
1,0,1,0, 1,0,0,0, 0,0,0,0, 0,0,0,0, // 0xe0
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 // 0xf0
};
static int E2_incr_table[4][9] = {
{ 0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106 },
{ -0x01, 0x02, -0x04, 0x08, 0x10, -0x20, 0x40, -0x80, 165 },
{ -0x01, 0x02, 0x04, -0x08, 0x10, -0x20, -0x40, 0x80, -151 },
{ 0x01, -0x02, 0x04, -0x08, -0x10, 0x20, -0x40, 0x80, 90 }
};
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
2009-05-02 23:35:44 +02:00
static void DSP_ChangeMode(DSP_MODES mode);
2009-05-03 00:02:15 +02:00
static void CheckDMAEnd();
static void END_DMA_Event(Bitu);
static void DMA_Silent_Event(Bitu val);
static void GenerateDMASound(Bitu size);
2009-05-02 23:35:44 +02:00
2009-05-02 23:03:37 +02:00
static void DSP_SetSpeaker(bool how) {
2009-05-03 00:02:15 +02:00
if (sb.speaker==how) return;
2009-05-02 23:03:37 +02:00
sb.speaker=how;
2009-05-03 00:18:08 +02:00
if (sb.type==SBT_16) return;
2009-05-03 00:02:15 +02:00
sb.chan->Enable(how);
if (sb.speaker) {
PIC_RemoveEvents(DMA_Silent_Event);
CheckDMAEnd();
} else {
}
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:43:00 +02:00
static INLINE void SB_RaiseIRQ(SB_IRQS type) {
LOG(LOG_SB,LOG_NORMAL)("Raising IRQ");
2009-05-02 23:53:27 +02:00
PIC_ActivateIRQ(sb.hw.irq);
2009-05-02 23:43:00 +02:00
switch (type) {
case SB_IRQ_8:
sb.irq.pending_8bit=true;
break;
case SB_IRQ_16:
sb.irq.pending_16bit=true;
break;
}
}
2009-05-02 23:03:37 +02:00
static INLINE void DSP_FlushData(void) {
2009-05-02 23:35:44 +02:00
sb.dsp.out.used=0;
sb.dsp.out.pos=0;
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
static void DSP_DMA_CallBack(DmaChannel * chan, DMAEvent event) {
if (event==DMA_REACHED_TC) return;
else if (event==DMA_MASKED) {
if (sb.mode==MODE_DMA) {
GenerateDMASound(sb.dma.min);
sb.mode=MODE_DMA_MASKED;
// DSP_ChangeMode(MODE_DMA_MASKED);
LOG(LOG_SB,LOG_NORMAL)("DMA masked,stopping output, left %d",chan->currcnt);
}
} else if (event==DMA_UNMASKED) {
if (sb.mode==MODE_DMA_MASKED && sb.dma.mode!=DSP_DMA_NONE) {
DSP_ChangeMode(MODE_DMA);
// sb.mode=MODE_DMA;
CheckDMAEnd();
LOG(LOG_SB,LOG_NORMAL)("DMA unmasked,starting output, auto %d block %d",chan->autoinit,chan->basecnt);
}
2009-05-02 23:35:44 +02:00
}
}
2009-05-03 00:18:08 +02:00
#define MIN_ADAPTIVE_STEP_SIZE 0
2009-05-02 23:43:00 +02:00
#define MAX_ADAPTIVE_STEP_SIZE 32767
#define DC_OFFSET_FADE 254
2009-05-03 00:02:15 +02:00
static INLINE Bit8u decode_ADPCM_4_sample(Bit8u sample,Bit8u & reference,Bits& scale) {
2009-05-03 00:18:08 +02:00
static const Bit8s scaleMap[64] = {
0, 1, 2, 3, 4, 5, 6, 7, 0, -1, -2, -3, -4, -5, -6, -7,
1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15,
2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30,
4, 12, 20, 28, 36, 44, 52, 60, -4, -12, -20, -28, -36, -44, -52, -60
};
static const Bit8u adjustMap[64] = {
0, 0, 0, 0, 0, 16, 16, 16,
0, 0, 0, 0, 0, 16, 16, 16,
240, 0, 0, 0, 0, 16, 16, 16,
240, 0, 0, 0, 0, 16, 16, 16,
240, 0, 0, 0, 0, 16, 16, 16,
240, 0, 0, 0, 0, 16, 16, 16,
240, 0, 0, 0, 0, 0, 0, 0,
240, 0, 0, 0, 0, 0, 0, 0
};
Bits samp = sample + scale;
if ((samp < 0) || (samp > 63)) {
LOG(LOG_SB,LOG_ERROR)("Bad ADPCM-4 sample");
if(samp < 0 ) samp = 0;
if(samp > 63) samp = 63;
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:18:08 +02:00
Bits ref = reference + scaleMap[samp];
if (ref > 0xff) reference = 0xff;
else if (ref < 0x00) reference = 0x00;
else reference = ref;
scale = (scale + adjustMap[samp]) & 0xff;
2009-05-03 00:02:15 +02:00
return reference;
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
static INLINE Bit8u decode_ADPCM_2_sample(Bit8u sample,Bit8u & reference,Bits& scale) {
2009-05-03 00:18:08 +02:00
static const Bit8s scaleMap[24] = {
0, 1, 0, -1, 1, 3, -1, -3,
2, 6, -2, -6, 4, 12, -4, -12,
8, 24, -8, -24, 6, 48, -16, -48
};
static const Bit8u adjustMap[24] = {
0, 4, 0, 4,
252, 4, 252, 4, 252, 4, 252, 4,
252, 4, 252, 4, 252, 4, 252, 4,
252, 0, 252, 0
};
Bits samp = sample + scale;
if ((samp < 0) || (samp > 23)) {
LOG(LOG_SB,LOG_ERROR)("Bad ADPCM-2 sample");
if(samp < 0 ) samp = 0;
if(samp > 23) samp = 23;
2009-05-02 23:53:27 +02:00
}
2009-05-03 00:18:08 +02:00
Bits ref = reference + scaleMap[samp];
if (ref > 0xff) reference = 0xff;
else if (ref < 0x00) reference = 0x00;
else reference = ref;
scale = (scale + adjustMap[samp]) & 0xff;
2009-05-03 00:02:15 +02:00
return reference;
2009-05-02 23:53:27 +02:00
}
2009-05-03 00:02:15 +02:00
INLINE Bit8u decode_ADPCM_3_sample(Bit8u sample,Bit8u & reference,Bits& scale) {
2009-05-03 00:18:08 +02:00
static const Bit8s scaleMap[40] = {
0, 1, 2, 3, 0, -1, -2, -3,
1, 3, 5, 7, -1, -3, -5, -7,
2, 6, 10, 14, -2, -6, -10, -14,
4, 12, 20, 28, -4, -12, -20, -28,
5, 15, 25, 35, -5, -15, -25, -35
};
static const Bit8u adjustMap[40] = {
0, 0, 0, 8, 0, 0, 0, 8,
248, 0, 0, 8, 248, 0, 0, 8,
248, 0, 0, 8, 248, 0, 0, 8,
248, 0, 0, 8, 248, 0, 0, 8,
248, 0, 0, 0, 248, 0, 0, 0
};
Bits samp = sample + scale;
if ((samp < 0) || (samp > 39)) {
LOG(LOG_SB,LOG_ERROR)("Bad ADPCM-3 sample");
if(samp < 0 ) samp = 0;
if(samp > 39) samp = 39;
2009-05-02 23:53:27 +02:00
}
2009-05-03 00:18:08 +02:00
Bits ref = reference + scaleMap[samp];
if (ref > 0xff) reference = 0xff;
else if (ref < 0x00) reference = 0x00;
else reference = ref;
scale = (scale + adjustMap[samp]) & 0xff;
2009-05-03 00:02:15 +02:00
return reference;
2009-05-02 23:53:27 +02:00
}
2009-05-02 23:35:44 +02:00
static void GenerateDMASound(Bitu size) {
2009-05-03 00:18:08 +02:00
Bitu read=0;Bitu done=0;Bitu i=0;
2009-05-03 00:37:32 +02:00
if(sb.dma.autoinit) {
if (sb.dma.left <= size) size = sb.dma.left;
} else if (sb.dma.left <= sb.dma.min) size = sb.dma.left;
2009-05-02 23:35:44 +02:00
switch (sb.dma.mode) {
2009-05-03 00:02:15 +02:00
case DSP_DMA_2:
read=sb.dma.chan->Read(size,sb.dma.buf.b8);
if (read && sb.adpcm.haveref) {
sb.adpcm.haveref=false;
sb.adpcm.reference=sb.dma.buf.b8[0];
2009-05-02 23:53:27 +02:00
sb.adpcm.stepsize=MIN_ADAPTIVE_STEP_SIZE;
2009-05-03 00:02:15 +02:00
i++;
2009-05-02 23:53:27 +02:00
}
2009-05-03 00:02:15 +02:00
for (;i<read;i++) {
MixTemp[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 6) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
MixTemp[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 4) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
MixTemp[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 2) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
MixTemp[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 0) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
2009-05-02 23:53:27 +02:00
}
2009-05-03 00:02:15 +02:00
sb.chan->AddSamples_m8(done,MixTemp);
break;
case DSP_DMA_3:
read=sb.dma.chan->Read(size,sb.dma.buf.b8);
if (read && sb.adpcm.haveref) {
sb.adpcm.haveref=false;
sb.adpcm.reference=sb.dma.buf.b8[0];
2009-05-02 23:53:27 +02:00
sb.adpcm.stepsize=MIN_ADAPTIVE_STEP_SIZE;
2009-05-03 00:02:15 +02:00
i++;
2009-05-02 23:53:27 +02:00
}
2009-05-03 00:02:15 +02:00
for (;i<read;i++) {
MixTemp[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] >> 5) & 0x7,sb.adpcm.reference,sb.adpcm.stepsize);
MixTemp[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] >> 2) & 0x7,sb.adpcm.reference,sb.adpcm.stepsize);
2009-05-03 00:18:08 +02:00
MixTemp[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] & 0x3) << 1,sb.adpcm.reference,sb.adpcm.stepsize);
2009-05-02 23:53:27 +02:00
}
2009-05-03 00:02:15 +02:00
sb.chan->AddSamples_m8(done,MixTemp);
2009-05-02 23:53:27 +02:00
break;
2009-05-03 00:02:15 +02:00
case DSP_DMA_4:
read=sb.dma.chan->Read(size,sb.dma.buf.b8);
if (read && sb.adpcm.haveref) {
sb.adpcm.haveref=false;
sb.adpcm.reference=sb.dma.buf.b8[0];
2009-05-02 23:43:00 +02:00
sb.adpcm.stepsize=MIN_ADAPTIVE_STEP_SIZE;
2009-05-03 00:02:15 +02:00
i++;
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:02:15 +02:00
for (;i<read;i++) {
MixTemp[done++]=decode_ADPCM_4_sample(sb.dma.buf.b8[i] >> 4,sb.adpcm.reference,sb.adpcm.stepsize);
MixTemp[done++]=decode_ADPCM_4_sample(sb.dma.buf.b8[i]& 0xf,sb.adpcm.reference,sb.adpcm.stepsize);
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:02:15 +02:00
sb.chan->AddSamples_m8(done,MixTemp);
break;
case DSP_DMA_8:
if (sb.dma.stereo) {
read=sb.dma.chan->Read(size,&sb.dma.buf.b8[sb.dma.remain_size]);
Bitu total=read+sb.dma.remain_size;
2009-05-03 00:28:34 +02:00
if (!sb.dma.sign) sb.chan->AddSamples_s8(total>>1,sb.dma.buf.b8);
else sb.chan->AddSamples_s8s(total>>1,(Bit8s*)sb.dma.buf.b8);
2009-05-03 00:02:15 +02:00
if (total&1) {
sb.dma.remain_size=1;
sb.dma.buf.b8[0]=sb.dma.buf.b8[total-1];
} else sb.dma.remain_size=0;
} else {
read=sb.dma.chan->Read(size,sb.dma.buf.b8);
2009-05-03 00:28:34 +02:00
if (!sb.dma.sign) sb.chan->AddSamples_m8(read,sb.dma.buf.b8);
else sb.chan->AddSamples_m8s(read,(Bit8s *)sb.dma.buf.b8);
2009-05-02 23:43:00 +02:00
}
2009-05-02 23:35:44 +02:00
break;
2009-05-03 00:02:15 +02:00
case DSP_DMA_16:
2009-05-03 00:28:34 +02:00
case DSP_DMA_16_ALIASED:
2009-05-03 00:02:15 +02:00
if (sb.dma.stereo) {
2009-05-03 00:28:34 +02:00
/* In DSP_DMA_16_ALIASED mode temporarily divide by 2 to get number of 16-bit
samples, because 8-bit DMA Read returns byte size, while in DSP_DMA_16 mode
16-bit DMA Read returns word size */
read=sb.dma.chan->Read(size,(Bit8u *)&sb.dma.buf.b16[sb.dma.remain_size])
>> (sb.dma.mode==DSP_DMA_16_ALIASED ? 1:0);
2009-05-03 00:02:15 +02:00
Bitu total=read+sb.dma.remain_size;
2009-05-03 00:28:34 +02:00
if (sb.dma.sign) sb.chan->AddSamples_s16(total>>1,sb.dma.buf.b16);
else sb.chan->AddSamples_s16u(total>>1,(Bit16u *)sb.dma.buf.b16);
2009-05-03 00:02:15 +02:00
if (total&1) {
sb.dma.remain_size=1;
sb.dma.buf.b16[0]=sb.dma.buf.b16[total-1];
} else sb.dma.remain_size=0;
} else {
2009-05-03 00:28:34 +02:00
read=sb.dma.chan->Read(size,(Bit8u *)sb.dma.buf.b16)
>> (sb.dma.mode==DSP_DMA_16_ALIASED ? 1:0);
if (sb.dma.sign) sb.chan->AddSamples_m16(read,sb.dma.buf.b16);
else sb.chan->AddSamples_m16u(read,(Bit16u *)sb.dma.buf.b16);
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:28:34 +02:00
//restore buffer length value to byte size in aliased mode
if (sb.dma.mode==DSP_DMA_16_ALIASED) read=read<<1;
2009-05-02 23:35:44 +02:00
break;
2009-05-02 23:43:00 +02:00
default:
LOG_MSG("Unhandled dma mode %d",sb.dma.mode);
sb.mode=MODE_NONE;
return;
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:02:15 +02:00
sb.dma.left-=read;
if (!sb.dma.left) {
PIC_RemoveEvents(END_DMA_Event);
if (!sb.dma.autoinit) {
LOG(LOG_SB,LOG_NORMAL)("Single cycle transfer ended");
sb.mode=MODE_NONE;
sb.dma.mode=DSP_DMA_NONE;
2009-05-02 23:43:00 +02:00
} else {
2009-05-03 00:02:15 +02:00
sb.dma.left=sb.dma.total;
if (!sb.dma.left) {
LOG(LOG_SB,LOG_NORMAL)("Auto-init transfer with 0 size");
sb.mode=MODE_NONE;
2009-05-02 23:43:00 +02:00
}
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:02:15 +02:00
if (sb.dma.mode >= DSP_DMA_16) SB_RaiseIRQ(SB_IRQ_16);
else SB_RaiseIRQ(SB_IRQ_8);
2009-05-02 23:35:44 +02:00
}
}
2009-05-03 00:02:15 +02:00
static void GenerateDACSound(Bitu len) {
if (!sb.dac.used) {
sb.mode=MODE_NONE;
return;
}
Bitu dac_add=(sb.dac.used<<16)/len;
Bitu dac_pos=0;
Bit16s * out=(Bit16s *)MixTemp;
for (Bitu i=len;i;i--) {
*out++=sb.dac.data[0+(dac_pos>>16)];
dac_pos+=dac_add;
}
sb.dac.used=0;
sb.chan->AddSamples_m16(len,(Bit16s *)MixTemp);
}
static void DMA_Silent_Event(Bitu val) {
if (sb.dma.left<val) val=sb.dma.left;
Bitu read=sb.dma.chan->Read(val,sb.dma.buf.b8);
sb.dma.left-=read;
if (!sb.dma.left) {
if (sb.dma.mode >= DSP_DMA_16) SB_RaiseIRQ(SB_IRQ_16);
else SB_RaiseIRQ(SB_IRQ_8);
if (sb.dma.autoinit) sb.dma.left=sb.dma.total;
else {
sb.mode=MODE_NONE;
sb.dma.mode=DSP_DMA_NONE;
2009-05-02 23:35:44 +02:00
}
}
2009-05-03 00:02:15 +02:00
if (sb.dma.left) {
Bitu bigger=(sb.dma.left > sb.dma.min) ? sb.dma.min : sb.dma.left;
float delay=(bigger*1000.0f)/sb.dma.rate;
PIC_AddEvent(DMA_Silent_Event,delay,bigger);
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:02:15 +02:00
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:02:15 +02:00
static void END_DMA_Event(Bitu val) {
GenerateDMASound(val);
2009-05-02 23:35:44 +02:00
}
static void CheckDMAEnd(void) {
2009-05-03 00:02:15 +02:00
if (!sb.dma.left) return;
2009-05-03 00:18:08 +02:00
if (!sb.speaker && sb.type!=SBT_16) {
2009-05-03 00:02:15 +02:00
Bitu bigger=(sb.dma.left > sb.dma.min) ? sb.dma.min : sb.dma.left;
float delay=(bigger*1000.0f)/sb.dma.rate;
PIC_AddEvent(DMA_Silent_Event,delay,bigger);
LOG(LOG_SB,LOG_NORMAL)("Silent DMA Transfer scheduling IRQ in %.3f milliseconds",delay);
} else if (sb.dma.left<sb.dma.min) {
float delay=(sb.dma.left*1000.0f)/sb.dma.rate;
LOG(LOG_SB,LOG_NORMAL)("Short transfer scheduling IRQ in %.3f milliseconds",delay);
PIC_AddEvent(END_DMA_Event,delay,sb.dma.left);
2009-05-02 23:35:44 +02:00
}
}
static void DSP_ChangeMode(DSP_MODES mode) {
2009-05-03 00:02:15 +02:00
if (sb.mode==mode) return;
else sb.chan->FillUp();
2009-05-02 23:35:44 +02:00
sb.mode=mode;
}
2009-05-03 00:02:15 +02:00
static void DSP_RaiseIRQEvent(Bitu val) {
2009-05-02 23:53:27 +02:00
SB_RaiseIRQ(SB_IRQ_8);
}
2009-05-03 00:18:08 +02:00
static void DSP_DoDMATransfer(DMA_MODES mode,Bitu freq,bool stereo) {
2009-05-03 00:37:32 +02:00
char const * type;
2009-05-03 00:02:15 +02:00
sb.mode=MODE_DMA_MASKED;
sb.chan->FillUp();
2009-05-02 23:35:44 +02:00
sb.dma.left=sb.dma.total;
sb.dma.mode=mode;
2009-05-03 00:02:15 +02:00
sb.dma.stereo=stereo;
sb.irq.pending_8bit=false;
sb.irq.pending_16bit=false;
2009-05-02 23:03:37 +02:00
switch (mode) {
2009-05-03 00:02:15 +02:00
case DSP_DMA_2:
type="2-bits ADPCM";
sb.dma.mul=(1 << SB_SH)/4;
2009-05-02 23:03:37 +02:00
break;
2009-05-03 00:02:15 +02:00
case DSP_DMA_3:
type="3-bits ADPCM";
sb.dma.mul=(1 << SB_SH)/3;
2009-05-02 23:03:37 +02:00
break;
2009-05-03 00:02:15 +02:00
case DSP_DMA_4:
type="4-bits ADPCM";
sb.dma.mul=(1 << SB_SH)/2;
2009-05-02 23:03:37 +02:00
break;
2009-05-03 00:02:15 +02:00
case DSP_DMA_8:
type="8-bits PCM";
sb.dma.mul=(1 << SB_SH);
2009-05-02 23:53:27 +02:00
break;
2009-05-03 00:02:15 +02:00
case DSP_DMA_16_ALIASED:
type="16-bits(aliased) PCM";
sb.dma.mul=(1 << SB_SH)*2;
break;
case DSP_DMA_16:
type="16-bits PCM";
sb.dma.mul=(1 << SB_SH);
2009-05-02 23:53:27 +02:00
break;
2009-05-02 23:03:37 +02:00
default:
2009-05-02 23:43:00 +02:00
LOG(LOG_SB,LOG_ERROR)("DSP:Illegal transfer mode %d",mode);
2009-05-02 23:03:37 +02:00
return;
}
2009-05-03 00:02:15 +02:00
if (sb.dma.stereo) sb.dma.mul*=2;
sb.dma.rate=(sb.freq*sb.dma.mul) >> SB_SH;
sb.dma.min=(sb.dma.rate*3)/1000;
sb.chan->SetFreq(freq);
2009-05-02 23:35:44 +02:00
sb.dma.mode=mode;
2009-05-03 00:02:15 +02:00
PIC_RemoveEvents(END_DMA_Event);
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,
sb.dma.stereo ? "Stereo" : "Mono",
sb.dma.autoinit ? "Auto-Init" : "Single-Cycle",
freq,sb.dma.rate,sb.dma.total
);
#endif
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:44:30 +02:00
static void DSP_PrepareDMA_Old(DMA_MODES mode,bool autoinit,bool sign) {
2009-05-03 00:02:15 +02:00
sb.dma.autoinit=autoinit;
2009-05-03 00:44:30 +02:00
sb.dma.sign=sign;
2009-05-03 00:02:15 +02:00
if (!autoinit) sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8);
2009-05-03 00:18:08 +02:00
sb.dma.chan=GetDMAChannel(sb.hw.dma8);
DSP_DoDMATransfer(mode,sb.freq / (sb.mixer.stereo ? 2 : 1),sb.mixer.stereo);
2009-05-03 00:02:15 +02:00
}
static void DSP_PrepareDMA_New(DMA_MODES mode,Bitu length,bool autoinit,bool stereo) {
Bitu freq=sb.freq;
2009-05-03 00:28:34 +02:00
//equal length if data format and dma channel are both 16-bit or 8-bit
2009-05-03 00:02:15 +02:00
sb.dma.total=length;
sb.dma.autoinit=autoinit;
if (mode==DSP_DMA_16) {
2009-05-03 00:28:34 +02:00
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;
}
} else {
2009-05-03 00:18:08 +02:00
sb.dma.chan=GetDMAChannel(sb.hw.dma8);
2009-05-03 00:02:15 +02:00
mode=DSP_DMA_16_ALIASED;
2009-05-03 00:28:34 +02:00
//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;
2009-05-03 00:02:15 +02:00
}
2009-05-03 00:18:08 +02:00
} else sb.dma.chan=GetDMAChannel(sb.hw.dma8);
DSP_DoDMATransfer(mode,freq,stereo);
2009-05-03 00:02:15 +02:00
}
2009-05-02 23:03:37 +02:00
static void DSP_AddData(Bit8u val) {
2009-05-02 23:35:44 +02:00
if (sb.dsp.out.used<DSP_BUFSIZE) {
Bitu start=sb.dsp.out.used+sb.dsp.out.pos;
2009-05-02 23:03:37 +02:00
if (start>=DSP_BUFSIZE) start-=DSP_BUFSIZE;
2009-05-02 23:35:44 +02:00
sb.dsp.out.data[start]=val;
sb.dsp.out.used++;
2009-05-02 23:03:37 +02:00
} else {
2009-05-02 23:43:00 +02:00
LOG(LOG_SB,LOG_ERROR)("DSP:Data Output buffer full");
2009-05-02 23:03:37 +02:00
}
}
2009-05-02 23:35:44 +02:00
2009-05-02 23:03:37 +02:00
static void DSP_Reset(void) {
2009-05-02 23:53:27 +02:00
LOG(LOG_SB,LOG_ERROR)("DSP:Reset");
PIC_DeActivateIRQ(sb.hw.irq);
2009-05-02 23:35:44 +02:00
DSP_ChangeMode(MODE_NONE);
sb.dsp.cmd_len=0;
sb.dsp.in.pos=0;
sb.dsp.write_busy=0;
sb.dma.left=0;
sb.dma.total=0;
2009-05-02 23:43:00 +02:00
sb.dma.stereo=false;
2009-05-03 00:28:34 +02:00
sb.dma.sign=false;
2009-05-03 00:02:15 +02:00
sb.dma.autoinit=false;
sb.dma.mode=DSP_DMA_NONE;
sb.dma.remain_size=0;
2009-05-02 23:20:05 +02:00
sb.freq=22050;
2009-05-02 23:35:44 +02:00
sb.time_constant=45;
2009-05-02 23:12:18 +02:00
sb.dac.used=0;
2009-05-02 23:35:44 +02:00
sb.dac.last=0;
sb.e2.value=0xaa;
sb.e2.count=0;
2009-05-02 23:43:00 +02:00
sb.irq.pending_8bit=false;
sb.irq.pending_16bit=false;
2009-05-03 00:02:15 +02:00
sb.chan->SetFreq(22050);
2009-05-03 00:18:08 +02:00
// DSP_SetSpeaker(false);
2009-05-03 00:02:15 +02:00
PIC_RemoveEvents(END_DMA_Event);
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:35:44 +02:00
2009-05-02 23:03:37 +02:00
static void DSP_DoReset(Bit8u val) {
2009-05-02 23:43:00 +02:00
if ((val&1)!=0) {
2009-05-02 23:03:37 +02:00
//TODO Get out of highspeed mode
DSP_Reset();
2009-05-02 23:35:44 +02:00
sb.dsp.state=DSP_S_RESET;
2009-05-02 23:03:37 +02:00
} else {
DSP_FlushData();
DSP_AddData(0xaa);
2009-05-02 23:35:44 +02:00
sb.dsp.state=DSP_S_NORMAL;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:02:15 +02:00
static void DSP_E2_DMA_CallBack(DmaChannel * chan, DMAEvent event) {
if (event==DMA_UNMASKED) {
2009-05-02 23:35:44 +02:00
Bit8u val=sb.e2.value;
2009-05-03 00:18:08 +02:00
DmaChannel * chan=GetDMAChannel(sb.hw.dma8);
chan->Register_Callback(0);
chan->Write(1,&val);
2009-05-03 00:02:15 +02:00
}
}
static void DSP_ADC_CallBack(DmaChannel * chan, DMAEvent event) {
if (event!=DMA_UNMASKED) return;
Bit8u val=128;
2009-05-03 00:18:08 +02:00
DmaChannel * ch=GetDMAChannel(sb.hw.dma8);
2009-05-03 00:28:34 +02:00
while (sb.dma.left--) {
2009-05-03 00:18:08 +02:00
ch->Write(1,&val);
2009-05-02 23:35:44 +02:00
}
2009-05-03 00:02:15 +02:00
SB_RaiseIRQ(SB_IRQ_8);
2009-05-03 00:18:08 +02:00
ch->Register_Callback(0);
2009-05-02 23:35:44 +02:00
}
2009-05-02 23:03:37 +02:00
2009-05-03 00:02:15 +02:00
Bitu DEBUG_EnableDebugger(void);
2009-05-03 00:18:08 +02:00
#define DSP_SB16_ONLY if (sb.type != SBT_16) { LOG(LOG_SB,LOG_ERROR)("DSP:Command %2X requires SB16",sb.dsp.cmd); break; }
#define DSP_SB2_ABOVE if (sb.type <= SBT_1) { LOG(LOG_SB,LOG_ERROR)("DSP:Command %2X requires SB2 or above",sb.dsp.cmd); break; }
2009-05-02 23:03:37 +02:00
static void DSP_DoCommand(void) {
2009-05-02 23:43:00 +02:00
// LOG_MSG("DSP Command %X",sb.dsp.cmd);
2009-05-02 23:35:44 +02:00
switch (sb.dsp.cmd) {
2009-05-03 00:28:34 +02:00
case 0x04: /* DSP Status SB 2.0/pro version. NOT SB16. */
2009-05-02 23:43:00 +02:00
DSP_FlushData();
DSP_AddData(0xff); //Everthing enabled
break;
2009-05-02 23:03:37 +02:00
case 0x10: /* Direct DAC */
2009-05-02 23:35:44 +02:00
DSP_ChangeMode(MODE_DAC);
2009-05-02 23:12:18 +02:00
if (sb.dac.used<DSP_DACSIZE) {
2009-05-02 23:35:44 +02:00
sb.dac.data[sb.dac.used++]=(Bit8s(sb.dsp.in.data[0] ^ 0x80)) << 8;
2009-05-03 00:02:15 +02:00
sb.dac.data[sb.dac.used++]=(Bit8s(sb.dsp.in.data[0] ^ 0x80)) << 8;
2009-05-02 23:03:37 +02:00
}
break;
2009-05-02 23:35:44 +02:00
case 0x24: /* Singe Cycle 8-Bit DMA ADC */
2009-05-03 00:02:15 +02:00
sb.dma.left=sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8);
2009-05-03 00:44:30 +02:00
sb.dma.sign=false;
2009-05-03 00:02:15 +02:00
LOG(LOG_SB,LOG_ERROR)("DSP:Faked ADC for %d bytes",sb.dma.total);
2009-05-03 00:18:08 +02:00
GetDMAChannel(sb.hw.dma8)->Register_Callback(DSP_ADC_CallBack);
2009-05-03 00:02:15 +02:00
break;
2009-05-02 23:35:44 +02:00
case 0x14: /* Singe Cycle 8-Bit DMA DAC */
case 0x91: /* Singe Cycle 8-Bit DMA High speed DAC */
2009-05-03 00:18:08 +02:00
/* Note: 0x91 is documented only for DSP ver.2.x and 3.x, not 4.x */
2009-05-03 00:44:30 +02:00
DSP_PrepareDMA_Old(DSP_DMA_8,false,false);
2009-05-02 23:03:37 +02:00
break;
2009-05-02 23:35:44 +02:00
case 0x90: /* Auto Init 8-bit DMA High Speed */
2009-05-02 23:03:37 +02:00
case 0x1c: /* Auto Init 8-bit DMA */
2009-05-03 00:18:08 +02:00
DSP_SB2_ABOVE; /* Note: 0x90 is documented only for DSP ver.2.x and 3.x, not 4.x */
2009-05-03 00:44:30 +02:00
DSP_PrepareDMA_Old(DSP_DMA_8,true,false);
2009-05-02 23:03:37 +02:00
break;
2009-05-03 00:18:08 +02:00
case 0x38: /* Write to SB MIDI Output */
if (sb.midi == true) MIDI_RawOutByte(sb.dsp.in.data[0]);
break;
2009-05-02 23:03:37 +02:00
case 0x40: /* Set Timeconstant */
2009-05-03 00:02:15 +02:00
sb.freq=(1000000 / (256 - sb.dsp.in.data[0]));
2009-05-03 00:08:43 +02:00
/* Nasty kind of hack to allow runtime changing of frequency */
if (sb.dma.mode != DSP_DMA_NONE && sb.dma.autoinit) {
2009-05-03 00:44:30 +02:00
DSP_PrepareDMA_Old(sb.dma.mode,sb.dma.autoinit,sb.dma.sign);
2009-05-03 00:08:43 +02:00
}
2009-05-03 00:02:15 +02:00
break;
case 0x41: /* Set Output Samplerate */
case 0x42: /* Set Input Samplerate */
2009-05-03 00:18:08 +02:00
DSP_SB16_ONLY;
2009-05-03 00:02:15 +02:00
sb.freq=(sb.dsp.in.data[0] << 8) | sb.dsp.in.data[1];
2009-05-02 23:03:37 +02:00
break;
case 0x48: /* Set DMA Block Size */
2009-05-03 00:18:08 +02:00
DSP_SB2_ABOVE;
2009-05-02 23:35:44 +02:00
//TODO Maybe check limit for new irq?
sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8);
2009-05-02 23:03:37 +02:00
break;
2009-05-03 00:18:08 +02:00
case 0x75: /* 075h : Single Cycle 4-bit ADPCM Reference */
2009-05-03 00:02:15 +02:00
sb.adpcm.haveref=true;
2009-05-03 00:18:08 +02:00
case 0x74: /* 074h : Single Cycle 4-bit ADPCM */
2009-05-03 00:44:30 +02:00
DSP_PrepareDMA_Old(DSP_DMA_4,false,false);
2009-05-02 23:03:37 +02:00
break;
2009-05-03 00:02:15 +02:00
case 0x77: /* 077h : Single Cycle 3-bit(2.6bit) ADPCM Reference*/
sb.adpcm.haveref=true;
2009-05-02 23:53:27 +02:00
case 0x76: /* 074h : Single Cycle 3-bit(2.6bit) ADPCM */
2009-05-03 00:44:30 +02:00
DSP_PrepareDMA_Old(DSP_DMA_3,false,false);
2009-05-02 23:53:27 +02:00
break;
2009-05-03 00:02:15 +02:00
case 0x17: /* 017h : Single Cycle 2-bit ADPCM Reference*/
sb.adpcm.haveref=true;
2009-05-02 23:53:27 +02:00
case 0x16: /* 074h : Single Cycle 2-bit ADPCM */
2009-05-03 00:44:30 +02:00
DSP_PrepareDMA_Old(DSP_DMA_2,false,false);
2009-05-02 23:53:27 +02:00
break;
2009-05-02 23:20:05 +02:00
case 0x80: /* Silence DAC */
2009-05-03 00:02:15 +02:00
PIC_AddEvent(&DSP_RaiseIRQEvent,
(1000.0f*(1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8))/sb.freq));
break;
2009-05-03 00:18:08 +02:00
case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7:
case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: case 0xc5: case 0xc6: case 0xc7:
case 0xc8: case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf:
DSP_SB16_ONLY;
2009-05-03 00:02:15 +02:00
/* Generic 8/16 bit DMA */
2009-05-03 00:18:08 +02:00
// DSP_SetSpeaker(true); //SB16 always has speaker enabled
2009-05-03 00:02:15 +02:00
sb.dma.sign=(sb.dsp.in.data[0] & 0x10) > 0;
DSP_PrepareDMA_New((sb.dsp.cmd & 0x10) ? DSP_DMA_16 : DSP_DMA_8,
1+sb.dsp.in.data[1]+(sb.dsp.in.data[2] << 8),
(sb.dsp.cmd & 0x4)>0,
(sb.dsp.in.data[0] & 0x20) > 0
);
2009-05-02 23:20:05 +02:00
break;
2009-05-03 00:02:15 +02:00
case 0xd5: /* Halt 16-bit DMA */
2009-05-03 00:18:08 +02:00
DSP_SB16_ONLY;
case 0xd0: /* Halt 8-bit DMA */
2009-05-03 00:02:15 +02:00
// DSP_ChangeMode(MODE_NONE);
// Games sometimes already program a new dma before stopping, gives noise
sb.mode=MODE_DMA_PAUSE;
PIC_RemoveEvents(END_DMA_Event);
2009-05-02 23:03:37 +02:00
break;
case 0xd1: /* Enable Speaker */
DSP_SetSpeaker(true);
break;
case 0xd3: /* Disable Speaker */
DSP_SetSpeaker(false);
break;
2009-05-03 00:18:08 +02:00
case 0xd8: /* Speaker status */
DSP_SB2_ABOVE;
DSP_FlushData();
if (sb.speaker) DSP_AddData(0xff);
else DSP_AddData(0x00);
break;
2009-05-03 00:02:15 +02:00
case 0xd6: /* Continue DMA 16-bit */
2009-05-03 00:18:08 +02:00
DSP_SB16_ONLY;
case 0xd4: /* Continue DMA 8-bit*/
2009-05-03 00:02:15 +02:00
if (sb.mode==MODE_DMA_PAUSE) {
sb.mode=MODE_DMA_MASKED;
sb.dma.chan->Register_Callback(DSP_DMA_CallBack);
}
2009-05-02 23:35:44 +02:00
break;
2009-05-03 00:18:08 +02:00
case 0xd9: /* Exit Autoinitialize 16-bit */
DSP_SB16_ONLY;
2009-05-02 23:35:44 +02:00
case 0xda: /* Exit Autoinitialize 8-bit */
2009-05-03 00:18:08 +02:00
DSP_SB2_ABOVE;
2009-05-02 23:35:44 +02:00
/* Set mode to single transfer so it ends with current block */
2009-05-03 00:02:15 +02:00
sb.dma.autoinit=false; //Should stop itself
2009-05-02 23:35:44 +02:00
break;
2009-05-02 23:03:37 +02:00
case 0xe0: /* DSP Identification - SB2.0+ */
DSP_FlushData();
2009-05-02 23:35:44 +02:00
DSP_AddData(~sb.dsp.in.data[0]);
2009-05-02 23:03:37 +02:00
break;
case 0xe1: /* Get DSP Version */
DSP_FlushData();
2009-05-03 00:02:15 +02:00
switch (sb.type) {
case SBT_1:
DSP_AddData(0x1);DSP_AddData(0x1);break;
case SBT_2:
DSP_AddData(0x2);DSP_AddData(0x1);break;
case SBT_PRO1:
DSP_AddData(0x3);DSP_AddData(0x0);break;
case SBT_PRO2:
DSP_AddData(0x3);DSP_AddData(0x2);break;
case SBT_16:
DSP_AddData(0x4);DSP_AddData(0x5);break;
}
2009-05-02 23:03:37 +02:00
break;
case 0xe2: /* Weird DMA identification write routine */
{
2009-05-02 23:43:00 +02:00
LOG(LOG_SB,LOG_NORMAL)("DSP Function 0xe2");
2009-05-02 23:35:44 +02:00
for (Bitu i = 0; i < 8; i++)
if ((sb.dsp.in.data[0] >> i) & 0x01) sb.e2.value += E2_incr_table[sb.e2.count % 4][i];
sb.e2.value += E2_incr_table[sb.e2.count % 4][8];
sb.e2.count++;
2009-05-03 00:18:08 +02:00
GetDMAChannel(sb.hw.dma8)->Register_Callback(DSP_E2_DMA_CallBack);
2009-05-02 23:03:37 +02:00
}
break;
case 0xe3: /* DSP Copyright */
{
DSP_FlushData();
2009-05-03 00:37:32 +02:00
for (size_t i=0;i<=strlen(copyright_string);i++) {
2009-05-02 23:03:37 +02:00
DSP_AddData(copyright_string[i]);
}
}
break;
case 0xe4: /* Write Test Register */
2009-05-02 23:35:44 +02:00
sb.dsp.test_register=sb.dsp.in.data[0];
2009-05-02 23:03:37 +02:00
break;
case 0xe8: /* Read Test Register */
DSP_FlushData();
2009-05-02 23:35:44 +02:00
DSP_AddData(sb.dsp.test_register);;
2009-05-02 23:03:37 +02:00
break;
case 0xf2: /* Trigger 8bit IRQ */
2009-05-02 23:43:00 +02:00
SB_RaiseIRQ(SB_IRQ_8);
2009-05-02 23:03:37 +02:00
break;
2009-05-03 00:18:08 +02:00
case 0xf8: /* Undocumented, pre-SB16 only */
DSP_FlushData();
DSP_AddData(0);
break;
case 0x30: case 0x31:
LOG(LOG_SB,LOG_ERROR)("DSP:Unimplemented MIDI I/O command %2X",sb.dsp.cmd);
break;
case 0x34: case 0x35: case 0x36: case 0x37:
DSP_SB2_ABOVE;
LOG(LOG_SB,LOG_ERROR)("DSP:Unimplemented MIDI UART command %2X",sb.dsp.cmd);
break;
case 0x7d: case 0x7f: case 0x1f:
DSP_SB2_ABOVE;
LOG(LOG_SB,LOG_ERROR)("DSP:Unimplemented auto-init DMA ADPCM command %2X",sb.dsp.cmd);
break;
case 0x20:
case 0x2c:
case 0x98: case 0x99: /* Documented only for DSP 2.x and 3.x */
case 0xa0: case 0xa8: /* Documented only for DSP 3.x */
LOG(LOG_SB,LOG_ERROR)("DSP:Unimplemented input command %2X",sb.dsp.cmd);
break;
2009-05-03 00:28:34 +02:00
case 0x0f: /* SB16 ASP get register */
DSP_AddData(0xff); //Fall through
case 0x0e: /* SB16 ASP Command ? */
case 0x05: /* SB16 ASP set register */
LOG(LOG_SB,LOG_NORMAL)("DSP Unhandled SB16ASP command %X",sb.dsp.cmd);
break;
2009-05-02 23:03:37 +02:00
default:
2009-05-03 00:18:08 +02:00
LOG(LOG_SB,LOG_ERROR)("DSP:Unhandled (undocumented) command %2X",sb.dsp.cmd);
2009-05-02 23:35:44 +02:00
break;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:35:44 +02:00
sb.dsp.cmd=DSP_NO_COMMAND;
sb.dsp.cmd_len=0;
sb.dsp.in.pos=0;
2009-05-02 23:03:37 +02:00
}
static void DSP_DoWrite(Bit8u val) {
2009-05-02 23:35:44 +02:00
switch (sb.dsp.cmd) {
2009-05-02 23:03:37 +02:00
case DSP_NO_COMMAND:
2009-05-02 23:35:44 +02:00
sb.dsp.cmd=val;
sb.dsp.cmd_len=DSP_cmd_len[val];
sb.dsp.in.pos=0;
if (!sb.dsp.cmd_len) DSP_DoCommand();
2009-05-02 23:03:37 +02:00
break;
default:
2009-05-02 23:35:44 +02:00
sb.dsp.in.data[sb.dsp.in.pos]=val;
sb.dsp.in.pos++;
if (sb.dsp.in.pos>=sb.dsp.cmd_len) DSP_DoCommand();
2009-05-02 23:03:37 +02:00
}
}
static Bit8u DSP_ReadData(void) {
2009-05-03 00:18:08 +02:00
/* Static so it repeats the last value on succesive reads (JANGLE DEMO) */
static Bit8u data = 0;
2009-05-02 23:35:44 +02:00
if (sb.dsp.out.used) {
data=sb.dsp.out.data[sb.dsp.out.pos];
sb.dsp.out.pos++;
if (sb.dsp.out.pos>=DSP_BUFSIZE) sb.dsp.out.pos-=DSP_BUFSIZE;
sb.dsp.out.used--;
2009-05-02 23:03:37 +02:00
}
return data;
}
2009-05-03 00:02:15 +02:00
//The soundblaster manual says 2.0 Db steps but we'll go for a bit less
#define CALCVOL(_VAL) (float)pow(10.0f,((float)(31-_VAL)*-1.3f)/20)
static void CTMIXER_UpdateVolumes(void) {
if (!sb.mixer.enabled) return;
MixerChannel * chan;
2009-05-03 00:28:34 +02:00
//adjust to get linear master volume slider in trackers
2009-05-03 00:02:15 +02:00
chan=MIXER_FindChannel("SB");
2009-05-03 00:28:34 +02:00
if (chan) chan->SetVolume(float(sb.mixer.master[0])/31.0f*CALCVOL(sb.mixer.dac[0]),
float(sb.mixer.master[1])/31.0f*CALCVOL(sb.mixer.dac[1]));
2009-05-03 00:02:15 +02:00
chan=MIXER_FindChannel("FM");
2009-05-03 00:28:34 +02:00
if (chan) chan->SetVolume(float(sb.mixer.master[0])/31.0f*CALCVOL(sb.mixer.fm[0]),
float(sb.mixer.master[1])/31.0f*CALCVOL(sb.mixer.fm[1]));
2009-05-03 00:02:15 +02:00
}
static void CTMIXER_Reset(void) {
sb.mixer.fm[0]=
sb.mixer.fm[1]=
sb.mixer.dac[0]=
sb.mixer.dac[1]=31;
2009-05-03 00:28:34 +02:00
sb.mixer.master[0]=
sb.mixer.master[1]=31;
2009-05-03 00:02:15 +02:00
CTMIXER_UpdateVolumes();
}
2009-05-03 00:28:34 +02:00
#define SETPROVOL(_WHICH_,_VAL_) \
_WHICH_[0]= ((((_VAL_) & 0xf0) >> 3)|(sb.type==SBT_16 ? 1:3)); \
_WHICH_[1]= ((((_VAL_) & 0x0f) << 1)|(sb.type==SBT_16 ? 1:3)); \
#define MAKEPROVOL(_WHICH_) \
((((_WHICH_[0] & 0x1e) << 3) | ((_WHICH_[1] & 0x1e) >> 1)) & (sb.type==SBT_16 ? 0xff:0xee))
static void DSP_ChangeStereo(bool stereo) {
if (!sb.dma.stereo && stereo) {
sb.chan->SetFreq(sb.freq/2);
sb.dma.mul*=2;
sb.dma.rate=(sb.freq*sb.dma.mul) >> SB_SH;
sb.dma.min=(sb.dma.rate*3)/1000;
} else if (sb.dma.stereo && !stereo) {
sb.chan->SetFreq(sb.freq);
sb.dma.mul/=2;
sb.dma.rate=(sb.freq*sb.dma.mul) >> SB_SH;
sb.dma.min=(sb.dma.rate*3)/1000;
}
sb.dma.stereo=stereo;
}
2009-05-03 00:02:15 +02:00
static void CTMIXER_Write(Bit8u val) {
2009-05-02 23:35:44 +02:00
switch (sb.mixer.index) {
2009-05-03 00:18:08 +02:00
case 0x00: /* Reset */
CTMIXER_Reset();
LOG(LOG_SB,LOG_WARN)("Mixer reset value %x",val);
break;
2009-05-03 00:28:34 +02:00
case 0x02: /* Master Volume (SB2 Only) */
SETPROVOL(sb.mixer.master,(val&0xf)|(val<<4));
CTMIXER_UpdateVolumes();
2009-05-02 23:43:00 +02:00
break;
case 0x04: /* DAC Volume (SBPRO) */
2009-05-03 00:02:15 +02:00
SETPROVOL(sb.mixer.dac,val);
CTMIXER_UpdateVolumes();
2009-05-02 23:43:00 +02:00
break;
2009-05-03 00:28:34 +02:00
case 0x06: /* FM output selection, Somewhat obsolete with dual OPL SBpro + FM volume (SB2 Only) */
2009-05-03 00:18:08 +02:00
//volume controls both channels
2009-05-03 00:28:34 +02:00
SETPROVOL(sb.mixer.fm,(val&0xf)|(val<<4));
2009-05-03 00:02:15 +02:00
CTMIXER_UpdateVolumes();
2009-05-03 00:18:08 +02:00
if(val&0x60) LOG(LOG_SB,LOG_WARN)("Turned FM one channel off. not implemented %X",val);
2009-05-02 23:43:00 +02:00
//TODO Change FM Mode if only 1 fm channel is selected
break;
2009-05-03 00:28:34 +02:00
case 0x08: /* CDA Volume (SB2 Only) */
SETPROVOL(sb.mixer.cda,(val&0xf)|(val<<4));
break;
case 0x0a: /* Mic Level (SBPRO) or DAC Volume (SB2): 2-bit, 3-bit on SB16 */
if (sb.type==SBT_2) sb.mixer.dac[0]=sb.mixer.dac[1]=((val & 0x6) << 2)|3;
else sb.mixer.mic=((val & 0x7) << 2)|(sb.type==SBT_16?1:3);
2009-05-02 23:43:00 +02:00
break;
case 0x0e: /* Output/Stereo Select */
2009-05-03 00:02:15 +02:00
sb.mixer.stereo=(val & 0x2) > 0;
sb.mixer.filtered=(val & 0x20) > 0;
2009-05-03 00:28:34 +02:00
DSP_ChangeStereo(sb.mixer.stereo);
2009-05-02 23:43:00 +02:00
LOG(LOG_SB,LOG_WARN)("Mixer set to %s",sb.dma.stereo ? "STEREO" : "MONO");
break;
2009-05-03 00:28:34 +02:00
case 0x22: /* Master Volume (SBPRO) */
SETPROVOL(sb.mixer.master,val);
CTMIXER_UpdateVolumes();
break;
2009-05-02 23:43:00 +02:00
case 0x26: /* FM Volume (SBPRO) */
2009-05-03 00:02:15 +02:00
SETPROVOL(sb.mixer.fm,val);
2009-05-03 00:28:34 +02:00
CTMIXER_UpdateVolumes();
2009-05-02 23:43:00 +02:00
break;
case 0x28: /* CD Audio Volume (SBPRO) */
2009-05-03 00:02:15 +02:00
SETPROVOL(sb.mixer.cda,val);
2009-05-02 23:35:44 +02:00
break;
2009-05-03 00:28:34 +02:00
case 0x2e: /* Line-in Volume (SBPRO) */
2009-05-03 00:02:15 +02:00
SETPROVOL(sb.mixer.lin,val);
break;
2009-05-03 00:28:34 +02:00
//case 0x20: /* Master Volume Left (SBPRO) ? */
case 0x30: /* Master Volume Left (SB16) */
if (sb.type==SBT_16) {
sb.mixer.master[0]=val>>3;
CTMIXER_UpdateVolumes();
}
break;
//case 0x21: /* Master Volume Right (SBPRO) ? */
case 0x31: /* Master Volume Right (SB16) */
if (sb.type==SBT_16) {
sb.mixer.master[1]=val>>3;
CTMIXER_UpdateVolumes();
}
break;
case 0x32: /* DAC Volume Left (SB16) */
if (sb.type==SBT_16) {
sb.mixer.dac[0]=val>>3;
CTMIXER_UpdateVolumes();
}
break;
case 0x33: /* DAC Volume Right (SB16) */
if (sb.type==SBT_16) {
sb.mixer.dac[1]=val>>3;
CTMIXER_UpdateVolumes();
}
break;
case 0x34: /* FM Volume Left (SB16) */
if (sb.type==SBT_16) {
sb.mixer.fm[0]=val>>3;
CTMIXER_UpdateVolumes();
}
break;
case 0x35: /* FM Volume Right (SB16) */
if (sb.type==SBT_16) {
sb.mixer.fm[1]=val>>3;
CTMIXER_UpdateVolumes();
}
break;
case 0x36: /* CD Volume Left (SB16) */
if (sb.type==SBT_16) sb.mixer.cda[0]=val>>3;
break;
case 0x37: /* CD Volume Right (SB16) */
if (sb.type==SBT_16) sb.mixer.cda[1]=val>>3;
break;
case 0x38: /* Line-in Volume Left (SB16) */
if (sb.type==SBT_16) sb.mixer.lin[0]=val>>3;
break;
case 0x39: /* Line-in Volume Right (SB16) */
if (sb.type==SBT_16) sb.mixer.lin[1]=val>>3;
break;
case 0x3a:
if (sb.type==SBT_16) sb.mixer.mic=val>>3;
break;
2009-05-03 00:02:15 +02:00
case 0x80: /* IRQ Select */
sb.hw.irq=0xff;
if (val & 0x1) sb.hw.irq=2;
else if (val & 0x2) sb.hw.irq=5;
else if (val & 0x4) sb.hw.irq=7;
else if (val & 0x8) sb.hw.irq=10;
break;
case 0x81: /* DMA Select */
sb.hw.dma8=0xff;
sb.hw.dma16=0xff;
if (val & 0x1) sb.hw.dma8=0;
else if (val & 0x2) sb.hw.dma8=1;
else if (val & 0x8) sb.hw.dma8=3;
if (val & 0x20) sb.hw.dma16=5;
else if (val & 0x40) sb.hw.dma16=6;
else if (val & 0x80) sb.hw.dma16=7;
LOG(LOG_SB,LOG_NORMAL)("Mixer select dma8:%x dma16:%x",sb.hw.dma8,sb.hw.dma16);
2009-05-02 23:35:44 +02:00
break;
default:
2009-05-03 00:28:34 +02:00
if( ((sb.type == SBT_PRO1 || sb.type == SBT_PRO2) && sb.mixer.index==0x0c) || /* Input control on SBPro */
(sb.type == SBT_16 && sb.mixer.index >= 0x3b && sb.mixer.index <= 0x47)) /* New SB16 registers */
2009-05-03 00:18:08 +02:00
sb.mixer.unhandled[sb.mixer.index] = val;
2009-05-02 23:43:00 +02:00
LOG(LOG_SB,LOG_WARN)("MIXER:Write %X to unhandled index %X",val,sb.mixer.index);
2009-05-02 23:35:44 +02:00
}
}
2009-05-03 00:02:15 +02:00
static Bit8u CTMIXER_Read(void) {
2009-05-02 23:35:44 +02:00
Bit8u ret;
2009-05-03 00:02:15 +02:00
// if ( sb.mixer.index< 0x80) LOG_MSG("Read mixer %x",sb.mixer.index);
2009-05-02 23:35:44 +02:00
switch (sb.mixer.index) {
2009-05-02 23:43:00 +02:00
case 0x00: /* RESET */
return 0x00;
2009-05-03 00:28:34 +02:00
case 0x02: /* Master Volume (SB2 Only) */
return ((sb.mixer.master[1]>>1) & 0xe);
2009-05-02 23:43:00 +02:00
case 0x22: /* Master Volume (SBPRO) */
2009-05-03 00:02:15 +02:00
return MAKEPROVOL(sb.mixer.master);
2009-05-02 23:43:00 +02:00
case 0x04: /* DAC Volume (SBPRO) */
2009-05-03 00:18:08 +02:00
return MAKEPROVOL(sb.mixer.dac);
2009-05-03 00:28:34 +02:00
case 0x06: /* FM Volume (SB2 Only) + FM output selection */
return ((sb.mixer.fm[1]>>1) & 0xe);
case 0x08: /* CD Volume (SB2 Only) */
return ((sb.mixer.cda[1]>>1) & 0xe);
case 0x0a: /* Mic Level (SBPRO) or Voice (SB2 Only) */
if (sb.type==SBT_2) return (sb.mixer.dac[0]>>2);
else return ((sb.mixer.mic >> 2) & (sb.type==SBT_16 ? 7:6));
2009-05-02 23:43:00 +02:00
case 0x0e: /* Output/Stereo Select */
2009-05-03 00:02:15 +02:00
return 0x11|(sb.mixer.stereo ? 0x02 : 0x00)|(sb.mixer.filtered ? 0x20 : 0x00);
2009-05-02 23:43:00 +02:00
case 0x26: /* FM Volume (SBPRO) */
2009-05-03 00:02:15 +02:00
return MAKEPROVOL(sb.mixer.fm);
2009-05-02 23:43:00 +02:00
case 0x28: /* CD Audio Volume (SBPRO) */
2009-05-03 00:02:15 +02:00
return MAKEPROVOL(sb.mixer.cda);
2009-05-02 23:43:00 +02:00
case 0x2e: /* Line-IN Volume (SBPRO) */
2009-05-03 00:02:15 +02:00
return MAKEPROVOL(sb.mixer.lin);
2009-05-03 00:28:34 +02:00
case 0x30: /* Master Volume Left (SB16) */
if (sb.type==SBT_16) return sb.mixer.master[0]<<3;
ret=0xa;
break;
case 0x31: /* Master Volume Right (S16) */
if (sb.type==SBT_16) return sb.mixer.master[1]<<3;
ret=0xa;
break;
case 0x32: /* DAC Volume Left (SB16) */
if (sb.type==SBT_16) return sb.mixer.dac[0]<<3;
ret=0xa;
break;
case 0x33: /* DAC Volume Right (SB16) */
if (sb.type==SBT_16) return sb.mixer.dac[1]<<3;
ret=0xa;
break;
case 0x34: /* FM Volume Left (SB16) */
if (sb.type==SBT_16) return sb.mixer.fm[0]<<3;
ret=0xa;
break;
case 0x35: /* FM Volume Right (SB16) */
if (sb.type==SBT_16) return sb.mixer.fm[1]<<3;
ret=0xa;
break;
case 0x36: /* CD Volume Left (SB16) */
if (sb.type==SBT_16) return sb.mixer.cda[0]<<3;
ret=0xa;
break;
case 0x37: /* CD Volume Right (SB16) */
if (sb.type==SBT_16) return sb.mixer.cda[1]<<3;
ret=0xa;
break;
case 0x38: /* Line-in Volume Left (SB16) */
if (sb.type==SBT_16) return sb.mixer.lin[0]<<3;
ret=0xa;
break;
case 0x39: /* Line-in Volume Right (SB16) */
if (sb.type==SBT_16) return sb.mixer.lin[1]<<3;
ret=0xa;
break;
case 0x3a: /* Mic Volume (SB16) */
if (sb.type==SBT_16) return sb.mixer.mic<<3;
ret=0xa;
break;
2009-05-03 00:02:15 +02:00
case 0x80: /* IRQ Select */
switch (sb.hw.irq) {
case 2: return 0x1;
case 5: return 0x2;
case 7: return 0x4;
case 10: return 0x8;
}
case 0x81: /* DMA Select */
ret=0;
switch (sb.hw.dma8) {
case 0:ret|=0x1;break;
case 1:ret|=0x2;break;
case 3:ret|=0x8;break;
}
switch (sb.hw.dma16) {
case 5:ret|=0x20;break;
case 6:ret|=0x40;break;
case 7:ret|=0x80;break;
}
return ret;
2009-05-03 00:28:34 +02:00
case 0x82: /* IRQ Status */
2009-05-02 23:43:00 +02:00
return (sb.irq.pending_8bit ? 0x1 : 0) |
(sb.irq.pending_16bit ? 0x2 : 0);
2009-05-03 00:28:34 +02:00
default:
if ( ((sb.type == SBT_PRO1 || sb.type == SBT_PRO2) && sb.mixer.index==0x0c) || /* Input control on SBPro */
(sb.type == SBT_16 && sb.mixer.index >= 0x3b && sb.mixer.index <= 0x47)) /* New SB16 registers */
2009-05-03 00:18:08 +02:00
ret = sb.mixer.unhandled[sb.mixer.index];
else
ret=0xa;
2009-05-02 23:43:00 +02:00
LOG(LOG_SB,LOG_WARN)("MIXER:Read from unhandled index %X",sb.mixer.index);
2009-05-02 23:35:44 +02:00
}
return ret;
}
2009-05-03 00:02:15 +02:00
static Bitu read_sb(Bitu port,Bitu iolen) {
2009-05-02 23:35:44 +02:00
switch (port-sb.hw.base) {
case MIXER_INDEX:
return sb.mixer.index;
case MIXER_DATA:
2009-05-03 00:02:15 +02:00
return CTMIXER_Read();
2009-05-02 23:03:37 +02:00
case DSP_READ_DATA:
return DSP_ReadData();
case DSP_READ_STATUS:
//TODO See for high speed dma :)
2009-05-02 23:43:00 +02:00
sb.irq.pending_8bit=false;
2009-05-02 23:35:44 +02:00
if (sb.dsp.out.used) return 0xff;
2009-05-02 23:03:37 +02:00
else return 0x7f;
2009-05-03 00:02:15 +02:00
case DSP_ACK_16BIT:
sb.irq.pending_16bit=false;
break;
2009-05-02 23:03:37 +02:00
case DSP_WRITE_STATUS:
2009-05-02 23:35:44 +02:00
switch (sb.dsp.state) {
2009-05-02 23:03:37 +02:00
case DSP_S_NORMAL:
2009-05-02 23:35:44 +02:00
sb.dsp.write_busy++;
if (sb.dsp.write_busy & 8) return 0xff;
2009-05-02 23:03:37 +02:00
return 0x7f;
case DSP_S_RESET:
return 0xff;
}
return 0xff;
case DSP_RESET:
return 0xff;
default:
2009-05-02 23:43:00 +02:00
LOG(LOG_SB,LOG_NORMAL)("Unhandled read from SB Port %4X",port);
2009-05-02 23:03:37 +02:00
break;
}
return 0xff;
}
2009-05-03 00:02:15 +02:00
static void write_sb(Bitu port,Bitu val,Bitu iolen) {
2009-05-02 23:35:44 +02:00
switch (port-sb.hw.base) {
2009-05-02 23:03:37 +02:00
case DSP_RESET:
DSP_DoReset(val);
break;
case DSP_WRITE_DATA:
DSP_DoWrite(val);
break;
2009-05-02 23:35:44 +02:00
case MIXER_INDEX:
sb.mixer.index=val;
break;
case MIXER_DATA:
2009-05-03 00:02:15 +02:00
CTMIXER_Write(val);
2009-05-02 23:35:44 +02:00
break;
2009-05-02 23:03:37 +02:00
default:
2009-05-02 23:43:00 +02:00
LOG(LOG_SB,LOG_NORMAL)("Unhandled write to SB Port %4X",port);
2009-05-02 23:03:37 +02:00
break;
}
}
2009-05-03 00:02:15 +02:00
static void adlib_gusforward(Bitu port,Bitu val,Bitu iolen) {
adlib_commandreg=val;
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
static void SBLASTER_CallBack(Bitu len) {
switch (sb.mode) {
case MODE_NONE:
case MODE_DMA_PAUSE:
case MODE_DMA_MASKED:
sb.chan->AddSilence();
break;
case MODE_DAC:
// GenerateDACSound(len);
// break;
if (!sb.dac.used) {
sb.mode=MODE_NONE;
return;
}
sb.chan->AddStretched(sb.dac.used,sb.dac.data);
sb.dac.used=0;
break;
case MODE_DMA:
len*=sb.dma.mul;
if (len&SB_SH_MASK) len+=1 << SB_SH;
len>>=SB_SH;
if (len>sb.dma.left) len=sb.dma.left;
GenerateDMASound(len);
break;
}
}
2009-05-03 00:18:08 +02:00
class SBLASTER: public Module_base {
private:
/* Data */
IO_ReadHandleObject ReadHandler[0x10];
IO_WriteHandleObject WriteHandler[0x10];
AutoexecObject autoexecline;
MixerObject MixerChan;
/* Support Functions */
void Find_Type_And_Opl(Section_prop* config,SB_TYPES& type, OPL_Mode& opl_mode){
const char * sbtype=config->Get_string("sbtype");
if (!strcasecmp(sbtype,"sb1")) type=SBT_1;
else if (!strcasecmp(sbtype,"sb2")) type=SBT_2;
else if (!strcasecmp(sbtype,"sbpro1")) type=SBT_PRO1;
else if (!strcasecmp(sbtype,"sbpro2")) type=SBT_PRO2;
else if (!strcasecmp(sbtype,"sb16")) type=SBT_16;
else if (!strcasecmp(sbtype,"none")) type=SBT_NONE;
else type=SBT_16;
if (type==SBT_16) {
if ((machine!=MCH_VGA) || !SecondDMAControllerAvailable()) type=SBT_PRO2;
}
/* OPL/CMS Init */
const char * omode=config->Get_string("oplmode");
if (!strcasecmp(omode,"none")) opl_mode=OPL_none;
else if (!strcasecmp(omode,"cms")) opl_mode=OPL_cms;
else if (!strcasecmp(omode,"opl2")) opl_mode=OPL_opl2;
else if (!strcasecmp(omode,"dualopl2")) opl_mode=OPL_dualopl2;
else if (!strcasecmp(omode,"opl3")) opl_mode=OPL_opl3;
/* Else assume auto */
else {
switch (type) {
case SBT_NONE:opl_mode=OPL_none;break;
case SBT_1:opl_mode=OPL_opl2;break;
case SBT_2:opl_mode=OPL_opl2;break;
case SBT_PRO1:opl_mode=OPL_dualopl2;break;
case SBT_PRO2:
case SBT_16:
opl_mode=OPL_opl3;break;
}
}
}
public:
SBLASTER(Section* configuration):Module_base(configuration) {
Bitu i;
Section_prop * section=static_cast<Section_prop *>(configuration);
sb.hw.base=section->Get_hex("sbbase");
sb.hw.irq=section->Get_int("irq");
sb.hw.dma8=section->Get_int("dma");
sb.hw.dma16=section->Get_int("hdma");
sb.mixer.enabled=section->Get_bool("mixer");
Bitu oplrate=section->Get_int("oplrate");
sb.mixer.stereo=false;
2009-05-03 00:28:34 +02:00
OPL_Mode opl_mode = OPL_none;
2009-05-03 00:18:08 +02:00
Find_Type_And_Opl(section,sb.type,opl_mode);
switch (opl_mode) {
case OPL_none:
WriteHandler[0].Install(0x388,adlib_gusforward,IO_MB);
break;
case OPL_cms:
WriteHandler[0].Install(0x388,adlib_gusforward,IO_MB);
CMS_Init(section);
break;
case OPL_opl2:
CMS_Init(section);
case OPL_dualopl2:
case OPL_opl3:
OPL_Init(section,opl_mode);
break;
}
if (sb.type==SBT_NONE) return;
sb.chan=MixerChan.Install(&SBLASTER_CallBack,22050,"SB");
sb.dsp.state=DSP_S_NORMAL;
2009-05-02 23:35:44 +02:00
2009-05-03 00:18:08 +02:00
for (i=4;i<=0xf;i++) {
if (i==8 || i==9) continue;
//Disable mixer ports for lower soundblaster
if ((sb.type==SBT_1 || sb.type==SBT_2) && (i==4 || i==5)) continue;
ReadHandler[i].Install(sb.hw.base+i,read_sb,IO_MB);
WriteHandler[i].Install(sb.hw.base+i,write_sb,IO_MB);
}
DSP_Reset();
CTMIXER_Reset();
// The documentation does not specify if SB gets initialized with the speaker enabled
// or disabled. Real SBPro2 has it disabled.
sb.speaker=false;
// On SB16 the speaker flag does not affect actual speaker state.
if (sb.type == SBT_16) sb.chan->Enable(true);
else sb.chan->Enable(false);
2009-05-03 00:28:34 +02:00
// Create set blaster line
ostringstream temp;
temp << "SET BLASTER=A" << setw(3)<< hex << sb.hw.base
<< " I" << dec << sb.hw.irq << " D"<< sb.hw.dma8;
if (sb.type==SBT_16) temp << " H" << sb.hw.dma16;
temp << " T" << static_cast<unsigned int>(sb.type) << ends;
autoexecline.Install(temp.str());
2009-05-03 00:18:08 +02:00
/* Soundblaster midi interface */
if (!MIDI_Available()) sb.midi = false;
else sb.midi = true;
}
2009-05-03 00:02:15 +02:00
2009-05-03 00:18:08 +02:00
~SBLASTER() {
Section_prop * section=static_cast<Section_prop *>(m_configuration);
2009-05-03 00:28:34 +02:00
OPL_Mode opl_mode = OPL_none;
2009-05-03 00:18:08 +02:00
Find_Type_And_Opl(section,sb.type,opl_mode);
switch (opl_mode) {
case OPL_none:
break;
case OPL_cms:
CMS_ShutDown(m_configuration);
break;
case OPL_opl2:
CMS_ShutDown(m_configuration);
case OPL_dualopl2:
case OPL_opl3:
OPL_ShutDown(m_configuration);
break;
2009-05-03 00:02:15 +02:00
}
2009-05-03 00:18:08 +02:00
if (sb.type==SBT_NONE) return;
DSP_Reset();//Stop everything
}
}; //End of SBLASTER class
static SBLASTER* test;
void SBLASTER_ShutDown(Section* sec) {
delete test;
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
2009-05-03 00:18:08 +02:00
void SBLASTER_Init(Section* sec) {
test = new SBLASTER(sec);
sec->AddDestroyFunction(&SBLASTER_ShutDown,true);
}