Skip to content

Instantly share code, notes, and snippets.

@root42

root42/SB_IRQ.C Secret

Created November 27, 2019 20:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save root42/4c22e225ddd9092e4498d8942e208450 to your computer and use it in GitHub Desktop.
Save root42/4c22e225ddd9092e4498d8942e208450 to your computer and use it in GitHub Desktop.
Fixing the IRQ handling of the SoundBlaster Auto-Init DMA Code
#include "dos.h"
#include "stdio.h"
#include "stdlib.h"
#include "mem.h"
int sb_detect();
void sb_init();
void sb_deinit();
void read_buffer(short buffer);
void single_cycle_playback();
short sb_base; /* default 220h */
char sb_irq; /* default 7 */
char sb_dma; /* default 1 */
void interrupt ( *old_irq )();
FILE *raw_file;
long file_size;
volatile char do_read;
volatile short r_buffer; /* Read buffer indicator */
volatile long to_be_read;
volatile int playing;
volatile long to_be_played;
unsigned char *dma_buffer;
short page;
short offset;
#define DMA_BLOCK_LENGTH 0x7FFF
#define SB_BLOCK_LENGTH 0x3FFF
#define SB_RESET 0x6
#define SB_READ_DATA 0xA
#define SB_READ_DATA_STATUS 0xE
#define SB_WRITE_DATA 0xC
#define SB_SET_BLOCK_SIZE 0x48
#define SB_SINGLE_CYCLE_PLAYBACK 0x14
#define SB_START_AUTOINIT_PLAYBACK 0x1C
#define SB_STOP_AUTOINIT_PLAYBACK 0xDA
#define SB_SET_PLAYBACK_FREQUENCY 0x40
#define SB_PAUSE_PLAYBACK 0xD0
#define SB_ENABLE_SPEAKER 0xD1
#define SB_DISABLE_SPEAKER 0xD3
#define MASK_REGISTER 0x0A
#define MODE_REGISTER 0x0B
#define MSB_LSB_FLIP_FLOP 0x0C
#define DMA_CHANNEL_0 0x87
#define DMA_CHANNEL_1 0x83
#define DMA_CHANNEL_3 0x82
int reset_dsp(short port)
{
outportb( port + SB_RESET, 1 );
delay(10);
outportb( port + SB_RESET, 0 );
delay(10);
if( ((inportb(port + SB_READ_DATA_STATUS) & 0x80) == 0x80)
&& (inportb(port + SB_READ_DATA) == 0x0AA )
) {
sb_base = port;
return 1;
}
return 0;
}
void write_dsp( unsigned char command )
{
while( (inportb( sb_base + SB_WRITE_DATA ) & 0x80) == 0x80 );
outportb( sb_base + SB_WRITE_DATA, command );
}
int sb_detect()
{
char temp;
char *BLASTER;
sb_base = 0;
/* possible values: 210, 220, 230, 240, 250, 260, 280 */
#if 0
for( temp = 1; temp < 9; temp++ ) {
if( temp != 7 ) {
if( reset_dsp( 0x200 + (temp << 4) ) ) {
break;
}
}
}
if( temp == 9 ) {
return 0;
}
#endif
BLASTER = getenv("BLASTER");
for( temp = 0; temp < strlen( BLASTER ); ++temp ) {
if((BLASTER[temp] | 32) == 'a') {
if( !reset_dsp( 0x200 + ( ( BLASTER[temp + 2] - '0' ) << 4) ) ) {
printf("Warning: DSP not detected.\n");
}
}
}
sb_dma = 0;
for( temp = 0; temp < strlen( BLASTER ); ++temp ) {
if((BLASTER[temp] | 32) == 'd') {
sb_dma = BLASTER[temp + 1] - '0';
}
}
for( temp = 0; temp < strlen( BLASTER ); ++temp ) {
if((BLASTER[temp] | 32) == 'i') {
sb_irq = BLASTER[temp + 1] - '0';
if( BLASTER[temp + 2] != ' ' ) {
sb_irq = sb_irq * 10 + BLASTER[ temp + 2 ] - '0';
}
}
}
return sb_base != 0;
}
void interrupt sb_irq_handler()
{
disable();
inportb(sb_base + SB_READ_DATA_STATUS);
outportb( 0x20, 0x20 );
if( sb_irq == 2 || sb_irq == 10 || sb_irq == 11 ) {
outportb( 0xA0, 0x20 );
}
if( playing ) {
to_be_played -= 16384;
if (to_be_played > 0) {
/* read_buffer (r_buffer); */
do_read = 1;
if (to_be_played <= 16384) {
r_buffer ^= 1;
single_cycle_playback ();
} else if (to_be_played <= 32768) {
write_dsp(SB_STOP_AUTOINIT_PLAYBACK);
}
} else {
playing = 0;
}
}
r_buffer ^= 1;
enable();
}
void init_irq()
{
/* save the old irq vector */
if( sb_irq == 2 || sb_irq == 10 || sb_irq == 11 ) {
if( sb_irq == 2) old_irq = getvect( 0x71 );
if( sb_irq == 10) old_irq = getvect( 0x72 );
if( sb_irq == 11) old_irq = getvect( 0x73 );
} else {
old_irq = getvect( sb_irq + 8 );
}
/* set our own irq vector */
if( sb_irq == 2 || sb_irq == 10 || sb_irq == 11 ) {
if( sb_irq == 2) setvect( 0x71, sb_irq_handler );
if( sb_irq == 10) setvect( 0x72, sb_irq_handler );
if( sb_irq == 11) setvect( 0x73, sb_irq_handler );
} else {
setvect( sb_irq + 8, sb_irq_handler );
}
/* enable irq with the mainboard's PIC */
if( sb_irq == 2 || sb_irq == 10 || sb_irq == 11 ) {
if( sb_irq == 2) outportb( 0xA1, inportb( 0xA1 ) & 253 );
if( sb_irq == 10) outportb( 0xA1, inportb( 0xA1 ) & 251 );
if( sb_irq == 11) outportb( 0xA1, inportb( 0xA1 ) & 247 );
outportb( 0x21, inportb( 0x21 ) & 251 );
} else {
outportb( 0x21, inportb( 0x21 ) & !(1 << sb_irq) );
}
}
void deinit_irq()
{
/* restore the old irq vector */
if( sb_irq == 2 || sb_irq == 10 || sb_irq == 11 ) {
if( sb_irq == 2) setvect( 0x71, old_irq );
if( sb_irq == 10) setvect( 0x72, old_irq );
if( sb_irq == 11) setvect( 0x73, old_irq );
} else {
setvect( sb_irq + 8, old_irq );
}
/* enable irq with the mainboard's PIC */
if( sb_irq == 2 || sb_irq == 10 || sb_irq == 11 ) {
if( sb_irq == 2) outportb( 0xA1, inportb( 0xA1 ) | 2 );
if( sb_irq == 10) outportb( 0xA1, inportb( 0xA1 ) | 4 );
if( sb_irq == 11) outportb( 0xA1, inportb( 0xA1 ) | 8 );
outportb( 0x21, inportb( 0x21 ) | 4 );
} else {
outportb( 0x21, inportb( 0x21 ) & (1 << sb_irq) );
}
}
void assign_dma_buffer()
{
unsigned char * temp_buf;
long linear_address;
short page1, page2;
temp_buf = (unsigned char *) malloc(32768);
linear_address = FP_SEG(temp_buf);
linear_address = (linear_address << 4)+FP_OFF(temp_buf);
page1 = linear_address >> 16;
page2 = (linear_address + 32767) >> 16;
if( page1 != page2 ) {
dma_buffer = (char *)malloc(32768);
free( temp_buf );
} else {
dma_buffer = temp_buf;
}
linear_address = FP_SEG(dma_buffer);
linear_address = (linear_address << 4)+FP_OFF(dma_buffer);
page = linear_address >> 16;
offset = linear_address & 0xFFFF;
}
void read_buffer(short buffer)
{
const int buf_off = buffer << 14;
/*If the remaining part of the file is smaller than 16K, */
/*load it and fill out with silence */
if (to_be_read <= 0) {
return;
}
if (to_be_read < 16384) {
memset (dma_buffer + buf_off, 128, 16384);
fread (dma_buffer + buf_off, 1, to_be_read, raw_file);
to_be_read = 0;
} else {
fread (dma_buffer + buf_off, 1, 16384, raw_file);
to_be_read -= 16384;
}
}
void sb_init()
{
init_irq();
assign_dma_buffer();
write_dsp( SB_ENABLE_SPEAKER );
}
void sb_deinit()
{
write_dsp( SB_DISABLE_SPEAKER );
free( dma_buffer );
deinit_irq();
}
void auto_init_playback ()
{
outportb(MASK_REGISTER, 4 | sb_dma); /*Mask DMA channel */
outportb(MSB_LSB_FLIP_FLOP, 0); /*Clear byte pointer */
outportb(MODE_REGISTER, 0x58 | sb_dma); /*Set mode */
outportb(sb_dma << 1, offset & 0xFF); /*Write the offset to the DMA controller */
outportb(sb_dma << 1, offset >> 8); /*Write the offset to the DMA controller */
/*
The mode consists of the following:
0x58+x = binary 01 01 10 xx
| | | |
| | | +- DMA channel
| | +---- Read operation (the DSP reads from memory)
| +------- Auto init mode
+---------- Block mode
*/
/* Write the page to the DMA controller */
switch(sb_dma) {
case 0 : outportb (DMA_CHANNEL_0, page);
break;
case 1 : outportb (DMA_CHANNEL_1, page);
break;
case 3 : outportb (DMA_CHANNEL_3, page);
break;
}
outportb((sb_dma << 1) + 1, DMA_BLOCK_LENGTH & 0xFF);
outportb((sb_dma << 1) + 1, DMA_BLOCK_LENGTH >> 8);
outportb(MASK_REGISTER, sb_dma);
write_dsp(SB_SET_BLOCK_SIZE);
write_dsp(SB_BLOCK_LENGTH & 0xFF);
write_dsp(SB_BLOCK_LENGTH >> 8);
write_dsp(SB_START_AUTOINIT_PLAYBACK);
}
void single_cycle_playback()
{
playing = 1;
/* program the DMA controller */
outportb( MASK_REGISTER, 4 | sb_dma );
outportb( MSB_LSB_FLIP_FLOP, 0 );
outportb( MODE_REGISTER, 0x48 | sb_dma );
outportb( sb_dma << 1, offset & 0xFF );
outportb( sb_dma << 1, offset >> 8 );
switch( sb_dma ) {
case 0:
outportb( DMA_CHANNEL_0, page );
break;
case 1:
outportb( DMA_CHANNEL_1, page );
break;
case 3:
outportb( DMA_CHANNEL_3, page );
break;
}
outportb((sb_dma << 1) + 1, to_be_played & 0xFF );
outportb((sb_dma << 1) + 1, to_be_played >> 8 );
outportb(MASK_REGISTER, sb_dma);
write_dsp(SB_SINGLE_CYCLE_PLAYBACK);
write_dsp(to_be_played & 0xFF);
write_dsp(to_be_played >> 8);
to_be_played = 0;
}
void sb_single_play( const char *file_name )
{
memset(dma_buffer, 0, 32768);
raw_file = fopen(file_name, "rb");
fseek(raw_file, 0L, SEEK_END);
file_size = ftell( raw_file );
fseek(raw_file, 0L, SEEK_SET);
fread(dma_buffer, 1, file_size, raw_file);
write_dsp(SB_SET_PLAYBACK_FREQUENCY);
write_dsp(256-1000000/11000);
to_be_played = file_size;
single_cycle_playback();
}
void sb_auto_play(const char *file_name)
{
long before;
/*Don't play if there's no buffer */
if (!dma_buffer) {
return;
}
/*Start playback in buffer 0 and clear the buffer */
r_buffer = 0;
memset (dma_buffer, 0, 32768);
/*Open the file for output */
raw_file = fopen(file_name, "rb");
if (!raw_file) {
return;
}
/* determine file size and set size of read and play buffers */
fseek(raw_file, 0L, SEEK_END);
file_size = ftell( raw_file );
fseek(raw_file, 0L, SEEK_SET);
to_be_read = to_be_played = file_size;
printf("to be read: %lu\n", to_be_read);
/*Set playback frequency */
write_dsp (SB_SET_PLAYBACK_FREQUENCY);
write_dsp (256 - 1000000 / 11000);
/*DSP-command D1h - Enable speaker */
write_dsp (SB_ENABLE_SPEAKER);
/*Read first bit of data */
read_buffer (0);
read_buffer (1);
if (to_be_read > 0) {
printf("auto init play...\n");
auto_init_playback();
} else {
printf("single cycle play...\n");
single_cycle_playback();
}
playing = 1;
do_read = 0;
}
void sb_stop ()
{
write_dsp( SB_PAUSE_PLAYBACK );
playing = 0;
fclose( raw_file );
}
int main(int argc, char** argv)
{
int sb_detected = 0;
short percent;
if( argc < 2 ) {
printf( "Usage: %s FILE.RAW\n", argv[0] );
return 1;
}
sb_detected = sb_detect();
if( !sb_detected ) {
printf("SoundBlaster not found!\n");
return 1;
} else {
printf("SoundBlaster found at A%x I%u D%u.\n", sb_base, sb_irq, sb_dma );
}
sb_init();
sb_auto_play( argv[1] );
while( playing && !kbhit() ) {
if( do_read == 1 ) {
read_buffer( r_buffer ^ 1 );
do_read = 0;
} else {
delay( 10 );
percent = (100L * (file_size - to_be_read) + (file_size / 2)) / file_size;
printf("\b\b\b\b%3u%%", percent);
}
}
sb_stop();
sb_deinit();
printf("\nExit\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment