PATCH (resubmitting): DMA and SOUNDBLASTER emulation

Christian Costa titan.costa at wanadoo.fr
Wed May 15 16:38:42 CDT 2002


This patch intends to provide the necessary infrastrucure to
enable sound in DOS games.
The board is configured at address=0x220, irq=5 and dma=1.
Irq and dma are just #define but can be set from a config file.
The dma emulation is almost complete and the soundblaster
emulation, altough it still needs a lot of work, works fairly
with digital 8-bit sound. At least for the game Dreamweb I used
to debug my code...
This version is much cleaner that the previous one and fix the
unresolved external (caused by a missing file in the diff).

If something is wrong with this code, feel free to ask.

Changelog:

Add DMA and SOUNBLASTER emulation

           Added files: dlls/winedos/dma.h
                        dlls/winedos/dma.c
                        dlls/winedos/soundblaster.h
                        dlls/winedos/soundblaster.c
        Modified files: dlls/winedos/ioports.c
                        dlls/winedos/Makefile.in

Christian Costa   titan.costa at wanadoo.fr
-------------- next part --------------
/*
 * DMA Emulation
 *
 * Copyright 2002 Christian Costa
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __WINE_DMA_H
#define __WINE_DMA_H

#include "winbase.h"

int DMA_Transfer(int channel,int reqlength,void* buffer);
void DMA_ioport_out( WORD port, BYTE val );
BYTE DMA_ioport_in( WORD port );

#endif /* __WINE_DMA_H */
-------------- next part --------------
/*
 * DMA Emulation
 *
 * Copyright 2002 Christian Costa
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include "windef.h"
#include "dosexe.h"
#include "dma.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(dma);

/* Internal registers of the 2 DMA chips wich control 8 DMA channels */
static DWORD DMA_BaseAddress[8];
static WORD  DMA_ByteCount[8];
static DWORD DMA_CurrentBaseAddress[8];
static WORD  DMA_CurrentByteCount[8];
static BYTE  DMA_Command[8];
static BYTE  DMA_Mask[2]={0x0F,0x0F};
static BYTE  DMA_Status[2]={0x00,0x00};
static BOOL  DMA_Toggle[2]={FALSE,FALSE};

/*
 * DMA_Transfer : Try to perform a transfer of reqlen elements (8 or 16 bits)
 * on the specified channel and return the elements transferred
 */
int DMA_Transfer(int channel,int reqlen,void* buffer)
{
    int i,size,ret=0;
    int opmode,increment,autoinit,trmode,dmachip;
    int regmode = DMA_Command[channel];
    char *p,*dmabuf;

    dmabuf = buffer;
    dmachip = (channel<4) ? 0 : 1;

    TRACE("DMA_Command = %x reqlen=%d\n",regmode,reqlen);

    /* Exit if channel is masked */
    if (DMA_Mask[dmachip]&(1<<(channel&3)))
        return 0;

    opmode = (regmode & 0xC0) >> 6;
    increment = !(regmode & 0x20);
    autoinit = regmode & 0x10;
    trmode = (regmode & 0x0C) >> 2;

    /* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */
    size = (channel<4) ? 1 : 2;

    /* Process operating mode */
    switch(opmode)
    {
    case 0:
        /* Request mode */
        FIXME("Request Mode - Not Implemented\n");
        return 0;
    case 1:
        /* Single Mode */
        break;
    case 2:
        /* Request mode */
        FIXME("Block Mode - Not Implemented\n");
        return 0;
    case 3:
        /* Cascade Mode */
        ERR("Cascade Mode should not be used by regular apps\n");
        return 0;
    }

    /* Perform one the 4 transfer modes */
    if (trmode == 4) {
        /* Illegal */
        ERR("DMA Transfer Type Illegal\n");
        return 0;
    }

    ret = min(DMA_CurrentByteCount[channel],reqlen);

    /* Update DMA registers */
    DMA_CurrentByteCount[channel]-=ret;
    if (increment)
        DMA_CurrentBaseAddress[channel] += ret * size;
    else
        DMA_CurrentBaseAddress[channel] -= ret * size;

    switch(trmode)
    {
    case 0:
        /* Verification (no real transfer)*/
        TRACE("Verification DMA operation\n");
        break;
    case 1:
        /* Write */
        TRACE("Perform Write transfer of %d bytes at %lx with count %x\n",ret,
            DMA_CurrentBaseAddress[channel],DMA_CurrentByteCount[channel]);
        if (increment)
            memcpy((void*)DMA_CurrentBaseAddress[channel],dmabuf,ret*size);
        else
            for(i=0,p=(char*)DMA_CurrentBaseAddress[channel];i<ret*size;i++)
                /* FIXME: possible endianness issue for 16 bits DMA */
                *(p-i) = dmabuf[i];
        break;
    case 2:
        /* Read */
        TRACE("Perform Read transfer of %d bytes at %lx with count %x\n",ret,
            DMA_CurrentBaseAddress[channel],DMA_CurrentByteCount[channel]);
        if (increment)
            memcpy(dmabuf,(void*)DMA_CurrentBaseAddress[channel],ret*size);
        else
            for(i=0,p=(char*)DMA_CurrentBaseAddress[channel];i<ret*size;i++)
                /* FIXME: possible endianness issue for 16 bits DMA */
                dmabuf[i] = *(p-i);
        break;
    }

    /* Check for end of transfer */
    if (DMA_CurrentByteCount[channel]==0) {
        TRACE("DMA buffer empty\n");

        /* Update status register of the DMA chip corresponding to the channel */
        DMA_Status[dmachip] |= 1 << (channel & 0x3); /* Mark transfer as finished */
        DMA_Status[dmachip] &= ~(1 << ((channel & 0x3) + 4)); /* Reset soft request if any */

        if (autoinit) {
            /* Reload Current* register to their initial values */
            DMA_CurrentBaseAddress[channel] = DMA_BaseAddress[channel];
            DMA_CurrentByteCount[channel] = DMA_ByteCount[channel];
        }
    }

    return ret;
}


