From 53268a54d19cdcdd5bfdde02328af7a3b188bc58 Mon Sep 17 00:00:00 2001 From: Christian Costa Date: Thu, 16 May 2002 18:34:48 +0000 Subject: [PATCH] Added DMA and SoundBlaster emulation. --- dlls/winedos/Makefile.in | 4 +- dlls/winedos/dma.c | 381 +++++++++++++++++++++++++++++++++++ dlls/winedos/dosexe.h | 9 + dlls/winedos/ioports.c | 93 +++++++++ dlls/winedos/soundblaster.c | 383 ++++++++++++++++++++++++++++++++++++ 5 files changed, 869 insertions(+), 1 deletion(-) create mode 100644 dlls/winedos/dma.c create mode 100644 dlls/winedos/soundblaster.c diff --git a/dlls/winedos/Makefile.in b/dlls/winedos/Makefile.in index bfaa285451e..fa109766d97 100644 --- a/dlls/winedos/Makefile.in +++ b/dlls/winedos/Makefile.in @@ -7,8 +7,9 @@ IMPORTS = user32 kernel32 ntdll LDIMPORTS = user32.dll kernel32.dll ntdll.dll C_SRCS = \ - dosaspi.c \ devices.c \ + dma.c \ + dosaspi.c \ dosvm.c \ int09.c \ int10.c \ @@ -23,6 +24,7 @@ C_SRCS = \ int67.c \ ioports.c \ module.c \ + soundblaster.c \ vga.c \ xms.c diff --git a/dlls/winedos/dma.c b/dlls/winedos/dma.c new file mode 100644 index 00000000000..e02954c26d7 --- /dev/null +++ b/dlls/winedos/dma.c @@ -0,0 +1,381 @@ +/* + * 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 "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>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; +} diff --git a/dlls/winedos/dosexe.h b/dlls/winedos/dosexe.h index e7fb71f4bcb..c8190fb3112 100644 --- a/dlls/winedos/dosexe.h +++ b/dlls/winedos/dosexe.h @@ -72,6 +72,11 @@ extern int DOSDEV_Write(DWORD dev, DWORD buf, int buflen, int verify); extern int DOSDEV_IoctlRead(DWORD dev, DWORD buf, int buflen); extern int DOSDEV_IoctlWrite(DWORD dev, DWORD buf, int buflen); +/* dma.c */ +extern int DMA_Transfer(int channel,int reqlength,void* buffer); +extern void DMA_ioport_out( WORD port, BYTE val ); +extern BYTE DMA_ioport_in( WORD port ); + /* int09.c */ extern void WINAPI DOSVM_Int09Handler(CONTEXT86*); extern void WINAPI DOSVM_Int09SendScan(BYTE scan,BYTE ascii); @@ -113,6 +118,10 @@ extern void WINAPI DOSVM_Int33Console(MOUSE_EVENT_RECORD*); extern void WINAPI DOSVM_Int67Handler(CONTEXT86*); extern void WINAPI EMS_Ioctl_Handler(CONTEXT86*); +/* soundblaster.c */ +extern void SB_ioport_out( WORD port, BYTE val ); +extern BYTE SB_ioport_in( WORD port ); + /* xms.c */ extern void WINAPI XMS_Handler(CONTEXT86*); diff --git a/dlls/winedos/ioports.c b/dlls/winedos/ioports.c index e8e4bc1fda1..81fd2332818 100644 --- a/dlls/winedos/ioports.c +++ b/dlls/winedos/ioports.c @@ -36,10 +36,51 @@ BOOL WINAPI DOSVM_inport( int port, int size, DWORD *res ) 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,10 +98,62 @@ BOOL WINAPI DOSVM_outport( int port, int size, DWORD value ) 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 */ } diff --git a/dlls/winedos/soundblaster.c b/dlls/winedos/soundblaster.c new file mode 100644 index 00000000000..833351b99d8 --- /dev/null +++ b/dlls/winedos/soundblaster.c @@ -0,0 +1,383 @@ +/* + * 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 "wine/debug.h" +#include "dsound.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; +}