package org.jpc.emulator.peripheral;
import org.jpc.emulator.AbstractHardwareComponent;
import org.jpc.emulator.HardwareComponent;
import org.jpc.emulator.motherboard.*;
import org.jpc.j2se.Option;
import org.jpc.support.Clock;
import java.util.logging.*;
public class SBlaster extends AbstractHardwareComponent implements IODevice
{
private static final Logger Log = Logger.getLogger(SBlaster.class.getName());
private static InterruptController irqDevice;
private static Clock timeSource;
private static DMAController dma;
private static boolean ioportRegistered = false;
public static final String BLASTER = "SET BLASTER=A220 I7 D1 T3";
public static final int BASE = 0x220;
public static final int IRQ = 7;
public static final int DMA = 1;
public static final int HDMA = 5;
public static final int OPL_RATE = 44100;
public static final String OPLEMU = "default";
public static int oplmode;
private static final boolean DEBUG = false;
static final private int SB_PIC_EVENTS = 0;
static final private int DSP_MAJOR = 3;
static final private int DSP_MINOR = 1;
static final private int MIXER_INDEX = 0x04;
static final private int MIXER_DATA = 0x05;
static final private int DSP_RESET = 0x06;
static final private int DSP_READ_DATA = 0x0A;
static final private int DSP_WRITE_DATA = 0x0C;
static final private int DSP_WRITE_STATUS = 0x0C;
static final private int DSP_READ_STATUS = 0x0E;
static final private int DSP_ACK_16BIT = 0x0f;
static final private int DSP_NO_COMMAND = 0;
static final private int DMA_BUFSIZE = 1024;
static final private int DSP_BUFSIZE = 64;
static final private int DSP_DACSIZE = 512;
//Should be enough for sound generated in millisecond blocks
static final private int SB_BUF_SIZE = 8096;
static final private int SB_SH = 14;
static final private int SB_SH_MASK = ((1 << SB_SH)-1);
static final private int DSP_S_RESET = 0;
static final private int DSP_S_RESET_WAIT = 1;
static final private int DSP_S_NORMAL = 2;
static final private int DSP_S_HIGHSPEED = 3;
// SB_TYPES
static final private int SBT_NONE = 0;
static final private int SBT_1 = 1;
static final private int SBT_PRO1 = 2;
static final private int SBT_2 = 3;
static final private int SBT_PRO2 = 4;
static final private int SBT_16 = 6;
static final private int SBT_GB = 7;
// SB_IRQS
static final private int SB_IRQ_8 = 0;
static final private int SB_IRQ_16 = 1;
static final private int SB_IRQ_MPU = 2;
// DSP_MODES
static final private int MODE_NONE = 0;
static final private int MODE_DAC = 1;
static final private int MODE_DMA = 2;
static final private int MODE_DMA_PAUSE = 3;
static final private int MODE_DMA_MASKED = 4;
// DMA_MODES
static final private int DSP_DMA_NONE = 0;
static final private int DSP_DMA_2 = 1;
static final private int DSP_DMA_3 = 2;
static final private int DSP_DMA_4 = 3;
static final private int DSP_DMA_8 = 4;
static final private int DSP_DMA_16 = 5;
static final private int DSP_DMA_16_ALIASED = 6;
static final private int PLAY_MONO = 0;
static final private int PLAY_STEREO = 1;
static private class SB_INFO {
/*Bitu*/int freq;
static private class Dma {
boolean stereo,sign,autoinit;
/*DMA_MODES*/int mode;
/*Bitu*/int rate,mul;
/*Bitu*/int total,left,min;
/*Bit64u*/long start;
static private class Buf {
/*Bit8u*/ byte[] b8 = new byte[DMA_BUFSIZE];
/*Bit16s*/ short[] b16 = new short[DMA_BUFSIZE];
byte[] b16tmp = new byte[2*DMA_BUFSIZE];
}
Buf buf = new Buf();
/*Bitu*/int bits;
DMAController.DMAChannel chan;
int position;
/*Bitu*/int remain_size;
}
Dma dma = new Dma();
boolean speaker;
boolean midi;
/*Bit8u*/short time_constant;
/*DSP_MODES*/int mode;
/*SB_TYPES*/int type;
static private class Irq {
boolean pending_8bit;
boolean pending_16bit;
}
Irq irq = new Irq();
static private class Dsp {
/*Bit8u*/short state;
/*Bit8u*/short cmd;
/*Bit8u*/short cmd_len;
/*Bit8u*/short cmd_in_pos;
/*Bit8u*/short[] cmd_in = new short[DSP_BUFSIZE];
static private class Data {
/*Bit8u*/short lastval;
/*Bit8u*/short[] data = new short[DSP_BUFSIZE];
/*Bitu*/int pos,used;
}
Data in = new Data();
Data out = new Data();
/*Bit8u*/short test_register;
/*Bitu*/int write_busy;
}
Dsp dsp = new Dsp();
private static class Dac {
/*Bit16s*/short[] data = new short[DSP_DACSIZE+1];
/*Bitu*/int used;
/*Bit16s*/short last;
}
Dac dac = new Dac();
private static class _Mixer {
/*Bit8u*/short index;
/*Bit8u*/short[] dac = new short[2],fm = new short[2],cda =new short[2],master = new short[2],lin = new short[2];
/*Bit8u*/short mic;
boolean stereo;
boolean enabled;
boolean filtered;
/*Bit8u*/short[] unhandled = new short[0x48];
}
_Mixer mixer = new _Mixer();
private static class Adpcm {
/*Bit8u*/ShortRef reference = new ShortRef();
/*Bits*/IntRef stepsize = new IntRef(0);
boolean haveref;
}
Adpcm adpcm = new Adpcm();
private static class Hw {
/*Bitu*/int base;
/*Bitu*/int irq;
/*Bit8u*/short dma8,dma16;
}
Hw hw = new Hw();
private static class E2 {
/*Bits*/int value;
/*Bitu*/int count;
}
E2 e2 = new E2();
Mixer.MixerChannel chan;
}
private static SB_INFO sb;
private final static String copyright_string="COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
// number of bytes in input for commands (sb/sbpro)
private final static /*Bit8u*/byte[] DSP_cmd_len_sb = new byte[] {
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x00
// 1,0,0,0, 2,0,2,2, 0,0,0,0, 0,0,0,0, // 0x10
1,0,0,0, 2,2,2,2, 0,0,0,0, 0,0,0,0, // 0x10 Wari hack
0,0,0,0, 2,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0, // 0x30
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
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xb0
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 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
};
// number of bytes in input for commands (sb16)
private final static /*Bit8u*/byte[] DSP_cmd_len_sb16 = new byte[] {
0,0,0,0, 1,2,0,0, 1,0,0,0, 0,0,2,1, // 0x00
// 1,0,0,0, 2,0,2,2, 0,0,0,0, 0,0,0,0, // 0x10
1,0,0,0, 2,2,2,2, 0,0,0,0, 0,0,0,0, // 0x10 Wari hack
0,0,0,0, 2,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0, // 0x30
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,1,0,0, 0,0,0,0 // 0xf0
};
private static /*Bit8u*/short[] ASP_regs = new short[256];
private static boolean ASP_init_in_progress = false;
private final static int[][] E2_incr_table = new int[][] {
{ 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 }
};
private static void DSP_SetSpeaker(boolean how) {
if (sb.speaker==how) return;
sb.speaker=how;
if (sb.type==SBT_16) return;
sb.chan.Enable(how);
if (sb.speaker) {
//Pic.PIC_RemoveEvents(DMA_Silent_Event);
CheckDMAEnd();
} else {
}
}
private static void SB_RaiseIRQ(/*SB_IRQS*/int type) {
Log.log(Level.FINE,"Raising IRQ");
switch (type) {
case SB_IRQ_8:
if (sb.irq.pending_8bit) {
// LOG_MSG("SB: 8bit irq pending");
return;
}
sb.irq.pending_8bit=true;
//Pic.PIC_ActivateIRQ(sb.hw.irq);
irqDevice.setIRQ(sb.hw.irq, 1);
break;
case SB_IRQ_16:
if (sb.irq.pending_16bit) {
// LOG_MSG("SB: 16bit irq pending");
return;
}
sb.irq.pending_16bit=true;
//Pic.PIC_ActivateIRQ(sb.hw.irq);
irqDevice.setIRQ(sb.hw.irq, 1);
break;
default:
break;
}
}
private static void DSP_FlushData() {
sb.dsp.out.used=0;
sb.dsp.out.pos=0;
}
private static DMAEventHandler DSP_DMA_CallBack = new DMAEventHandler() {
public void handleDMAEvent(DMAEvent event) {
if (event == DMAEvent.DMA_REACHED_TC) return;
else if (event == DMAEvent.DMA_MASKED) {
if (sb.mode==MODE_DMA) {
GenerateDMASound(sb.dma.min);
sb.mode=MODE_DMA_MASKED;
// DSP_ChangeMode(MODE_DMA_MASKED);
Log.log(Level.INFO,"DMA masked, stopping output. ");
}
} else if (event == DMAEvent.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(Level.INFO,"DMA unmasked, starting output. ");
}
}
}
};
static final private int MIN_ADAPTIVE_STEP_SIZE = 0;
static final private int MAX_ADAPTIVE_STEP_SIZE = 32767;
static final private int DC_OFFSET_FADE = 254;
final private static /*Bit8s*/byte[] scaleMap1 = new byte[] {
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 final private /*Bit8u*/short[] adjustMap1 = new short[] {
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
};
private static /*Bit8u*/byte decode_ADPCM_4_sample(/*Bit8u*/int sample,/*Bit8u*/ShortRef reference,/*Bits*/IntRef scale) {
/*Bits*/int samp = sample + scale.value;
if ((samp < 0) || (samp > 63)) {
Log.log(Level.SEVERE,"Bad ADPCM-4 sample");
if(samp < 0 ) samp = 0;
if(samp > 63) samp = 63;
}
/*Bits*/int ref = reference.value + scaleMap1[samp];
if (ref > 0xff) reference.value = 0xff;
else if (ref < 0x00) reference.value = 0x00;
else reference.value = (/*Bit8u*/short)(ref&0xff);
scale.value = (scale.value + adjustMap1[samp]) & 0xff;
return (byte)reference.value;
}
static final private /*Bit8s*/byte[] scaleMap2 = new byte[] {
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 final private /*Bit8u*/short[] adjustMap2 = new short[] {
0, 4, 0, 4,
252, 4, 252, 4, 252, 4, 252, 4,
252, 4, 252, 4, 252, 4, 252, 4,
252, 0, 252, 0
};
private static /*Bit8u*/byte decode_ADPCM_2_sample(/*Bit8u*/int sample,/*Bit8u*/ShortRef reference,/*Bits*/IntRef scale) {
/*Bits*/int samp = sample + scale.value;
if ((samp < 0) || (samp > 23)) {
Log.log(Level.SEVERE,"Bad ADPCM-2 sample");
if(samp < 0 ) samp = 0;
if(samp > 23) samp = 23;
}
/*Bits*/int ref = reference.value + scaleMap2[samp];
if (ref > 0xff) reference.value = 0xff;
else if (ref < 0x00) reference.value = 0x00;
else reference.value = (/*Bit8u*/short)(ref&0xff);
scale.value = (scale.value + adjustMap2[samp]) & 0xff;
return (byte)reference.value;
}
static final private /*Bit8s*/byte[] scaleMap3 = new byte[] {
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 final private /*Bit8u*/short[] adjustMap3 = new short[] {
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
};
static private /*Bit8u*/byte decode_ADPCM_3_sample(/*Bit8u*/int sample,/*Bit8u*/ShortRef reference,/*Bits*/IntRef scale) {
/*Bits*/int samp = sample + scale.value;
if ((samp < 0) || (samp > 39)) {
Log.log(Level.SEVERE,"Bad ADPCM-3 sample");
if(samp < 0 ) samp = 0;
if(samp > 39) samp = 39;
}
/*Bits*/int ref = reference.value + scaleMap3[samp];
if (ref > 0xff) reference.value = 0xff;
else if (ref < 0x00) reference.value = 0x00;
else reference.value = (/*Bit8u*/short)(ref&0xff);
scale.value = (scale.value + adjustMap3[samp]) & 0xff;
return (byte)reference.value;
}
private static void GenerateDMASound(/*Bitu*/int size) {
/*Bitu*/int read=0;/*Bitu*/int done=0;/*Bitu*/int i=0;
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;
switch (sb.dma.mode) {
case DSP_DMA_2:
sb.dma.chan.readMemory(sb.dma.buf.b8, 0, sb.dma.position, size);//.Read(size,sb.dma.buf.b8, 0);
read = size;
if (read!=0 && sb.adpcm.haveref) {
sb.adpcm.haveref=false;
sb.adpcm.reference.value=(short)(sb.dma.buf.b8[0] & 0xFF);
sb.adpcm.stepsize.value=MIN_ADAPTIVE_STEP_SIZE;
i++;
}
for (;i<read;i++) {
Mixer.MixTemp8[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 6) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
Mixer.MixTemp8[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 4) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
Mixer.MixTemp8[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 2) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
Mixer.MixTemp8[done++]=decode_ADPCM_2_sample((sb.dma.buf.b8[i] >> 0) & 0x3,sb.adpcm.reference,sb.adpcm.stepsize);
}
sb.chan.AddSamples_m8(done,Mixer.MixTemp8);
break;
case DSP_DMA_3:
sb.dma.chan.readMemory(sb.dma.buf.b8, 0, sb.dma.position, size);//read = chan.Read(size,sb.dma.buf.b8, 0);
read = size;
if (read!=0 && sb.adpcm.haveref) {
sb.adpcm.haveref=false;
sb.adpcm.reference.value=(short)(sb.dma.buf.b8[0] & 0xFF);
sb.adpcm.stepsize.value=MIN_ADAPTIVE_STEP_SIZE;
i++;
}
for (;i<read;i++) {
Mixer.MixTemp8[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] >>> 5) & 0x7,sb.adpcm.reference,sb.adpcm.stepsize);
Mixer.MixTemp8[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] >> 2) & 0x7,sb.adpcm.reference,sb.adpcm.stepsize);
Mixer.MixTemp8[done++]=decode_ADPCM_3_sample((sb.dma.buf.b8[i] & 0x3) << 1,sb.adpcm.reference,sb.adpcm.stepsize);
}
sb.chan.AddSamples_m8(done, Mixer.MixTemp8);
break;
case DSP_DMA_4:
sb.dma.chan.readMemory(sb.dma.buf.b8, 0, sb.dma.position, size);//read = chan.Read(size,sb.dma.buf.b8, 0);
read = size;
if (read!=0 && sb.adpcm.haveref) {
sb.adpcm.haveref=false;
sb.adpcm.reference.value=(short)(sb.dma.buf.b8[0] & 0xFF);
sb.adpcm.stepsize.value=MIN_ADAPTIVE_STEP_SIZE;
i++;
}
for (;i<read;i++) {
Mixer.MixTemp8[done++]=decode_ADPCM_4_sample(sb.dma.buf.b8[i] >>> 4,sb.adpcm.reference,sb.adpcm.stepsize);
Mixer.MixTemp8[done++]=decode_ADPCM_4_sample(sb.dma.buf.b8[i] & 0xf,sb.adpcm.reference,sb.adpcm.stepsize);
}
sb.chan.AddSamples_m8(done,Mixer.MixTemp8);
break;
case DSP_DMA_8:
if (sb.dma.stereo) {
sb.dma.chan.readMemory(sb.dma.buf.b8, sb.dma.remain_size, sb.dma.position, size);//read=sb.dma.chan.Read(size, sb.dma.buf.b8, sb.dma.remain_size);
read = size;
//System.out.printf("DMA Read 4 : %d\n", read);
/*Bitu*/int total=read+sb.dma.remain_size;
if (!sb.dma.sign) sb.chan.AddSamples_s8(total>>1,sb.dma.buf.b8);
else sb.chan.AddSamples_s8s(total>>>1,sb.dma.buf.b8);
if ((total&1)!=0) {
sb.dma.remain_size=1;
sb.dma.buf.b8[0]=sb.dma.buf.b8[total-1];
} else sb.dma.remain_size=0;
} else {
sb.dma.chan.readMemory(sb.dma.buf.b8, 0, sb.dma.position, size);//read=sb.dma.chan.Read(size,sb.dma.buf.b8,0);
read = size;
if (!sb.dma.sign) sb.chan.AddSamples_m8(read,sb.dma.buf.b8);
else sb.chan.AddSamples_m8s(read,sb.dma.buf.b8);
}
break;
case DSP_DMA_16:
case DSP_DMA_16_ALIASED:
if (sb.dma.stereo) {
/* 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 */
sb.dma.chan.readMemory(sb.dma.buf.b16tmp, sb.dma.remain_size, sb.dma.position, 2*size);//read=sb.dma.chan.Read(size,sb.dma.buf.b16, sb.dma.remain_size)
for (int j=0; j< size; j++)
sb.dma.buf.b16[j+sb.dma.remain_size] = (short)((sb.dma.buf.b16tmp[sb.dma.remain_size+2*j] & 0xff) | (sb.dma.buf.b16tmp[sb.dma.remain_size+2*j+1] << 8));
read = size >> (sb.dma.mode==DSP_DMA_16_ALIASED ? 1:0);
/*Bitu*/int total=read+sb.dma.remain_size;
// #if defined (WORDS_BIGENDIAN)
// if (sb.dma.sign) sb.chan.AddSamples_s16_nonnative(total>>1,sb.dma.buf.b16);
// else sb.chan.AddSamples_s16u_nonnative(total>>1,(Bit16u *)sb.dma.buf.b16);
// #else
if (sb.dma.sign) sb.chan.AddSamples_s16(total>>>1,sb.dma.buf.b16);
else sb.chan.AddSamples_s16u(total>>>1,sb.dma.buf.b16);
//#endif
if ((total&1)!=0) {
sb.dma.remain_size=1;
sb.dma.buf.b16[0]=sb.dma.buf.b16[total-1];
} else sb.dma.remain_size=0;
} else {
//read=sb.dma.chan.Read(size,sb.dma.buf.b16, 0)
sb.dma.chan.readMemory(sb.dma.buf.b16tmp, 0, sb.dma.position, 2*size);
for (int j=0; j< size; j++)
sb.dma.buf.b16[j] = (short)((sb.dma.buf.b16tmp[2*j] & 0xff) | (sb.dma.buf.b16tmp[2*j+1] << 8));
read = size >> (sb.dma.mode==DSP_DMA_16_ALIASED ? 1:0);
// #if static final private intd(WORDS_BIGENDIAN)
// if (sb.dma.sign) sb.chan.AddSamples_m16_nonnative(read,sb.dma.buf.b16);
// else sb.chan.AddSamples_m16u_nonnative(read,(Bit16u *)sb.dma.buf.b16);
// #else
if (sb.dma.sign) sb.chan.AddSamples_m16(read,sb.dma.buf.b16);
else sb.chan.AddSamples_m16u(read,sb.dma.buf.b16);
// #endif
}
//restore buffer length value to byte size in aliased mode
if (sb.dma.mode==DSP_DMA_16_ALIASED) read=read<<1;
break;
default:
Log.log(Level.SEVERE, "Unhandled dma mode "+sb.dma.mode);
sb.mode=MODE_NONE;
return;
}
sb.dma.left-=read;
if (sb.dma.left==0) {
//Pic.PIC_RemoveEvents(END_DMA_Event);
if (sb.dma.mode >= DSP_DMA_16) SB_RaiseIRQ(SB_IRQ_16);
else SB_RaiseIRQ(SB_IRQ_8);
if (!sb.dma.autoinit) {
Log.log(Level.INFO,"Single cycle transfer ended");
sb.mode=MODE_NONE;
sb.dma.mode=DSP_DMA_NONE;
} else {
sb.dma.left=sb.dma.total;
if (sb.dma.left==0) {
Log.log(Level.INFO,"Auto-init transfer with 0 size");
sb.mode=MODE_NONE;
}
}
}
}
private static void DMA_Silent_Event(/*Bitu*/int val) {
if (sb.dma.left<val) val=sb.dma.left;
sb.dma.chan.readMemory(sb.dma.buf.b8, 0, sb.dma.position, val);//Read(val,sb.dma.buf.b8, 0);
int read = val;
sb.dma.left-=read;
if (sb.dma.left==0) {
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;
}
}
if (sb.dma.left!=0) {
/*Bitu*/int bigger=(sb.dma.left > sb.dma.min) ? sb.dma.min : sb.dma.left;
float delay=(bigger*1000.0f)/sb.dma.rate;
//Pic.PIC_AddEvent(DMA_Silent_Event,delay,bigger);
}
}
private static void END_DMA_Event(/*Bitu*/int val) {
GenerateDMASound(val);
}
private static void CheckDMAEnd() {
if (sb.dma.left==0) return;
if (!sb.speaker && sb.type!=SBT_16) {
/*Bitu*/int bigger=(sb.dma.left > sb.dma.min) ? sb.dma.min : sb.dma.left;
float delay=(bigger*1000.0f)/sb.dma.rate;
//Pic.PIC_AddEvent(DMA_Silent_Event,delay,bigger);
Log.log(Level.INFO,"Silent DMA Transfer scheduling IRQ in "+ String.format("%3f", delay)+" milliseconds");
} else if (sb.dma.left<sb.dma.min) {
float delay=(sb.dma.left*1000.0f)/sb.dma.rate;
Log.log(Level.INFO,"Short transfer scheduling IRQ in "+ String.format("%3f", delay)+" milliseconds");
//Pic.PIC_AddEvent(END_DMA_Event,delay,sb.dma.left);
END_DMA_Event(sb.dma.left);
}
}
private static void DSP_ChangeMode(/*DSP_MODES*/int mode) {
if (sb.mode==mode) return;
else sb.chan.FillUp();
sb.mode=mode;
}
// private static Pic.PIC_EventHandler DSP_RaiseIRQEvent = new Pic.PIC_EventHandler() {
// public void call(/*Bitu*/int val) {
// SB_RaiseIRQ(SB_IRQ_8);
// }
// };
static void DSP_DoDMATransfer(/*DMA_MODES*/int mode,/*Bitu*/int freq,boolean stereo) {
String type;
sb.mode=MODE_DMA_MASKED;
sb.chan.FillUp();
sb.dma.left=sb.dma.total;
sb.dma.mode=mode;
sb.dma.stereo=stereo;
sb.irq.pending_8bit=false;
sb.irq.pending_16bit=false;
switch (mode) {
case DSP_DMA_2:
type="2-bits ADPCM";
sb.dma.mul=(1 << SB_SH)/4;
break;
case DSP_DMA_3:
type="3-bits ADPCM";
sb.dma.mul=(1 << SB_SH)/3;
break;
case DSP_DMA_4:
type="4-bits ADPCM";
sb.dma.mul=(1 << SB_SH)/2;
break;
case DSP_DMA_8:
type="8-bits PCM";
sb.dma.mul=(1 << SB_SH);
break;
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);
break;
default:
Log.log(Level.SEVERE,"DSP:Illegal transfer mode "+mode);
return;
}
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);
sb.dma.mode=mode;
//Pic.PIC_RemoveEvents(END_DMA_Event);
//sb.dma.chan.Register_Callback(DSP_DMA_CallBack);
sb.dma.chan.registerEventHandler(DSP_DMA_CallBack);
if (DEBUG) {
Log.log(Level.INFO,"DMA Transfer:"+type+" "+(sb.dma.stereo ? "Stereo" : "Mono")+" "+(sb.dma.autoinit ? "Auto-Init" : "Single-Cycle")+" freq "+freq+" rate "+sb.dma.rate+" size "+sb.dma.total);
}
}
private static void DSP_PrepareDMA_Old(/*DMA_MODES*/int mode,boolean autoinit,boolean sign) {
sb.dma.autoinit=autoinit;
sb.dma.sign=sign;
if (!autoinit) sb.dma.total=1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8);
//sb.dma.chan=DMA.GetDMAChannel(sb.hw.dma8);
sb.dma.chan = dma.getChannel(sb.hw.dma8);
DSP_DoDMATransfer(mode,sb.freq / (sb.mixer.stereo ? 2 : 1),sb.mixer.stereo);
}
private static void DSP_PrepareDMA_New(/*DMA_MODES*/int mode,/*Bitu*/int length,boolean autoinit,boolean stereo) {
/*Bitu*/int freq=sb.freq;
//equal length if data format and dma channel are both 16-bit or 8-bit
sb.dma.total=length;
sb.dma.autoinit=autoinit;
if (mode==DSP_DMA_16) {
if (sb.hw.dma16!=0xff) {
//sb.dma.chan=DMA.GetDMAChannel(sb.hw.dma16);
sb.dma.chan = dma.getChannel(sb.hw.dma16);
// if (sb.dma.chan==null) {
// sb.dma.chan=DMA.GetDMAChannel(sb.hw.dma8);
// mode=DSP_DMA_16_ALIASED;
// sb.dma.total<<=1;
// }
} else {
//sb.dma.chan=DMA.GetDMAChannel(sb.hw.dma8);
sb.dma.chan = dma.getChannel(sb.hw.dma8);
mode=DSP_DMA_16_ALIASED;
//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;
}
} else
{
//sb.dma.chan=DMA.GetDMAChannel(sb.hw.dma8);
sb.dma.chan = dma.getChannel(sb.hw.dma8);
}
DSP_DoDMATransfer(mode,freq,stereo);
}
private static void DSP_AddData(/*Bit8u*/int val) {
if (sb.dsp.out.used<DSP_BUFSIZE) {
/*Bitu*/int start=sb.dsp.out.used+sb.dsp.out.pos;
if (start>=DSP_BUFSIZE) start-=DSP_BUFSIZE;
sb.dsp.out.data[start]=(short)val;
sb.dsp.out.used++;
} else {
Log.log(Level.SEVERE,"DSP:Data Output buffer full");
}
}
private static void DSP_FinishReset() {
DSP_FlushData();
DSP_AddData((short)0xaa);
sb.dsp.state=DSP_S_NORMAL;
}
private static void DSP_Reset() {
Log.log(Level.FINE,"DSP:Reset");
//Pic.PIC_DeActivateIRQ(sb.hw.irq);
irqDevice.setIRQ(sb.hw.irq, 0);
DSP_ChangeMode(MODE_NONE);
DSP_FlushData();
sb.dsp.cmd_len=0;
sb.dsp.in.pos=0;
sb.dsp.write_busy=0;
//Pic.PIC_RemoveEvents(DSP_FinishReset);
sb.dma.left=0;
sb.dma.total=0;
sb.dma.stereo=false;
sb.dma.sign=false;
sb.dma.autoinit=false;
sb.dma.mode=DSP_DMA_NONE;
sb.dma.remain_size=0;
//if (sb.dma.chan!=null) sb.dma.chan.Clear_Request();
sb.freq=22050;
sb.time_constant=45;
sb.dac.used=0;
sb.dac.last=0;
sb.e2.value=0xaa;
sb.e2.count=0;
sb.irq.pending_8bit=false;
sb.irq.pending_16bit=false;
sb.chan.SetFreq(22050);
// DSP_SetSpeaker(false);
//Pic.PIC_RemoveEvents(END_DMA_Event);
}
private static void DSP_DoReset(/*Bit8u*/short val) {
if (((val&1)!=0) && (sb.dsp.state!=DSP_S_RESET)) {
//TODO Get out of highspeed mode
DSP_Reset();
sb.dsp.state=DSP_S_RESET;
} else if (((val&1)==0) && (sb.dsp.state==DSP_S_RESET)) { // reset off
sb.dsp.state=DSP_S_RESET_WAIT;
//Pic.PIC_RemoveEvents(DSP_FinishReset);
//Pic.PIC_AddEvent(DSP_FinishReset,20.0f/1000.0f,0); // 20 microseconds
DSP_FinishReset();
}
}
private static DMATransferCapable DSP_E2_DMA_CallBack = new DMATransferCapable() {
public int handleTransfer(DMAController.DMAChannel c, int position, int size) {
sb.dma.chan = c;
sb.dma.position = position;
throw new IllegalStateException("Figure this out bitch...");
// if (event==DMA.DMAEvent.DMA_UNMASKED) {
// /*Bit8u*/byte[] val=new byte[] {(/*Bit8u*/byte)(sb.e2.value&0xff)};
// DMA.DmaChannel chan=DMA.GetDMAChannel(sb.hw.dma8);
// chan.Register_Callback(null);
// chan.Write(1, val, 0);
// return 1;
// }
// return 0;
}
};
private static DMATransferCapable DSP_ADC_CallBack = new DMATransferCapable() {
public int handleTransfer(DMAController.DMAChannel chan, int position, int size) {
sb.dma.chan = chan;
sb.dma.position = position;
int total = sb.dma.left;
throw new IllegalStateException("Figure this out too, bitch...");
// if (event!=DMA.DMAEvent.DMA_UNMASKED) return 0;
// /*Bit8u*/byte[] val=new byte[] {(byte)128};
// DMA.DmaChannel ch=DMA.GetDMAChannel(sb.hw.dma8);
// while ((sb.dma.left--)!=0) {
// ch.Write(1,val, 0);
// }
// SB_RaiseIRQ(SB_IRQ_8);
// //ch.Register_Callback(null);
// dma.releaseDmaRequest(sb.hw.dma8);
// return total;
}
};
static private boolean DSP_SB16_ONLY() {
if (sb.type != SBT_16) {
Log.log(Level.SEVERE,"DSP:Command "+Integer.toString(sb.dsp.cmd, 16)+" requires SB16");
return true;
}
return false;
}
static private boolean DSP_SB2_ABOVE() {
if (sb.type <= SBT_1) {
Log.log(Level.SEVERE,"DSP:Command "+Integer.toString(sb.dsp.cmd, 16)+" requires SB2 or above");
return true;
}
return false;
}
private static void DSP_DoCommand() {
Log.log(Level.INFO, String.format("DSP Command %X",sb.dsp.cmd));
switch (sb.dsp.cmd) {
case 0x04:
if (sb.type == SBT_16) {
/* SB16 ASP set mode register */
if ((sb.dsp.in.data[0]&0xf1)==0xf1) ASP_init_in_progress=true;
else ASP_init_in_progress=false;
Log.log(Level.INFO,"DSP Unhandled SB16ASP command "+Integer.toString(sb.dsp.cmd, 16)+" (set mode register to "+Integer.toString(sb.dsp.in.data[0],16)+")");
} else {
/* DSP Status SB 2.0/pro version. NOT SB16. */
DSP_FlushData();
if (sb.type == SBT_2) DSP_AddData(0x88);
else if ((sb.type == SBT_PRO1) || (sb.type == SBT_PRO2)) DSP_AddData(0x7b);
else DSP_AddData(0xff); //Everything enabled
}
break;
case 0x05: /* SB16 ASP set codec parameter */
Log.log(Level.SEVERE,"DSP Unhandled SB16ASP command "+Integer.toString(sb.dsp.cmd, 16)+" (set codec parameter)");
break;
case 0x08: /* SB16 ASP get version */
Log.log(Level.SEVERE,"DSP Unhandled SB16ASP command "+Integer.toString(sb.dsp.cmd, 16)+" sub "+Integer.toString(sb.dsp.in.data[0],16));
if (sb.type == SBT_16) {
switch (sb.dsp.in.data[0]) {
case 0x03:
DSP_AddData(0x18); // version ID (??)
break;
default:
Log.log(Level.SEVERE,"DSP Unhandled SB16ASP command "+Integer.toString(sb.dsp.cmd, 16)+" sub "+Integer.toString(sb.dsp.in.data[0], 16));
break;
}
} else {
Log.log(Level.SEVERE,"DSP Unhandled SB16ASP command "+Integer.toString(sb.dsp.cmd, 16)+" sub "+Integer.toString(sb.dsp.in.data[0],16));
}
break;
case 0x0e: /* SB16 ASP set register */
if (sb.type == SBT_16) {
// Log.log(LogTypes.LOG_SB,LogSeverities.LOG_NORMAL,"SB16 ASP set register %X := %X",sb.dsp.in.data[0],sb.dsp.in.data[1]);
ASP_regs[sb.dsp.in.data[0]] = sb.dsp.in.data[1];
} else {
Log.log(Level.SEVERE,"DSP Unhandled SB16ASP command "+Integer.toString(sb.dsp.cmd, 16)+" (set register)");
}
break;
case 0x0f: /* SB16 ASP get register */
if (sb.type == SBT_16) {
if ((ASP_init_in_progress) && (sb.dsp.in.data[0]==0x83)) {
ASP_regs[0x83] = (short)~ASP_regs[0x83];
}
// Log.log(LogTypes.LOG_SB,LogSeverities.LOG_NORMAL,"SB16 ASP get register %X == %X",sb.dsp.in.data[0],ASP_regs[sb.dsp.in.data[0]]);
DSP_AddData(ASP_regs[sb.dsp.in.data[0]]);
} else {
Log.log(Level.SEVERE,"DSP Unhandled SB16ASP command "+Integer.toString(sb.dsp.cmd, 16)+" (get register)");
}
break;
case 0x10: /* Direct DAC */
DSP_ChangeMode(MODE_DAC);
if (sb.dac.used<DSP_DACSIZE) {
sb.dac.data[sb.dac.used++]=(short)((sb.dsp.in.data[0] ^ 0x80) << 8);
sb.dac.data[sb.dac.used++]=(short)((sb.dsp.in.data[0] ^ 0x80) << 8);
}
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);
sb.dma.sign=false;
Log.log(Level.SEVERE,"DSP:Faked ADC for "+sb.dma.total+" bytes");
//DMA.GetDMAChannel(sb.hw.dma8).Register_Callback(DSP_ADC_CallBack);
dma.registerChannel(sb.hw.dma8, DSP_ADC_CallBack);
break;
case 0x14: /* Singe Cycle 8-Bit DMA DAC */
case 0x15: /* Wari hack. Waru uses this one instead of 0x14, but some weird stuff going on there anyway */
case 0x91: /* Singe Cycle 8-Bit DMA High speed DAC */
/* Note: 0x91 is documented only for DSP ver.2.x and 3.x, not 4.x */
DSP_PrepareDMA_Old(DSP_DMA_8,false,false);
break;
case 0x90: /* Auto Init 8-bit DMA High Speed */
case 0x1c: /* Auto Init 8-bit DMA */
if (DSP_SB2_ABOVE()) break; /* Note: 0x90 is documented only for DSP ver.2.x and 3.x, not 4.x */
DSP_PrepareDMA_Old(DSP_DMA_8,true,false);
break;
case 0x38: /* Write to SB MIDI Output */
if (sb.midi) Midi.MIDI_RawOutByte(sb.dsp.in.data[0]);
break;
case 0x40: /* Set Timeconstant */
sb.freq=(1000000 / (256 - sb.dsp.in.data[0]));
/* Nasty kind of hack to allow runtime changing of frequency */
if (sb.dma.mode != DSP_DMA_NONE && sb.dma.autoinit) {
DSP_PrepareDMA_Old(sb.dma.mode,sb.dma.autoinit,sb.dma.sign);
}
break;
case 0x41: /* Set Output Samplerate */
case 0x42: /* Set Input Samplerate */
if (DSP_SB16_ONLY()) break;
sb.freq=(sb.dsp.in.data[0] << 8) | sb.dsp.in.data[1];
break;
case 0x48: /* Set DMA Block Size */
if (DSP_SB2_ABOVE()) break;
//TODO Maybe check limit for new irq?
sb.dma.total=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;
case 0x74: /* 074h : Single Cycle 4-bit ADPCM */
DSP_PrepareDMA_Old(DSP_DMA_4,false,false);
break;
case 0x77: /* 077h : Single Cycle 3-bit(2.6bit) ADPCM Reference*/
sb.adpcm.haveref=true;
case 0x76: /* 074h : Single Cycle 3-bit(2.6bit) ADPCM */
DSP_PrepareDMA_Old(DSP_DMA_3,false,false);
break;
case 0x7d: /* Auto Init 4-bit ADPCM Reference */
if (DSP_SB2_ABOVE()) break;
sb.adpcm.haveref=true;
DSP_PrepareDMA_Old(DSP_DMA_4,true,false);
break;
case 0x17: /* 017h : Single Cycle 2-bit ADPCM Reference*/
sb.adpcm.haveref=true;
case 0x16: /* 074h : Single Cycle 2-bit ADPCM */
DSP_PrepareDMA_Old(DSP_DMA_2,false,false);
break;
case 0x80: /* Silence DAC */
//Pic.PIC_AddEvent(DSP_RaiseIRQEvent,
// (1000.0f*(1+sb.dsp.in.data[0]+(sb.dsp.in.data[1] << 8))/sb.freq));
SB_RaiseIRQ(SB_IRQ_8);
break;
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:
if (DSP_SB16_ONLY()) break;
/* Generic 8/16 bit DMA */
// DSP_SetSpeaker(true); //SB16 always has speaker enabled
sb.dma.sign=(sb.dsp.in.data[0] & 0x10) > 0;
DSP_PrepareDMA_New((sb.dsp.cmd & 0x10)!=0 ? 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
);
break;
case 0xd5: /* Halt 16-bit DMA */
if (DSP_SB16_ONLY()) break;
case 0xd0: /* Halt 8-bit DMA */
// DSP_ChangeMode(MODE_NONE);
// 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
}
sb.mode=MODE_DMA_PAUSE;
//Pic.PIC_RemoveEvents(END_DMA_Event);
break;
case 0xd1: /* Enable Speaker */
DSP_SetSpeaker(true);
break;
case 0xd3: /* Disable Speaker */
DSP_SetSpeaker(false);
break;
case 0xd8: /* Speaker status */
if (DSP_SB2_ABOVE()) break;
DSP_FlushData();
if (sb.speaker) DSP_AddData(0xff);
else DSP_AddData(0x00);
break;
case 0xd6: /* Continue DMA 16-bit */
if (DSP_SB16_ONLY()) break;
case 0xd4: /* Continue DMA 8-bit*/
if (sb.mode==MODE_DMA_PAUSE) {
sb.mode=MODE_DMA_MASKED;
if (sb.dma.chan!=null) //sb.dma.chan.Register_Callback(DSP_DMA_CallBack);
sb.dma.chan.registerEventHandler(DSP_DMA_CallBack);
}
break;
case 0xd9: /* Exit Autoinitialize 16-bit */
if (DSP_SB16_ONLY()) break;
case 0xda: /* Exit Autoinitialize 8-bit */
if (DSP_SB2_ABOVE()) break;
/* Set mode to single transfer so it ends with current block */
sb.dma.autoinit=false; //Should stop itself
break;
case 0xe0: /* DSP Identification - SB2.0+ */
DSP_FlushData();
DSP_AddData(~sb.dsp.in.data[0]);
break;
case 0xe1: /* Get DSP Version */
DSP_FlushData();
switch (sb.type) {
case SBT_1:
DSP_AddData(0x1);DSP_AddData(0x05);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;
default:
break;
}
break;
case 0xe2: /* Weird DMA identification write routine */
{
Log.log(Level.INFO,"DSP Function 0xe2");
for (/*Bitu*/int i = 0; i < 8; i++)
if (((sb.dsp.in.data[0] >> i) & 0x01)!=0) 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++;
//DMA.GetDMAChannel(sb.hw.dma8).Register_Callback(DSP_E2_DMA_CallBack);
dma.registerChannel(sb.hw.dma8, DSP_E2_DMA_CallBack);
}
break;
case 0xe3: /* DSP Copyright */
{
DSP_FlushData();
byte[] b = copyright_string.getBytes();
for (int i=0;i<b.length;i++) {
DSP_AddData(b[i]);
}
DSP_AddData(0);
}
break;
case 0xe4: /* Write Test Register */
sb.dsp.test_register=sb.dsp.in.data[0];
break;
case 0xe8: /* Read Test Register */
DSP_FlushData();
DSP_AddData(sb.dsp.test_register);
break;
case 0xf2: /* Trigger 8bit IRQ */
SB_RaiseIRQ(SB_IRQ_8);
break;
case 0xf3: /* Trigger 16bit IRQ */
DSP_SB16_ONLY();
SB_RaiseIRQ(SB_IRQ_16);
break;
case 0xf8: /* Undocumented, pre-SB16 only */
DSP_FlushData();
DSP_AddData(0);
break;
case 0x30: case 0x31:
Log.log(Level.SEVERE,"DSP:Unimplemented MIDI I/O command "+Integer.toString(sb.dsp.cmd,16));
break;
case 0x34: case 0x35: case 0x36: case 0x37:
if (DSP_SB2_ABOVE()) break;
Log.log(Level.SEVERE,"DSP:Unimplemented MIDI UART command "+Integer.toString(sb.dsp.cmd,16));
break;
case 0x7f: case 0x1f:
if (DSP_SB2_ABOVE()) break;
Log.log(Level.SEVERE,"DSP:Unimplemented auto-init DMA ADPCM command "+Integer.toString(sb.dsp.cmd,16));
break;
case 0x20:
DSP_AddData(0x7f); // fake silent input for Creative parrot
break;
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(Level.SEVERE,"DSP:Unimplemented input command "+Integer.toString(sb.dsp.cmd,16));
break;
case 0xf9: /* SB16 ASP ??? */
if (sb.type == SBT_16) {
Log.log(Level.SEVERE,"SB16 ASP unknown function "+Integer.toString(sb.dsp.in.data[0],16));
// just feed it what it expects
switch (sb.dsp.in.data[0]) {
case 0x0b:
DSP_AddData(0x00);
break;
case 0x0e:
DSP_AddData(0xff);
break;
case 0x0f:
DSP_AddData(0x07);
break;
case 0x23:
DSP_AddData(0x00);
break;
case 0x24:
DSP_AddData(0x00);
break;
case 0x2b:
DSP_AddData(0x00);
break;
case 0x2c:
DSP_AddData(0x00);
break;
case 0x2d:
DSP_AddData(0x00);
break;
case 0x37:
DSP_AddData(0x38);
break;
default:
DSP_AddData(0x00);
break;
}
} else {
Log.log(Level.SEVERE,"SB16 ASP unknown function "+Integer.toString(sb.dsp.cmd,16));
}
break;
default:
Log.log(Level.SEVERE,"DSP:Unhandled (undocumented) command "+Integer.toString(sb.dsp.cmd,16));
break;
}
sb.dsp.cmd=DSP_NO_COMMAND;
sb.dsp.cmd_len=0;
sb.dsp.in.pos=0;
}
private static void DSP_DoWrite(/*Bit8u*/short val) {
switch (sb.dsp.cmd) {
case DSP_NO_COMMAND:
sb.dsp.cmd=val;
if (sb.type == SBT_16) sb.dsp.cmd_len=DSP_cmd_len_sb16[val];
else sb.dsp.cmd_len=DSP_cmd_len_sb[val];
sb.dsp.in.pos=0;
if (sb.dsp.cmd_len==0) DSP_DoCommand();
break;
default:
sb.dsp.in.data[sb.dsp.in.pos]=val;
sb.dsp.in.pos++;
if (sb.dsp.in.pos>=sb.dsp.cmd_len)
DSP_DoCommand();
}
}
private static /*Bit8u*/short DSP_ReadData() {
/* Static so it repeats the last value on succesive reads (JANGLE DEMO) */
if (sb.dsp.out.used!=0) {
sb.dsp.out.lastval=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--;
}
return sb.dsp.out.lastval;
}
//The soundblaster manual says 2.0 Db steps but we'll go for a bit less
static private float CALCVOL(float _VAL) { return (float)Math.pow(10.0f,((float)(31-_VAL)*-1.3f)/20);}
private static void CTMIXER_UpdateVolumes() {
if (!sb.mixer.enabled) return;
Mixer.MixerChannel chan;
//adjust to get linear master volume slider in trackers
chan=Mixer.MIXER_FindChannel("SB");
if (chan!=null) 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]));
chan=Mixer.MIXER_FindChannel("FM");
if (chan!=null) 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]));
chan=Mixer.MIXER_FindChannel("CDAUDIO");
if (chan!=null) chan.SetVolume((float)(sb.mixer.master[0])/31.0f*CALCVOL(sb.mixer.cda[0]),
(float)(sb.mixer.master[1])/31.0f*CALCVOL(sb.mixer.cda[1]));
}
private static void CTMIXER_Reset() {
sb.mixer.fm[0]=
sb.mixer.fm[1]=
sb.mixer.cda[0]=
sb.mixer.cda[1]=
sb.mixer.dac[0]=
sb.mixer.dac[1]=31;
sb.mixer.master[0]=
sb.mixer.master[1]=31;
CTMIXER_UpdateVolumes();
}
static private void SETPROVOL(short[] _WHICH_,int _VAL_) {
_WHICH_[0]= (short) ((((_VAL_) & 0xf0) >> 3)|(sb.type==SBT_16 ? 1:3));
_WHICH_[1]= (short) ((((_VAL_) & 0x0f) << 1)|(sb.type==SBT_16 ? 1:3));
}
static private int MAKEPROVOL(short[] _WHICH_) {
return ((((_WHICH_[0] & 0x1e) << 3) | ((_WHICH_[1] & 0x1e) >> 1)) & (sb.type==SBT_16 ? 0xff:0xee));
}
private static void DSP_ChangeStereo(boolean 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;
}
private static void CTMIXER_Write(/*Bit8u*/short val) {
switch (sb.mixer.index) {
case 0x00: /* Reset */
CTMIXER_Reset();
Log.log(Level.WARNING,"Mixer reset value "+Integer.toString(val,16));
break;
case 0x02: /* Master Volume (SB2 Only) */
SETPROVOL(sb.mixer.master,(val&0xf)|(val<<4));
CTMIXER_UpdateVolumes();
break;
case 0x04: /* DAC Volume (SBPRO) */
SETPROVOL(sb.mixer.dac,val);
CTMIXER_UpdateVolumes();
break;
case 0x06: /* FM output selection, Somewhat obsolete with dual OPL SBpro + FM volume (SB2 Only) */
//volume controls both channels
SETPROVOL(sb.mixer.fm,(val&0xf)|(val<<4));
CTMIXER_UpdateVolumes();
if((val&0x60)!=0) Log.log(Level.WARNING,"Turned FM one channel off. not implemented "+Integer.toString(val,16));
//TODO Change FM Mode if only 1 fm channel is selected
break;
case 0x08: /* CDA Volume (SB2 Only) */
SETPROVOL(sb.mixer.cda,(val&0xf)|(val<<4));
CTMIXER_UpdateVolumes();
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]=(short)(((val & 0x6) << 2)|3);
CTMIXER_UpdateVolumes();
} else {
sb.mixer.mic=(short)(((val & 0x7) << 2)|(sb.type==SBT_16?1:3));
}
break;
case 0x0e: /* Output/Stereo Select */
sb.mixer.stereo=(val & 0x2) > 0;
sb.mixer.filtered=(val & 0x20) > 0;
DSP_ChangeStereo(sb.mixer.stereo);
Log.log(Level.WARNING,"Mixer set to "+(sb.dma.stereo ? "STEREO" : "MONO"));
break;
case 0x22: /* Master Volume (SBPRO) */
SETPROVOL(sb.mixer.master,val);
CTMIXER_UpdateVolumes();
break;
case 0x26: /* FM Volume (SBPRO) */
SETPROVOL(sb.mixer.fm,val);
CTMIXER_UpdateVolumes();
break;
case 0x28: /* CD Audio Volume (SBPRO) */
SETPROVOL(sb.mixer.cda,val);
CTMIXER_UpdateVolumes();
break;
case 0x2e: /* Line-in Volume (SBPRO) */
SETPROVOL(sb.mixer.lin,val);
break;
//case 0x20: /* Master Volume Left (SBPRO) ? */
case 0x30: /* Master Volume Left (SB16) */
if (sb.type==SBT_16) {
sb.mixer.master[0]=(short)(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]=(short)(val>>>3);
CTMIXER_UpdateVolumes();
}
break;
case 0x32: /* DAC Volume Left (SB16) */
if (sb.type==SBT_16) {
sb.mixer.dac[0]=(short)(val>>>3);
CTMIXER_UpdateVolumes();
}
break;
case 0x33: /* DAC Volume Right (SB16) */
if (sb.type==SBT_16) {
sb.mixer.dac[1]=(short)(val>>>3);
CTMIXER_UpdateVolumes();
}
break;
case 0x34: /* FM Volume Left (SB16) */
if (sb.type==SBT_16) {
sb.mixer.fm[0]=(short)(val>>>3);
CTMIXER_UpdateVolumes();
}
break;
case 0x35: /* FM Volume Right (SB16) */
if (sb.type==SBT_16) {
sb.mixer.fm[1]=(short)(val>>>3);
CTMIXER_UpdateVolumes();
}
break;
case 0x36: /* CD Volume Left (SB16) */
if (sb.type==SBT_16) {
sb.mixer.cda[0]=(short)(val>>>3);
CTMIXER_UpdateVolumes();
}
break;
case 0x37: /* CD Volume Right (SB16) */
if (sb.type==SBT_16) {
sb.mixer.cda[1]=(short)(val>>>3);
CTMIXER_UpdateVolumes();
}
break;
case 0x38: /* Line-in Volume Left (SB16) */
if (sb.type==SBT_16) sb.mixer.lin[0]=(short)(val>>>3);
break;
case 0x39: /* Line-in Volume Right (SB16) */
if (sb.type==SBT_16) sb.mixer.lin[1]=(short)(val>>>3);
break;
case 0x3a:
if (sb.type==SBT_16) sb.mixer.mic=(short)(val>>>3);
break;
case 0x80: /* IRQ Select */
sb.hw.irq=0xff;
if ((val & 0x1)!=0) sb.hw.irq=2;
else if ((val & 0x2)!=0) sb.hw.irq=5;
else if ((val & 0x4)!=0) sb.hw.irq=7;
else if ((val & 0x8)!=0) sb.hw.irq=10;
break;
case 0x81: /* DMA Select */
sb.hw.dma8=0xff;
sb.hw.dma16=0xff;
if ((val & 0x1)!=0) sb.hw.dma8=0;
else if ((val & 0x2)!=0) sb.hw.dma8=1;
else if ((val & 0x8)!=0) sb.hw.dma8=3;
if ((val & 0x20)!=0) sb.hw.dma16=5;
else if ((val & 0x40)!=0) sb.hw.dma16=6;
else if ((val & 0x80)!=0) sb.hw.dma16=7;
Log.log(Level.INFO,"Mixer select dma8:"+Integer.toString(sb.hw.dma8,16)+" dma16:"+Integer.toString(sb.hw.dma16,16));
break;
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 */
sb.mixer.unhandled[sb.mixer.index] = val;
Log.log(Level.WARNING,"MIXER:Write "+Integer.toString(val, 16)+" to unhandled index "+Integer.toString(sb.mixer.index,16));
}
}
private static /*Bit8u*/int CTMIXER_Read() {
/*Bit8u*/short ret;
// if ( sb.mixer.index< 0x80) LOG_MSG("Read mixer %x",sb.mixer.index);
switch (sb.mixer.index) {
case 0x00: /* RESET */
return 0x00;
case 0x02: /* Master Volume (SB2 Only) */
return ((sb.mixer.master[1]>>>1) & 0xe);
case 0x22: /* Master Volume (SBPRO) */
return MAKEPROVOL(sb.mixer.master);
case 0x04: /* DAC Volume (SBPRO) */
return MAKEPROVOL(sb.mixer.dac);
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));
case 0x0e: /* Output/Stereo Select */
return 0x11|(sb.mixer.stereo ? 0x02 : 0x00)|(sb.mixer.filtered ? 0x20 : 0x00);
case 0x26: /* FM Volume (SBPRO) */
return MAKEPROVOL(sb.mixer.fm);
case 0x28: /* CD Audio Volume (SBPRO) */
return MAKEPROVOL(sb.mixer.cda);
case 0x2e: /* Line-IN Volume (SBPRO) */
return MAKEPROVOL(sb.mixer.lin);
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;
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;
case 0x82: /* IRQ Status */
return (sb.irq.pending_8bit ? 0x1 : 0) |
(sb.irq.pending_16bit ? 0x2 : 0) |
((sb.type == SBT_16) ? 0x20 : 0);
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 */
ret = sb.mixer.unhandled[sb.mixer.index];
else
ret=0xa;
Log.log(Level.WARNING,"MIXER:Read from unhandled index "+Integer.toString(sb.mixer.index,16));
}
return ret;
}
// private static IoHandler.IO_ReadHandler read_sb = new IoHandler.IO_ReadHandler() {
// public /*Bitu*/int call(/*Bitu*/int port, /*Bitu*/int iolen) {
// switch (port-sb.hw.base) {
// case MIXER_INDEX:
// return sb.mixer.index;
// case MIXER_DATA:
// return CTMIXER_Read();
// case DSP_READ_DATA:
// return DSP_ReadData();
// case DSP_READ_STATUS:
// //TODO See for high speed dma :)
// if (sb.irq.pending_8bit) {
// sb.irq.pending_8bit=false;
// //Pic.PIC_DeActivateIRQ(sb.hw.irq);
// irqDevice.setIRQ(sb.hw.irq, 0);
// }
// if (sb.dsp.out.used!=0) return 0xff;
// else return 0x7f;
// case DSP_ACK_16BIT:
// sb.irq.pending_16bit=false;
// break;
// case DSP_WRITE_STATUS:
// switch (sb.dsp.state) {
// case DSP_S_NORMAL:
// sb.dsp.write_busy++;
// if ((sb.dsp.write_busy & 8)!=0) return 0xff;
// return 0x7f;
// case DSP_S_RESET:
// case DSP_S_RESET_WAIT:
// return 0xff;
// }
// return 0xff;
// case DSP_RESET:
// return 0xff;
// default:
// Log.log(Level.SEVERE,"Unhandled read from SB Port "+Integer.toString(port, 16));
// break;
// }
// return 0xff;
// }
// };
// private static IoHandler.IO_WriteHandler write_sb = new IoHandler.IO_WriteHandler() {
// public void call(/*Bitu*/int port, /*Bitu*/int val, /*Bitu*/int iolen) {
// /*Bit8u*/short val8=(/*Bit8u*/short)(val&0xff);
// switch (port-sb.hw.base) {
// case DSP_RESET:
// DSP_DoReset(val8);
// break;
// case DSP_WRITE_DATA:
// DSP_DoWrite(val8);
// break;
// case MIXER_INDEX:
// sb.mixer.index=val8;
// break;
// case MIXER_DATA:
// CTMIXER_Write(val8);
// break;
// default:
// Log.log(Level.SEVERE,"Unhandled write to SB Port "+Integer.toString(port,16));
// break;
// }
// }
// };
// private static IoHandler.IO_WriteHandler adlib_gusforward = new IoHandler.IO_WriteHandler() {
// public void call(/*Bitu*/int port, /*Bitu*/int val, /*Bitu*/int iolen) {
// //Gus.adlib_commandreg=(/*Bit8u*/short)(val&0xff);
// }
// };
public static boolean SB_Get_Address(/*Bitu*/IntRef sbaddr, /*Bitu*/IntRef sbirq, /*Bitu*/IntRef sbdma) {
sbaddr.value = 0;
sbirq.value = 0;
sbdma.value = 0;
if (sb.type == SBT_NONE) return false;
else {
sbaddr.value = sb.hw.base;
sbirq.value = sb.hw.irq;
sbdma.value = sb.hw.dma8;
return true;
}
}
private static Mixer.MIXER_Handler SBLASTER_CallBack = new Mixer.MIXER_Handler() {
public void call(/*Bitu*/int 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==0) {
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)!=0) len+=1 << SB_SH;
len>>=SB_SH;
if (len>sb.dma.left) len=sb.dma.left;
GenerateDMASound(len);
break;
}
}
};
/* Data */
// private IoHandler.IO_ReadHandleObject[] ReadHandler = new IoHandler.IO_ReadHandleObject[0x10];
// private IoHandler.IO_WriteHandleObject[] WriteHandler = new IoHandler.IO_WriteHandleObject[0x10];
private Mixer.MixerObject MixerChan = new Mixer.MixerObject();
/* Support Functions */
private void Find_Type_And_Opl(/*SB_TYPES*/IntRef type, /*OPL_Mode*/IntRef opl_mode){
String sbtype = Option.sbtype.value("sb16");
if (sbtype.equalsIgnoreCase("sb1")) type.value=SBT_1;
else if (sbtype.equalsIgnoreCase("sb2")) type.value=SBT_2;
else if (sbtype.equalsIgnoreCase("sbpro1")) type.value=SBT_PRO1;
else if (sbtype.equalsIgnoreCase("sbpro2")) type.value=SBT_PRO2;
else if (sbtype.equalsIgnoreCase("sb16")) type.value=SBT_16;
else if (sbtype.equalsIgnoreCase("gb")) type.value=SBT_GB;
else if (sbtype.equalsIgnoreCase("none")) type.value=SBT_NONE;
else type.value=SBT_16;
if (type.value==SBT_16) {
//if ((!Dosbox.IS_EGAVGA_ARCH()) || !DMA.SecondDMAControllerAvailable()) type.value=SBT_PRO2;
}
/* OPL/CMS Init */
switch (type.value) {
case SBT_NONE:
opl_mode.value = 0;
break;
case SBT_GB:
opl_mode.value=1;
break;
case SBT_1:
case SBT_2:
opl_mode.value=2;
break;
case SBT_PRO1:
opl_mode.value=3;
break;
case SBT_PRO2:
case SBT_16:
opl_mode.value=4;
break;
}
}
private void close() {
switch (oplmode) {
case 0:
break;
case 1:
//Gameblaster.CMS_ShutDown(m_configuration);
break;
case 2:
//Gameblaster.CMS_ShutDown(m_configuration);
// fall-through
case 3:
case 4:
//Adlib.OPL_ShutDown(m_configuration);
break;
}
if (sb.type==SBT_NONE || sb.type==SBT_GB) return;
DSP_Reset(); // Stop everything
}
public int ioPortRead8(int address)
{
return ioPortRead32(address);
}
public int ioPortRead16(int address)
{
return ioPortRead32(address);
}
public int ioPortRead32(int port)
{
switch (port-sb.hw.base) {
case MIXER_INDEX:
return sb.mixer.index;
case MIXER_DATA:
return CTMIXER_Read();
case DSP_READ_DATA:
return DSP_ReadData();
case DSP_READ_STATUS:
//TODO See for high speed dma :)
if (sb.irq.pending_8bit) {
sb.irq.pending_8bit=false;
//Pic.PIC_DeActivateIRQ(sb.hw.irq);
irqDevice.setIRQ(sb.hw.irq, 0);
}
if (sb.dsp.out.used!=0) return 0xff;
else return 0x7f;
case DSP_ACK_16BIT:
sb.irq.pending_16bit=false;
break;
case DSP_WRITE_STATUS:
switch (sb.dsp.state) {
case DSP_S_NORMAL:
sb.dsp.write_busy++;
if ((sb.dsp.write_busy & 8)!=0) return 0xff;
return 0x7f;
case DSP_S_RESET:
case DSP_S_RESET_WAIT:
return 0xff;
}
return 0xff;
case DSP_RESET:
return 0xff;
default:
Log.log(Level.SEVERE,"Unhandled read from SB Port "+Integer.toString(port, 16));
break;
}
return 0xff;
}
public void ioPortWrite8(int address, int data)
{
ioPortWrite32(address, data);
}
public void ioPortWrite16(int address, int data)
{
ioPortWrite32(address, data);
}
public void ioPortWrite32(int port, int data)
{
/*Bit8u*/short val8=(/*Bit8u*/short)(data & 0xff);
switch (port-sb.hw.base) {
case DSP_RESET:
DSP_DoReset(val8);
break;
case DSP_WRITE_DATA:
DSP_DoWrite(val8);
break;
case MIXER_INDEX:
sb.mixer.index=val8;
break;
case MIXER_DATA:
CTMIXER_Write(val8);
break;
default:
Log.log(Level.SEVERE,"Unhandled write to SB Port "+Integer.toString(port,16));
break;
}
}
public int[] ioPortsRequested()
{
int base = sb.hw.base;
int[] ports = new int[16-4-2];
int index = 0;
for (int i = 4; i < 16; i++)
{
if ((i==8) || (i==9))
continue;
ports[index++] = base+i;
}
return ports;
}
private static SBlaster test;
public static void SBLASTER_ShutDown() {
test.close();
test = null;
sb = null;
}
public SBlaster()
{
sb = new SB_INFO();
/*Bitu*/int i;
// for (i=0;i<WriteHandler.length;i++) {
// WriteHandler[i] = new IoHandler.IO_WriteHandleObject();
// }
// for (i=0;i<ReadHandler.length;i++) {
// ReadHandler[i] = new IoHandler.IO_ReadHandleObject();
// }
sb.hw.base = Option.sbbase.intValue(BASE, 16);
sb.hw.irq = Option.sb_irq.intValue(IRQ);
/*Bitu*/int dma8bit = Option.sb_dma.intValue(DMA);
if (dma8bit>0xff) dma8bit=0xff;
sb.hw.dma8=(/*Bit8u*/short)(dma8bit&0xff);
/*Bitu*/int dma16bit = Option.sb_hdma.intValue(HDMA);
if (dma16bit>0xff) dma16bit=0xff;
sb.hw.dma16=(/*Bit8u*/short)(dma16bit&0xff);
sb.mixer.enabled = Option.sbmixer.isSet();
sb.mixer.stereo=false;
IntRef t = new IntRef(sb.type);
IntRef o = new IntRef(oplmode);
Find_Type_And_Opl(t,o);
sb.type = t.value;
oplmode = o.value;
switch (oplmode) {
case 0:
//WriteHandler[0].Install(0x388,adlib_gusforward,IoHandler.IO_MB);
break;
case 1:
//WriteHandler[0].Install(0x388,adlib_gusforward,IoHandler.IO_MB);
//Gameblaster.CMS_Init(section);
break;
case 2:
//Gameblaster.CMS_Init(section);
// fall-through
case 3:
case 4:
//Adlib.OPL_Init(oplmode);
break;
}
if (sb.type==SBT_NONE || sb.type==SBT_GB) return;
sb.chan=MixerChan.Install(SBLASTER_CallBack,22050,"SB");
sb.dsp.state=DSP_S_NORMAL;
sb.dsp.out.lastval=0xaa;
//sb.dma.chan=null;
// 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,IoHandler.IO_MB);
// WriteHandler[i].Install(sb.hw.base+i,write_sb,IoHandler.IO_MB);
// }
for (i=0;i<256;i++) ASP_regs[i] = 0;
ASP_regs[5] = 0x01;
ASP_regs[9] = 0xf8;
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);
// String line = String.format("SET BLASTER=A%3x I%d D%d",new Object[]{new Integer(sb.hw.base),new Integer(sb.hw.irq), new Integer(sb.hw.dma8)});
// if (sb.type==SBT_16) line+= " H"+ sb.hw.dma16;
// line+=" T"+sb.type;
// autoexecline.Install(line);
/* Soundblaster midi interface */
if (!Midi.MIDI_Available()) sb.midi = false;
else sb.midi = true;
}
public boolean initialised()
{
return (irqDevice != null) && (timeSource != null) && (dma != null) && ioportRegistered;
}
public boolean updated()
{
return irqDevice.updated() && timeSource.updated() && dma.updated();
}
public void acceptComponent(HardwareComponent component)
{
if ((component instanceof InterruptController) && component.initialised())
irqDevice = (InterruptController) component;
if ((component instanceof Clock) && component.initialised())
timeSource = (Clock) component;
if ((component instanceof IOPortHandler) && component.initialised()) {
((IOPortHandler) component).registerIOPortCapable(this);
ioportRegistered = true;
}
if ((component instanceof DMAController) && component.initialised())
if (((DMAController) component).isPrimary())
{
dma = (DMAController) component;
//dma.registerChannel(sb.hw.dma8 & 3, DSP_DMA_CallBack);
//dma.registerChannel(sb.hw.dma16 & 3, this);
}
if (this.initialised())
{
DSP_Reset();
}
}
}