void DMA_ioport_out( WORD port, BYTE val )
{
    int channel,dmachip;

    switch(port)
    {
    case 0x00:
    case 0x02:
    case 0x04:
    case 0x06:
    case 0xC0:
    case 0xC4:
    case 0xC8:
    case 0xCC:
        /* Base Address*/
        channel = (port&0xC0)?((port-0xC0)>>2):(port>>1);
        dmachip = (channel<4) ? 0 : 1;
        if (!DMA_Toggle[dmachip])
            DMA_BaseAddress[channel]=(DMA_BaseAddress[channel] & ~0xFF)|(val & 0xFF);
        else {
            DMA_BaseAddress[channel]=(DMA_BaseAddress[channel] & (~(0xFF << 8)))|((val & 0xFF) << 8);
            DMA_CurrentBaseAddress[channel] = DMA_BaseAddress[channel];
            TRACE("Write Base Address = %lx\n",DMA_BaseAddress[channel]);
        }
        DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
        break;

    case 0x01:
    case 0x03:
    case 0x05:
    case 0x07:
    case 0xC2:
    case 0xC6:
    case 0xCA:
    case 0xCE:
        /* Count*/
        channel = ((port-1)&0xC0)?(((port-1)-0xC0)>>2):(port>>1);
        dmachip = (channel<4) ? 0 : 1;
        if (!DMA_Toggle[dmachip])
            DMA_ByteCount[channel]=(DMA_ByteCount[channel] & ~0xFF)|((val+1) & 0xFF);
        else {
            DMA_ByteCount[channel]=(DMA_ByteCount[channel] & (~(0xFF << 8)))|(((val+1) & 0xFF) << 8);
            DMA_CurrentByteCount[channel] = DMA_ByteCount[channel];
            TRACE("Write Count = %x.\n",DMA_ByteCount[channel]);
        }
        DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
        break;

    /* Low Page Base Address */
    case 0x87: DMA_BaseAddress[0]=(DMA_BaseAddress[0] & (~0xFF << 16))|((val & 0xFF) << 16); break;
    case 0x83: DMA_BaseAddress[1]=(DMA_BaseAddress[1] & (~0xFF << 16))|((val & 0xFF) << 16); break;
    case 0x81: DMA_BaseAddress[2]=(DMA_BaseAddress[2] & (~0xFF << 16))|((val & 0xFF) << 16); break;
    case 0x82: DMA_BaseAddress[3]=(DMA_BaseAddress[3] & (~0xFF << 16))|((val & 0xFF) << 16); break;
    case 0x8B: DMA_BaseAddress[5]=(DMA_BaseAddress[5] & (~0xFF << 16))|((val & 0xFF) << 16); break;
    case 0x89: DMA_BaseAddress[6]=(DMA_BaseAddress[6] & (~0xFF << 16))|((val & 0xFF) << 16); break;
    case 0x8A: DMA_BaseAddress[7]=(DMA_BaseAddress[7] & (~0xFF << 16))|((val & 0xFF) << 16); break;

    /* Low Page Base Address (only 4 lower bits are significant) */
    case 0x487: DMA_BaseAddress[0]=(DMA_BaseAddress[0] & (~0xFF << 24))|((val & 0x0F) << 24); break;
    case 0x483: DMA_BaseAddress[1]=(DMA_BaseAddress[1] & (~0xFF << 24))|((val & 0x0F) << 24); break;
    case 0x481: DMA_BaseAddress[2]=(DMA_BaseAddress[2] & (~0xFF << 24))|((val & 0x0F) << 24); break;
    case 0x482: DMA_BaseAddress[3]=(DMA_BaseAddress[3] & (~0xFF << 24))|((val & 0x0F) << 24); break;
    case 0x48B: DMA_BaseAddress[5]=(DMA_BaseAddress[5] & (~0xFF << 24))|((val & 0x0F) << 24); break;
    case 0x489: DMA_BaseAddress[6]=(DMA_BaseAddress[6] & (~0xFF << 24))|((val & 0x0F) << 24); break;
    case 0x48A: DMA_BaseAddress[7]=(DMA_BaseAddress[7] & (~0xFF << 24))|((val & 0x0F) << 24); break;

    case 0x08:
    case 0xD0:
        /* Command */
        FIXME("Write Command (%x) - Not Implemented\n",val);
        break;

    case 0x0B:
    case 0xD6:
        /* Mode */
        TRACE("Write Mode (%x)\n",val);
        DMA_Command[((port==0xD6)?4:0)+(val&0x3)]=val;
        switch(val>>6)
        {
        case 0:
            /* Request mode */
            FIXME("Request Mode - Not Implemented\n");
            break;
        case 1:
            /* Single Mode */
            break;
        case 2:
            /* Block mode */
            FIXME("Block Mode - Not Implemented\n");
            break;
        case 3:
            /* Cascade Mode */
            ERR("Cascade Mode should not be used by regular apps\n");
            break;
        }
        break;

    case 0x0A:
    case 0xD4:
        /* Write Single Mask Bit */
        TRACE("Write Single Mask Bit (%x)\n",val);
        dmachip = (port==0x0A) ? 0 : 1;
        if (val&4)
            DMA_Mask[dmachip] |= 1<<(val&3);
        else
            DMA_Mask[dmachip] &= ~(1<<(val&3));
        break;

    case 0x0F:
    case 0xDE:
        /* Write All Mask Bits (only 4 lower bits are significant */
        FIXME("Write All Mask Bits (%x)\n",val);
        dmachip = (port==0x0F) ? 0 : 1;
        DMA_Mask[dmachip] = val & 0x0F;
        break;

    case 0x09:
    case 0xD2:
        /* Software DRQx Request */
        FIXME("Software DRQx Request (%x) - Not Implemented\n",val);
        break;

    case 0x0C:
    case 0xD8:
        /* Reset DMA Pointer Flip-Flop */
        TRACE("Reset Flip-Flop\n");
        DMA_Toggle[port==0xD8]=FALSE;
        break;

    case 0x0D:
    case 0xDA:
        /* Master Reset */
        TRACE("Master Reset\n");
        dmachip = (port==0x0D) ? 0 : 1;
        /* Reset DMA Pointer Flip-Flop */
        DMA_Toggle[dmachip]=FALSE;
        /* Mask all channels */
        DMA_Mask[dmachip] = 0x0F;
        break;

    case 0x0E:
    case 0xDC:
        /* Reset Mask Register */
        FIXME("Reset Mask Register\n");
        dmachip = (port==0x0E) ? 0 : 1;
        /* Unmask all channels */
        DMA_Mask[dmachip] = 0x00;
        break;
    }
}

