kaos/KaosSrc/3dsdriv.cpp

510 lines
12 KiB
C++
Raw Permalink Normal View History

2024-10-12 20:32:30 -05: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. *
************************************************************************/
// Nicoso<73>t Galactic Poly-Sounds Player Driver - (C)1997 Nicoso<73>t
#include <dos.h>
#include "std.hpp"
#include "3dsdriv.hpp"
// DSP Commands
#define DSPC_SPEAKER_ON 0xd1
#define DSPC_SPEAKER_OFF 0xd3
#define DSPC_DMA_STOP 0xd0
#define DSPC_DMA_CONTINUE 0xd4
// DSP I/O Ports
#define DSP_MIXER_ADDR 0x04
#define DSP_MIXER_DATA 0x05
#define DSP_RESET 0x06
#define DSP_READ_DATA 0x0a
#define DSP_WRITE_DATA 0x0c
#define DSP_WRITE_STATUS 0x0c
#define DSP_DATA_AVAIL 0x0e
#define MIXER_IRQ_NR 0x80
#define MIXER_DMA_NR 0x81
#define MIXER_AGC_ON 0
#define MIXER_AGC_OFF 1
// SB16 Mixer output defs
#define MIX16_OUTPUT_LINE_LEFT 0x10
#define MIX16_OUTPUT_LINE_RIGHT 0x08
#define MIX16_OUTPUT_CD_LEFT 0x04
#define MIX16_OUTPUT_CD_RIGHT 0x02
#define MIX16_OUTPUT_LINE (MIX16_OUTPUT_LINE_LEFT | MIX16_OUTPUT_LINE_RIGHT)
#define MIX16_OUTPUT_CD (MIX16_OUTPUT_CD_LEFT | MIX16_OUTPUT_CD_RIGHT)
#define MIX16_OUTPUT_MIC 0x01
#define MIX16_DEFAULT_OUTPUT (MIX16_OUTPUT_LINE)
// SB16 Mixer input defs
#define MIX16_INPUT_MIC_LEFT 0x01
#define MIX16_INPUT_CD_LEFT 0x04
#define MIX16_INPUT_LINE_LEFT 0x10
#define MIX16_INPUT_MIDI_LEFT 0x40
#define MIX16_DEFAULT_INPUT_LEFT (MIX16_INPUT_MIC_RIGHT|MIX16_INPUT_LINE_LEFT)
#define MIX16_INPUT_MIC_RIGHT 0x01
#define MIX16_INPUT_CD_RIGHT 0x02
#define MIX16_INPUT_LINE_RIGHT 0x08
#define MIX16_INPUT_MIDI_RIGHT 0x20
#define MIX16_DEFAULT_INPUT_RIGHT (MIX16_INPUT_MIC_RIGHT|MIX16_INPUT_LINE_RIGHT)
// SBPRO Mixer input defs
#define MIXPRO_INPUT_MIC 0x11
#define MIXPRO_INPUT_LINE 0x13
#define MIXPRO_INPUT_CD 0x17
/*
#define DMA_VERIFY_TRANSFER_MODE 0x00
#define DMA_WRITE_TRANSFER_MODE 0x04
#define DMA_READ_TRANSFER_MODE 0x08
#define DMA_AUTOINIT_MODE 0x10
#define DMA_ADDRESS_DECREMENT_MODE 0x20
#define DMA_ADDRESS_INCREMENT_MODE 0x00
#define DMA_DEMAND_MODE 0x00
#define DMA_SINGLE_MODE 0x40
#define DMA_BLOCK_MODE 0x80
#define DMA_CASCADE_MODE 0xc0
*/
typedef struct {
byte left, right;
} TMixIndxTab;
// SB1 Mixer table ???
TMixIndxTab SB1tab[MIXER_DEVICE_NUM]={
//Left Right
{0x22,0x22}, //MIXER MASTER
{0x00,0x00}, //MIXER_BASS
{0x00,0x00}, //MIXER_TREBLE
{0x26,0x26}, //MIXER_MIDI
{0x04,0x04}, //MIXER_VOICE
{0x00,0x00}, //MIXER_SPEAKER
{0x2e,0x2e}, //MIXER_LINE
{0x0a,0x00}, //MIXER_MIC
{0x28,0x28}, //MIXER_CD
{0x00,0x00}, //MIXER_IMIX
{0x00,0x00}, //MIXER_ALTPCM
{0x00,0x00}, //MIXER_AGC
{0x00,0x00}, //MIXER_OUTPUTGAIN
{0x00,0x00} //MIXER_INPUTGAIN
};
// SBPro Mixer table
TMixIndxTab SBPROtab[MIXER_DEVICE_NUM]={
//Left Right
{0x22,0x22}, //MIXER MASTER
{0x00,0x00}, //MIXER_BASS
{0x00,0x00}, //MIXER_TREBLE
{0x26,0x26}, //MIXER_MIDI
{0x04,0x04}, //MIXER_VOICE
{0x00,0x00}, //MIXER_SPEAKER
{0x2e,0x2e}, //MIXER_LINE
{0x0a,0x00}, //MIXER_MIC
{0x28,0x28}, //MIXER_CD
{0x00,0x00}, //MIXER_IMIX
{0x00,0x00}, //MIXER_ALTPCM
{0x00,0x00}, //MIXER_AGC
{0x00,0x00}, //MIXER_OUTPUTGAIN
{0x00,0x00} //MIXER_INPUTGAIN
};
// SB16 Mixer table
TMixIndxTab SB16tab[MIXER_DEVICE_NUM]={
//Left Right
{0x30,0x31}, //MIXER MASTER
{0x46,0x47}, //MIXER_BASS
{0x44,0x45}, //MIXER_TREBLE
{0x34,0x35}, //MIXER_MIDI
{0x32,0x33}, //MIXER_VOICE
{0x3b,0x00}, //MIXER_SPEAKER
{0x38,0x39}, //MIXER_LINE
{0x3a,0x00}, //MIXER_MIC
{0x36,0x37}, //MIXER_CD
{0x00,0x00}, //MIXER_IMIX
{0x00,0x00}, //MIXER_ALTPCM
{0x43,0x00}, //MIXER_AGC
{0x41,0x42}, //MIXER_OUTPUTGAIN
{0x3f,0x40} //MIXER_INPUTGAIN
};
// SB Defaults
/*
unsigned SBdefault[MIXER_DEVICE_NUM] =
{// L R
0xf0f0, // Master (SB16 Max 0xf8)
0x9090, // Bass (SB16 Max 0xf0)
0xd0d0, // Treble (SB16 Max 0xf0)
0x0000, // MIDI (SB16 Max 0xf8)
0xe0e0, // Voice (SB16 Max 0xf8)
0x0000, // PC Speaker (SB16 Max 0xc0)
0xc0c0, // Ext Line
0x0000, // Mic
0x0000, // CD
0x0000, // Recor. Monitor
0x0000, // SB PCM
0x0000, // Agc On (AGC on = 0, off = 1)
0x4040, // Output Gain = 2 (2^)
0x0000 // Input Gain = 2 (Gain=1->0x00 2->0x40 4->0x80 8->0xc0)
};
*/
unsigned SBdefault[MIXER_DEVICE_NUM] =
{// L R
0xc0c0, // Master (SB16 Max 0xf8)
0xa0a0, // Bass (SB16 Max 0xf0)
0x7070, // Treble (SB16 Max 0xf0)
0x7070, // MIDI (SB16 Max 0xf8)
0xc0c0, // Voice (SB16 Max 0xf8)
0x0000, // PC Speaker (SB16 Max 0xc0)
0x9090, // Ext Line
0x0000, // Mic
0x0000, // CD
0x0000, // Recor. Monitor
0x0000, // SB PCM
0x0000, // Agc On (AGC on = 0, off = 1)
0x4040, // Output Gain = 2 (2^)
0x0000 // Input Gain = 2 (Gain=1->0x00 2->0x40 4->0x80 8->0xc0)
};
int sb_ioaddr = 0;
byte sb_DMA8ch = 1, // (1) 0..3
sb_DMA16ch = 1, // (6) 4..7 NOT ACTIVE, not yet
sb_IRQnum = 5;
byte sb_type;
int DMApage[4] = {0x87,0x83,0x81,0x82},
DMAaddr[4] = { 0, 2, 4, 6},
DMAlen[4] = { 1, 3, 5, 7};
TMixIndxTab *mixtab;
word DMAbuffer_size;
byte far *DMAbuffer, far *DMAbufhalf;
byte Old8259Mask;
void interrupt far (*oldsbvect)(...);
#pragma warn -rvl
byte sbmix_read(byte port) {
asm {
MOV DX, sb_ioaddr
ADD DX, DSP_MIXER_ADDR
MOV AL, port
OUT DX, AL
// Delay
MOV CX, 16
CLD
REP IN AL, 0x80
MOV DX, sb_ioaddr
ADD DX, DSP_MIXER_DATA
IN AL, DX
}}
#pragma warn +rvl
void sbmix_write(byte port, byte data) {
asm {
MOV DX, sb_ioaddr
ADD DX, DSP_MIXER_ADDR
MOV AL, port
OUT DX, AL
// Delay
MOV CX, 16
CLD
REP IN AL, 0x80
MOV DX, sb_ioaddr
ADD DX, DSP_MIXER_DATA
MOV AL, data
OUT DX, AL
}}
/*
byte sbmix_detect() {
// !!! Pu<50> provocare uno spike in output ???
// ...ma tutto ci<63> serve a qualcosa ? Secondo me no
sbmix_write(0x26,0xff); // MIDI
sbmix_write(0x04,0x33); // VOICE
if (sbmix_read(0x26) != 0xff) return 0;
if (sbmix_read(0x04) != 0x33) return 0;
sbmix_write(0x26,0x00); // off MIDI
sbmix_write(0x04,0x00); // off VOICE
return 1;
}
*/
char sbmix_setvol(byte dev, unsigned data) {
byte left = data & 0x00ff,
right = (data & 0xff00) >> 8;
if (sb_type == SBT_SBPRO) {
left >>= 4;
right >>= 4;
}
SBdefault[dev] = ((unsigned)right << 8) | left; // Questione visuale
if (mixtab[dev].left != 0x00) {
if (mixtab[dev].left == mixtab[dev].right) {
sbmix_write(mixtab[dev].left,(left<<4)|right);
return 1;
}
sbmix_write(mixtab[dev].left,left);
}
if (mixtab[dev].right != 0x00)
sbmix_write(mixtab[dev].right,right);
return 1;
}
/*
void sbmix_reset() {
!!!
for (int i=0; i<MIXER_DEVICE_NUM; i++)
sbmix_setvol(i, SBdefault[i]);
}
*/
void sbmix_setIRQ() {
char data;
switch (sb_IRQnum) {
case 2: data = 0xf1; break;
case 5: data = 0xf2; break;
case 7: data = 0xf4; break;
case 10: data = 0xf8; break;
// default: error("?");
}
sbmix_write(MIXER_IRQ_NR,data);
}
void sbmix_init() {
//sbmix_write(0,0); // Reset
if (sb_type>=SBT_SB16) mixtab=SB16tab; else
if (sb_type==SBT_SBPRO) mixtab=SBPROtab; else
mixtab=SB1tab; //error("No Mixer ?\n"); return;
//sbmix_reset();
if (sb_type >= SBT_SB16) {
//sbmix_write(0x3c,MIX16_DEFAULT_OUTPUT);
//sbmix_write(0x3d,MIX16_DEFAULT_INPUT_LEFT);
//sbmix_write(0x3e,MIX16_DEFAULT_INPUT_RIGHT);
sbmix_setIRQ();
sbmix_write(MIXER_DMA_NR,(1<<sb_DMA16ch) | (1<<sb_DMA8ch));
} else
if (sb_type == SBT_SBPRO) {
//sbmix_write(0x0c,MIXPRO_INPUT_LINE); // Input = LINE IN
sbmix_write(0x0e,0x13 /* = STEREO; 0x11 = MONO */); // Output = STEREO
}
}
#pragma warn -rvl
byte sb_readDSPsafe() {
asm {
MOV CX, 0x200
MOV DX, sb_ioaddr
ADD DX, DSP_DATA_AVAIL
} cic1: asm {
IN AL, DX
OR AL, AL
JS ready
LOOP cic1
XOR AL, AL
JMP end
} ready: asm {
MOV DX, sb_ioaddr
ADD DX, DSP_READ_DATA
IN AL, DX
} end:; }
#pragma warn +wrvl
void sb_writeDSPsafe(byte data) {
asm {
MOV CX, 0x200
MOV DX, sb_ioaddr
ADD DX, DSP_WRITE_STATUS // == WRITE_DATA
} cic1: asm {
IN AL, DX
OR AL, AL
JNS ready
LOOP cic1
JMP end
} ready: asm {
MOV AL, data
OUT DX, AL
} end:; }
void sb_ackDMA() {
inp(sb_ioaddr+DSP_DATA_AVAIL);
}
char sb_resetDSP() {
asm {
MOV DX, sb_ioaddr
ADD DX, DSP_RESET
MOV AL, 1
OUT DX, AL
/*
IN AL, DX // ritardo (3<>s ?)
IN AL, DX
IN AL, DX
IN AL, DX
*/
// Delay
MOV CX, 16
} lp: asm {
IN AL, 0x80
LOOP lp
MOV AL, 0
OUT DX, AL
}
for (register int i=0x20; i && sb_readDSPsafe() != 0xaa; i--);
return i;
}
char sb_check() {
sb_resetDSP();
sb_writeDSPsafe(0xe0);
sb_writeDSPsafe(0xc6);
return (sb_readDSPsafe() == 0x39);
}
char sb_autodetect() {
sb_ioaddr = 0x220;
while (sb_ioaddr <= 0x260) {
if (sb_check()) return 1;
sb_ioaddr += 0x10;
}
sb_ioaddr = 0x210;
return sb_check();
}
void setDMA() {
asm {
XOR BH, BH
MOV BL, sb_DMA8ch
MOV CH, BL
SHL BX, 1
MOV AL, 4
ADD AL, CH
OUT 0x0a, AL
XOR AL, AL
OUT 0x0c, AL
MOV AL, 0x58 // + AUTOINIT = 0x10
OR AL, CH
OUT 0x0b, AL
// Calc page-offset
MOV AX, WORD PTR DMAbuffer
MOV DX, WORD PTR DMAbuffer[2]
MOV CL, DH
SHR CL, 4
SHL DX, 4
ADD AX, DX
ADC CL, 0
// CL:AX = page:offset
MOV DX, WORD PTR DMAaddr[BX]
// LEA SI, DMAaddr
// MOV DX, [SI+BX]
OUT DX, AL
MOV AL, AH
OUT DX, AL
INC DL // !!! scorciatoia !!!
//MOV AX, ((DMA_BUFFER_SIZE*2)-1) // !!! ocio: 1 buffer diviso 2
MOV AX, DMAbuffer_size
SHL AX, 1
DEC AX
OUT DX, AL
MOV AL, AH
OUT DX, AL
MOV DX, WORD PTR DMApage[BX]
// LEA SI, DMApage
// MOV DX, [SI+BX]
MOV AL, CL
OUT DX, AL
MOV AL, CH
OUT 0x0a, AL
}}
void sb_enableDMA() {
switch (sb_type) {
case SBT_SB16:
sb_writeDSPsafe(0xc6);
sb_writeDSPsafe(0x20);
break;
default:
sb_writeDSPsafe(0x14); // it's DMA 8 bit !
break;
}
sb_writeDSPsafe((DMAbuffer_size-1) & 0xff);
sb_writeDSPsafe((DMAbuffer_size-1) >> 8);
}
void sb_settimeconst(byte tc) {
sb_writeDSPsafe(0x40);
sb_writeDSPsafe(tc);
}
void sb_setsamplerate(unsigned sr) {
sb_writeDSPsafe(0x42); // = PLAY; 0x41 = REC
sb_writeDSPsafe(sr >> 8); // high
sb_writeDSPsafe(sr & 0xff); // low
}
char sb_init(byte dma8, byte dma16, byte irq) {
if (sb_autodetect()) {
// SB identify
sb_resetDSP();
sb_writeDSPsafe(0xe1);
sb_type = sb_readDSPsafe();
sb_readDSPsafe(); // !!! non serve
if (sb_type<SBT_SBPRO) DMAbuffer_size=1024;
else DMAbuffer_size=2048;
if ((DMAbuffer = new char[DMAbuffer_size<<1]) == NULL) return 0;
// Adjust DMAbuffer ptr
asm {
MOV AX, WORD PTR DMAbuffer
MOV DX, WORD PTR DMAbuffer[2]
MOV BX, AX
SHR BX, 4
ADD DX, BX
AND AX, 0x000f
MOV WORD PTR DMAbuffer, AX
MOV WORD PTR DMAbuffer[2], DX
}
DMAbufhalf = DMAbuffer+DMAbuffer_size;
Old8259Mask=inp(0x21);
outp(0x21,Old8259Mask & ~(1<<sb_IRQnum));
disable();
oldsbvect=getvect(sb_IRQnum+0x08);
setvect(sb_IRQnum+0x08,soundISR);
enable();
sb_DMA8ch = dma8;
sb_DMA16ch = dma16;
sb_IRQnum = irq;
//if (sbmix_detect())
sbmix_init();
if (sb_type < SBT_SBPRO) sb_settimeconst(256-1000000L/11025); else
if (sb_type == SBT_SBPRO) sb_settimeconst(256-1000000L/22050); else
sb_setsamplerate(11025);
sb_writeDSPsafe(DSPC_SPEAKER_ON);
setDMA();
} else return 0;
return 1;
}
void sb_stopDMA() {
sb_writeDSPsafe(0xd0);
}
void sb_continueDMA() {
sb_writeDSPsafe(0xd4);
}
void sb_done() {
if (sb_ioaddr) {
sb_writeDSPsafe(DSPC_SPEAKER_OFF);
if(Old8259Mask & (1<<sb_IRQnum) != 0x00)
outp(0x21,(inp(0x21)|(1<<sb_IRQnum)));
disable();
setvect(sb_IRQnum+0x08,oldsbvect);
enable();
sb_resetDSP();
}
}