BYTE DMA_ioport_in( WORD port )
{
    int channel,dmachip;
    BYTE res = 0;

    switch(port)
    {
    case 0x00:
    case 0x02:
    case 0x04:
    case 0x06:
    case 0xC0:
    case 0xC4:
    case 0xC8:
    case 0xCC:
        /* Base Address*/
        channel = (port&0xC0)?((port-0xC0)>>2):(port>>1);
        dmachip = (channel<4) ? 0 : 1;
        if (!DMA_Toggle[dmachip])
            res = DMA_CurrentBaseAddress[channel] & 0xFF;
        else {
            res = (DMA_CurrentBaseAddress[channel] & (0xFF << 8))>>8;
            TRACE("Read Current Base Address = %lx\n",DMA_CurrentBaseAddress[channel]);
        }
        DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
        break;

    case 0x01:
    case 0x03:
    case 0x05:
    case 0x07:
    case 0xC2:
    case 0xC6:
    case 0xCA:
    case 0xCE:
        /* Count*/
        channel = ((port-1)&0xC0)?(((port-1)-0xC0)>>2):(port>>1);
        dmachip = (channel<4) ? 0 : 1;
        if (!DMA_Toggle[dmachip])
            res = DMA_CurrentByteCount[channel] & 0xFF;
        else {
            res = (DMA_CurrentByteCount[channel] & (0xFF << 8))>>8;
            TRACE("Read Current Count = %x.\n",DMA_CurrentByteCount[channel]);
        }
        DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
        break;

    /* Low Page Base Address */
    case 0x87: res = (DMA_BaseAddress[0]&(0xFF<<16))>>16; break;
    case 0x83: res = (DMA_BaseAddress[1]&(0xFF<<16))>>16; break;
    case 0x81: res = (DMA_BaseAddress[2]&(0xFF<<16))>>16; break;
    case 0x82: res = (DMA_BaseAddress[3]&(0xFF<<16))>>16; break;
    case 0x8B: res = (DMA_BaseAddress[5]&(0xFF<<16))>>16; break;
    case 0x89: res = (DMA_BaseAddress[6]&(0xFF<<16))>>16; break;
    case 0x8A: res = (DMA_BaseAddress[7]&(0xFF<<16))>>16; break;

    /* High Page Base Address */
    case 0x487: res = (DMA_BaseAddress[0]&(0xFF<<24))>>24; break;
    case 0x483: res = (DMA_BaseAddress[1]&(0xFF<<24))>>24; break;
    case 0x481: res = (DMA_BaseAddress[2]&(0xFF<<24))>>24; break;
    case 0x482: res = (DMA_BaseAddress[3]&(0xFF<<24))>>24; break;
    case 0x48B: res = (DMA_BaseAddress[5]&(0xFF<<24))>>24; break;
    case 0x489: res = (DMA_BaseAddress[6]&(0xFF<<24))>>24; break;
    case 0x48A: res = (DMA_BaseAddress[7]&(0xFF<<24))>>24; break;

    case 0x08:
    case 0xD0:
        /* Status */
        TRACE("Status Register Read\n");
        res = DMA_Status[(port==0x08)?0:1];

    case 0x0D:
    case 0xDA:
        /* Temporary */
        FIXME("Temporary Register Read- Not Implemented\n");
        break;
    }
    return res;
}
-------------- next part --------------
/*
 * Soundblaster Emulation
 *
 * Copyright 2002 Christian Costa
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __WINE_SOUNDBLASTER_H
#define __WINE_SOUNDBLASTER_H

#include "winbase.h"

void SB_ioport_out( WORD port, BYTE val );
BYTE SB_ioport_in( WORD port );

#endif /* __WINE_SOUNDBLASTER_H */
-------------- next part --------------
/*
 * Soundblaster Emulation
 *
 * Copyright 2002 Christian Costa
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include "windef.h"
#include "dosexe.h"
#include "soundblaster.h"
#include "wine/debug.h"
#include "dsound.h"
#include "dma.h"

WINE_DEFAULT_DEBUG_CHANNEL(sblaster);

/* Board Configuration */
/* FIXME: Should be in a config file */ 
#define SB_IRQ 5
#define SB_IRQ_PRI 11
#define SB_DMA 1

/* Soundblaster state */
static int SampleMode;         /* Mono / Stereo */
static int SampleRate;
static int SamplesCount;
static BYTE DSP_Command[256];  /* Store param numbers in bytes for each command */
static BYTE DSP_InBuffer[10];  /* Store DSP command bytes parameters from host */
static int InSize;             /* Nb of bytes in InBuffer */ 
static BYTE DSP_OutBuffer[10]; /* Store DSP information bytes to host */
static int OutSize;            /* Nb of bytes in InBuffer */ 
static int command;            /* Current command */
static int end_sound_loop = 0;
static int dma_enable = 0;

/* The maximum size of a dma transfer can be 65536 */
#define DMATRFSIZE 1024 

/* DMA can perform 8 or 16-bit transfer */
static BYTE dma_buffer[DMATRFSIZE*2];

/* Direct Sound buffer config */
#define DSBUFLEN 4096 /* FIXME: Only this value seems to work */

/* Direct Sound playback stuff */
static HMODULE hmodule;
typedef HRESULT (WINAPI* fnDirectSoundCreate) (LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
fnDirectSoundCreate lpDirectSoundCreate;
static LPDIRECTSOUND lpdsound;
static LPDIRECTSOUNDBUFFER lpdsbuf;
static DSBUFFERDESC buf_desc;
static WAVEFORMATEX wav_fmt;
static HANDLE SB_Thread;
static UINT buf_off;
extern HWND vga_hwnd;

/* SB_Poll performs DMA transfers and fills the Direct Sound Buffer */
static DWORD CALLBACK SB_Poll( void *dummy )
{
    HRESULT result;
    LPBYTE lpbuf1 = NULL;
    LPBYTE lpbuf2 = NULL;
    DWORD dwsize1 = 0;
    DWORD dwsize2 = 0;
    DWORD dwbyteswritten1 = 0;
    DWORD dwbyteswritten2 = 0;
    int size;

    /* FIXME: this loop must be improved */ 
    while(!end_sound_loop)
    {
        Sleep(10);

        if (dma_enable) {
            size = DMA_Transfer(SB_DMA,min(DMATRFSIZE,SamplesCount),dma_buffer);
        } else
            continue;

        result = IDirectSoundBuffer_Lock(lpdsbuf,buf_off,size,&lpbuf1,&dwsize1,&lpbuf2,&dwsize2,0);
        if (result != DS_OK) {
	  ERR("Unable to lock sound buffer !\n");
          continue;
        }

        dwbyteswritten1 = min(size,dwsize1);
        memcpy(lpbuf1,dma_buffer,dwbyteswritten1);
        if (size>dwsize1) {
            dwbyteswritten2 = min(size - dwbyteswritten1,dwsize2);
            memcpy(lpbuf2,dma_buffer+dwbyteswritten1,dwbyteswritten2);
        }
        buf_off = (buf_off + dwbyteswritten1 + dwbyteswritten2) % DSBUFLEN;

        result = IDirectSoundBuffer_Unlock(lpdsbuf,lpbuf1,dwbyteswritten1,lpbuf2,dwbyteswritten2);
        if (result!=DS_OK)
	    ERR("Unable to unlock sound buffer !\n");

        SamplesCount -= size;
        if (!SamplesCount) {
            DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
            dma_enable = 0;
        }
    }
    return 0;
}

BOOL SB_Init()
{
    HRESULT result;

    if (!lpdsound) {
        hmodule = LoadLibraryA("dsound.dll");
        if (!hmodule) {
            ERR("Can't load dsound.dll !\n");
            return 0;
        }
        lpDirectSoundCreate = (fnDirectSoundCreate)GetProcAddress(hmodule,"DirectSoundCreate");
        if (!lpDirectSoundCreate) {
            /* CloseHandle(hmodule); */
            ERR("Can't find DirectSoundCreate function !\n");
            return 0;
        }
        result = (*lpDirectSoundCreate)(NULL,&lpdsound,NULL);
        if (result != DS_OK) {
            ERR("Unable to initialize Sound Subsystem err = %lx !\n",result);
            return 0;
        }

        /* FIXME: To uncomment when :
           - SetCooperative level is correctly implemented
           - an always valid and non changing handle to a windows  (vga_hwnd) is available
             (this surely needs some work in vga.c)
        result = IDirectSound_SetCooperativeLevel(lpdsound,vga_hwnd,DSSCL_EXCLUSIVE|DSSCL_PRIORITY);
        if (result != DS_OK) {
            ERR("Can't set cooperative level !\n");
            return 0;
        }
        */

        /* Default format */
        wav_fmt.wFormatTag = WAVE_FORMAT_PCM;
        wav_fmt.nChannels = 1;
        wav_fmt.nSamplesPerSec = 22050;
        wav_fmt.nAvgBytesPerSec = 22050;
        wav_fmt.nBlockAlign = 1;
        wav_fmt.wBitsPerSample = 8;
        wav_fmt.cbSize = 0;

        memset(&buf_desc,0,sizeof(DSBUFFERDESC));
        buf_desc.dwSize = sizeof(DSBUFFERDESC);
        buf_desc.dwBufferBytes = DSBUFLEN;
        buf_desc.lpwfxFormat = &wav_fmt;
        result = IDirectSound_CreateSoundBuffer(lpdsound,&buf_desc,&lpdsbuf,NULL);
        if (result != DS_OK) {
            ERR("Can't create sound buffer !\n");
            return 0;
        }

        result = IDirectSoundBuffer_Play(lpdsbuf,0, 0, DSBPLAY_LOOPING);
        if (result != DS_OK) {
            ERR("Can't start playing !\n");
            return 0;
        }

        buf_off = 0;
        end_sound_loop = 0;
        SB_Thread = CreateThread(NULL, 0, SB_Poll, NULL, 0, NULL);
        TRACE("thread\n");
        if (!SB_Thread) {
            ERR("Can't create thread !\n");
            return 0;
        }
    }
    return 1;
}

void SB_Reset()
{
    int i;

    for(i=0;i<256;i++)
        DSP_Command[i]=0;

    /* Set Time Constant */
    DSP_Command[0x40]=1;
    /* Generate IRQ */
    DSP_Command[0xF2]=0;
    /* DMA DAC 8-bits */
    DSP_Command[0x14]=2;
    /* Generic DAC/ADC DMA (16-bit, 8-bit) */
    for(i=0xB0;i<=0xCF;i++)
        DSP_Command[i]=3;
    /* DSP Indentification */
    DSP_Command[0xE0]=1;

    /* Clear command and input buffer */
    command = -1;
    InSize = 0;

    /* Put a garbage value in the output buffer */
    OutSize = 1;
    if (SB_Init())
        /* All right, let's put the magic value for autodetection */ 
        DSP_OutBuffer[0] = 0xaa;
    else
        /* Something is wrong, put 0 to failed audetection */
        DSP_OutBuffer[0] = 0x00; 
}

/* Find a standard sampling rate for DirectSound */
int SB_StdSampleRate(int SampleRate)
{
  if (SampleRate>((44100+48000)/2)) return 48000;
  if (SampleRate>((32000+44100)/2)) return 44100;
  if (SampleRate>((24000+32000)/2)) return 32000;
  if (SampleRate>((22050+24000)/2)) return 24000;
  if (SampleRate>((16000+22050)/2)) return 22050;
  if (SampleRate>((12000+16000)/2)) return 16000;
  if (SampleRate>((11025+12000)/2)) return 12000;
  if (SampleRate>((8000+11025)/2))  return 11025;
  return 8000;
}

void SB_ioport_out( WORD port, BYTE val )
{
    switch(port)
    {
    /* DSP - Reset */
    case 0x226:
        TRACE("Resetting DSP.\n");
        SB_Reset();
        break;
    /* DSP - Write Data or Command */
    case 0x22c:
        TRACE("val=%x\n",val);
        if (command == -1) {
          /* Clear input buffer and set the current command */
          command = val;
          InSize = 0;
        }
        if (InSize!=DSP_Command[command])
	   /* Fill the input buffer the command parameters if any */
           DSP_InBuffer[InSize++]=val;
        else {
	    /* Process command */ 
            switch(command)
            {
            case 0x10: /* SB */
                FIXME("Direct DAC (8-bit) - Not Implemented\n");
                break;
            case 0x14: /* SB */
                SamplesCount = DSP_InBuffer[1]+(val<<8)+1;
                TRACE("DMA DAC (8-bit) for %x samples\n",SamplesCount);
                dma_enable = 1;
                break;
            case 0x20:
                FIXME("Direct ADC (8-bit) - Not Implemented\n");
                break;
            case 0x24: /* SB */
                FIXME("DMA ADC (8-bit) - Not Implemented\n");
                break;
            case 0x40: /* SB */
                SampleRate = 1000000/(256-val);
                TRACE("Set Time Constant (%d <-> %d Hz => %d Hz)\n",DSP_InBuffer[0],
                    SampleRate,SB_StdSampleRate(SampleRate));
                SampleRate = SB_StdSampleRate(SampleRate);
                wav_fmt.nSamplesPerSec = SampleRate;
                wav_fmt.nAvgBytesPerSec = SampleRate;
                IDirectSoundBuffer_SetFormat(lpdsbuf,&wav_fmt);
                break;
	    /* case 0xBX/0xCX -> See below */
            case 0xD0: /* SB */
                TRACE("Halt DMA operation (8-bit)\n");
                dma_enable = 0;
                break;
            case 0xD1: /* SB */
                FIXME("Enable Speaker - Not Implemented\n");
                break;
            case 0xD3: /* SB */
                FIXME("Disable Speaker - Not Implemented\n");
                break;
            case 0xD4: /* SB */
                FIXME("Continue DMA operation (8-bit) - Not Implemented\n");
                break;
            case 0xD8: /* SB */
                FIXME("Speaker Status - Not Implemented\n");
                break;
	    case 0xE0: /* SB 2.0 */
                TRACE("DSP Identification\n");
                DSP_OutBuffer[OutSize++] = ~val;
                break;
            case 0xE1: /* SB */
                FIXME("DSP Version - Not Implemented\n");
                break;
            case 0xF2: /* SB */ 
                TRACE("IRQ Request (8-bit)\n");
                DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
                break;
            default:
	      if (((command&0xF0)==0xB0)||((DSP_InBuffer[0]&0xF0)==0xC0)) {
		    /* SB16 */
                    FIXME("Generic DAC/ADC DMA (16-bit, 8-bit) - %d % d\n",command,DSP_InBuffer[1]);
                    if (command&0x02)
		        FIXME("Generic DAC/ADC fifo mode not supported\n");
                    if (command&0x04)
		        FIXME("Generic DAC/ADC autoinit dma mode not supported\n");
                    if (command&0x08)
		        FIXME("Generic DAC/ADC adc mode not supported\n");
                    switch(command>>4) {
                    case 0xB:
		        FIXME("Generic DAC/ADC 8-bit not supported\n");
                        SampleMode = 0;
                        break;                    
                    case 0xC:
		        FIXME("Generic DAC/ADC 16-bit not supported\n");
                        SampleMode = 1;
                        break;
                    default:
		        ERR("Generic DAC/ADC resolution unknown\n");
                        break;
                    }
                    if (DSP_InBuffer[1]&0x010)
		        FIXME("Generic DAC/ADC signed sample mode not supported\n");
                    if (DSP_InBuffer[1]&0x020)
		        FIXME("Generic DAC/ADC stereo mode not supported\n");
                    SamplesCount = DSP_InBuffer[2]+(val<<8)+1;
                    TRACE("Generic DMA for %x samples\n",SamplesCount);
                    dma_enable = 1;
	        }
                else
                    FIXME("DSP command %x not supported\n",val);
            }
            /* Empty the input buffer and end the command */
            InSize = 0;
            command = -1;
        }
    }
}

BYTE SB_ioport_in( WORD port )
{
    BYTE res = 0;

    switch(port)
    {
    /* DSP Read Data */
    case 0x22a:
        /* Value in the read buffer */
      if (OutSize)
          res = DSP_OutBuffer[--OutSize];
      else
	  /* return the last byte */ 
	  res = DSP_OutBuffer[0];
      break;
    /* DSP - Write Buffer Status */
    case 0x22c:
        /* DSP always ready for writing */
        res = 0x00;
        break;
    /* DSP - Data Available Status */
    /* DSP - IRQ Acknowledge, 8-bit */
    case 0x22e:
        /* DSP data availability check */
        if (OutSize)
            res = 0x80;
        else
	    res = 0x00;
        break;
    }
    return res;
}
-------------- next part --------------
Index: ioports.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/ioports.c,v
retrieving revision 1.2
diff -u -r1.2 ioports.c
--- ioports.c	9 Mar 2002 23:44:32 -0000	1.2
+++ ioports.c	15 May 2002 21:06:02 -0000
@@ -24,7 +24,8 @@
 #include "windef.h"
 #include "dosexe.h"
 #include "vga.h"
-
+#include "soundblaster.h"
+#include "dma.h"
 
 /**********************************************************************
  *	    DOSVM_inport
@@ -36,10 +37,51 @@
     case 0x60:
         *res = DOSVM_Int09ReadScan(NULL);
         break;
+    case 0x22a:
+    case 0x22c:
+    case 0x22e:
+        *res = (DWORD)SB_ioport_in( port );
+	break;
     case 0x3ba:
     case 0x3da:
         *res = (DWORD)VGA_ioport_in( port );
         break;
+    case 0x00:
+    case 0x01:
+    case 0x02:
+    case 0x03:
+    case 0x04:
+    case 0x05:
+    case 0x06:
+    case 0x07:
+    case 0xC0:
+    case 0xC2:
+    case 0xC4:
+    case 0xC6:
+    case 0xC8:
+    case 0xCA:
+    case 0xCC:    
+    case 0xCE:
+    case 0x87:
+    case 0x83:
+    case 0x81:
+    case 0x82:
+    case 0x8B:
+    case 0x89:
+    case 0x8A:
+    case 0x487:
+    case 0x483:
+    case 0x481:
+    case 0x482:
+    case 0x48B:
+    case 0x489:
+    case 0x48A:
+    case 0x08:
+    case 0xD0:
+    case 0x0D:
+    case 0xDA:
+        *res = (DWORD)DMA_ioport_in( port );
+        break;
     default:
         return FALSE;  /* not handled */
     }
@@ -57,9 +99,61 @@
     case 0x20:
         DOSVM_PIC_ioport_out( port, (BYTE)value );
         break;
+    case 0x226:	
+    case 0x22c:
+        SB_ioport_out( port, (BYTE)value );
+        break;
     case 0x3c8:
     case 0x3c9:
         VGA_ioport_out( port, (BYTE)value );
+        break;
+    case 0x00:
+    case 0x01:
+    case 0x02:
+    case 0x03:
+    case 0x04:
+    case 0x05:
+    case 0x06:
+    case 0x07:
+    case 0xC0:
+    case 0xC2:
+    case 0xC4:
+    case 0xC6:
+    case 0xC8:
+    case 0xCA:
+    case 0xCC:    
+    case 0xCE:
+    case 0x87:
+    case 0x83:
+    case 0x81:
+    case 0x82:
+    case 0x8B:
+    case 0x89:
+    case 0x8A:
+    case 0x487:
+    case 0x483:
+    case 0x481:
+    case 0x482:
+    case 0x48B:
+    case 0x489:
+    case 0x48A:
+    case 0x08:
+    case 0xD0:
+    case 0x0B:
+    case 0xD6:
+    case 0x0A:
+    case 0xD4:
+    case 0x0F:
+    case 0xDE:
+    case 0x09:
+    case 0xD2:
+    case 0x0C:
+    case 0xD8:
+    case 0x0D:
+    case 0xDA:
+    case 0x0E:
+    case 0xDC:
+        DMA_ioport_out( port, (BYTE)value );
         break;
     default:
         return FALSE;  /* not handled */
Index: Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/winedos/Makefile.in,v
retrieving revision 1.10
diff -u -r1.10 Makefile.in
--- Makefile.in	11 May 2002 23:06:35 -0000	1.10
+++ Makefile.in	15 May 2002 21:06:02 -0000
@@ -24,6 +24,8 @@
 	ioports.c \
 	module.c \
 	vga.c \
+	soundblaster.c \
+	dma.c \
 	xms.c
 
 @MAKE_DLL_RULES@


More information about the wine-patches mailing list