Store %gs in the TEB on every call to 16-bit code, and don't restore
[wine] / dlls / winedos / soundblaster.c
1 /*
2  * Soundblaster Emulation
3  *
4  * Copyright 2002 Christian Costa
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include "windef.h"
24 #include "dosexe.h"
25 #include "wine/debug.h"
26 #include "dsound.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(sblaster);
29
30 /* Board Configuration */
31 /* FIXME: Should be in a config file */
32 #define SB_IRQ 5
33 #define SB_IRQ_PRI 11
34 #define SB_DMA 1
35
36 /* Soundblaster state */
37 static int SampleMode;         /* Mono / Stereo */
38 static int SampleRate;
39 static int SamplesCount;
40 static BYTE DSP_Command[256];  /* Store param numbers in bytes for each command */
41 static BYTE DSP_InBuffer[10];  /* Store DSP command bytes parameters from host */
42 static int InSize;             /* Nb of bytes in InBuffer */
43 static BYTE DSP_OutBuffer[10]; /* Store DSP information bytes to host */
44 static int OutSize;            /* Nb of bytes in InBuffer */
45 static int command;            /* Current command */
46 static int end_sound_loop = 0;
47 static int dma_enable = 0;
48
49 /* The maximum size of a dma transfer can be 65536 */
50 #define DMATRFSIZE 1024
51
52 /* DMA can perform 8 or 16-bit transfer */
53 static BYTE dma_buffer[DMATRFSIZE*2];
54
55 /* Direct Sound buffer config */
56 #define DSBUFLEN 4096 /* FIXME: Only this value seems to work */
57
58 /* Direct Sound playback stuff */
59 static HMODULE hmodule;
60 typedef HRESULT (WINAPI* fnDirectSoundCreate) (LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
61 fnDirectSoundCreate lpDirectSoundCreate;
62 static LPDIRECTSOUND lpdsound;
63 static LPDIRECTSOUNDBUFFER lpdsbuf;
64 static DSBUFFERDESC buf_desc;
65 static WAVEFORMATEX wav_fmt;
66 static HANDLE SB_Thread;
67 static UINT buf_off;
68 extern HWND vga_hwnd;
69
70 /* SB_Poll performs DMA transfers and fills the Direct Sound Buffer */
71 static DWORD CALLBACK SB_Poll( void *dummy )
72 {
73     HRESULT result;
74     LPBYTE lpbuf1 = NULL;
75     LPBYTE lpbuf2 = NULL;
76     DWORD dwsize1 = 0;
77     DWORD dwsize2 = 0;
78     DWORD dwbyteswritten1 = 0;
79     DWORD dwbyteswritten2 = 0;
80     int size;
81
82     /* FIXME: this loop must be improved */
83     while(!end_sound_loop)
84     {
85         Sleep(10);
86
87         if (dma_enable) {
88             size = DMA_Transfer(SB_DMA,min(DMATRFSIZE,SamplesCount),dma_buffer);
89         } else
90             continue;
91
92         result = IDirectSoundBuffer_Lock(lpdsbuf,buf_off,size,&lpbuf1,&dwsize1,&lpbuf2,&dwsize2,0);
93         if (result != DS_OK) {
94           ERR("Unable to lock sound buffer !\n");
95           continue;
96         }
97
98         dwbyteswritten1 = min(size,dwsize1);
99         memcpy(lpbuf1,dma_buffer,dwbyteswritten1);
100         if (size>dwsize1) {
101             dwbyteswritten2 = min(size - dwbyteswritten1,dwsize2);
102             memcpy(lpbuf2,dma_buffer+dwbyteswritten1,dwbyteswritten2);
103         }
104         buf_off = (buf_off + dwbyteswritten1 + dwbyteswritten2) % DSBUFLEN;
105
106         result = IDirectSoundBuffer_Unlock(lpdsbuf,lpbuf1,dwbyteswritten1,lpbuf2,dwbyteswritten2);
107         if (result!=DS_OK)
108             ERR("Unable to unlock sound buffer !\n");
109
110         SamplesCount -= size;
111         if (!SamplesCount) {
112             DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
113             dma_enable = 0;
114         }
115     }
116     return 0;
117 }
118
119 BOOL SB_Init()
120 {
121     HRESULT result;
122
123     if (!lpdsound) {
124         hmodule = LoadLibraryA("dsound.dll");
125         if (!hmodule) {
126             ERR("Can't load dsound.dll !\n");
127             return 0;
128         }
129         lpDirectSoundCreate = (fnDirectSoundCreate)GetProcAddress(hmodule,"DirectSoundCreate");
130         if (!lpDirectSoundCreate) {
131             /* CloseHandle(hmodule); */
132             ERR("Can't find DirectSoundCreate function !\n");
133             return 0;
134         }
135         result = (*lpDirectSoundCreate)(NULL,&lpdsound,NULL);
136         if (result != DS_OK) {
137             ERR("Unable to initialize Sound Subsystem err = %lx !\n",result);
138             return 0;
139         }
140
141         /* FIXME: To uncomment when :
142            - SetCooperative level is correctly implemented
143            - an always valid and non changing handle to a windows  (vga_hwnd) is available
144              (this surely needs some work in vga.c)
145         result = IDirectSound_SetCooperativeLevel(lpdsound,vga_hwnd,DSSCL_EXCLUSIVE|DSSCL_PRIORITY);
146         if (result != DS_OK) {
147             ERR("Can't set cooperative level !\n");
148             return 0;
149         }
150         */
151
152         /* Default format */
153         wav_fmt.wFormatTag = WAVE_FORMAT_PCM;
154         wav_fmt.nChannels = 1;
155         wav_fmt.nSamplesPerSec = 22050;
156         wav_fmt.nAvgBytesPerSec = 22050;
157         wav_fmt.nBlockAlign = 1;
158         wav_fmt.wBitsPerSample = 8;
159         wav_fmt.cbSize = 0;
160
161         memset(&buf_desc,0,sizeof(DSBUFFERDESC));
162         buf_desc.dwSize = sizeof(DSBUFFERDESC);
163         buf_desc.dwBufferBytes = DSBUFLEN;
164         buf_desc.lpwfxFormat = &wav_fmt;
165         result = IDirectSound_CreateSoundBuffer(lpdsound,&buf_desc,&lpdsbuf,NULL);
166         if (result != DS_OK) {
167             ERR("Can't create sound buffer !\n");
168             return 0;
169         }
170
171         result = IDirectSoundBuffer_Play(lpdsbuf,0, 0, DSBPLAY_LOOPING);
172         if (result != DS_OK) {
173             ERR("Can't start playing !\n");
174             return 0;
175         }
176
177         buf_off = 0;
178         end_sound_loop = 0;
179         SB_Thread = CreateThread(NULL, 0, SB_Poll, NULL, 0, NULL);
180         TRACE("thread\n");
181         if (!SB_Thread) {
182             ERR("Can't create thread !\n");
183             return 0;
184         }
185     }
186     return 1;
187 }
188
189 void SB_Reset()
190 {
191     int i;
192
193     for(i=0;i<256;i++)
194         DSP_Command[i]=0;
195
196     /* Set Time Constant */
197     DSP_Command[0x40]=1;
198     /* Generate IRQ */
199     DSP_Command[0xF2]=0;
200     /* DMA DAC 8-bits */
201     DSP_Command[0x14]=2;
202     /* Generic DAC/ADC DMA (16-bit, 8-bit) */
203     for(i=0xB0;i<=0xCF;i++)
204         DSP_Command[i]=3;
205     /* DSP Indentification */
206     DSP_Command[0xE0]=1;
207
208     /* Clear command and input buffer */
209     command = -1;
210     InSize = 0;
211
212     /* Put a garbage value in the output buffer */
213     OutSize = 1;
214     if (SB_Init())
215         /* All right, let's put the magic value for autodetection */
216         DSP_OutBuffer[0] = 0xaa;
217     else
218         /* Something is wrong, put 0 to failed audetection */
219         DSP_OutBuffer[0] = 0x00;
220 }
221
222 /* Find a standard sampling rate for DirectSound */
223 int SB_StdSampleRate(int SampleRate)
224 {
225   if (SampleRate>((44100+48000)/2)) return 48000;
226   if (SampleRate>((32000+44100)/2)) return 44100;
227   if (SampleRate>((24000+32000)/2)) return 32000;
228   if (SampleRate>((22050+24000)/2)) return 24000;
229   if (SampleRate>((16000+22050)/2)) return 22050;
230   if (SampleRate>((12000+16000)/2)) return 16000;
231   if (SampleRate>((11025+12000)/2)) return 12000;
232   if (SampleRate>((8000+11025)/2))  return 11025;
233   return 8000;
234 }
235
236 void SB_ioport_out( WORD port, BYTE val )
237 {
238     switch(port)
239     {
240     /* DSP - Reset */
241     case 0x226:
242         TRACE("Resetting DSP.\n");
243         SB_Reset();
244         break;
245     /* DSP - Write Data or Command */
246     case 0x22c:
247         TRACE("val=%x\n",val);
248         if (command == -1) {
249           /* Clear input buffer and set the current command */
250           command = val;
251           InSize = 0;
252         }
253         if (InSize!=DSP_Command[command])
254            /* Fill the input buffer the command parameters if any */
255            DSP_InBuffer[InSize++]=val;
256         else {
257             /* Process command */
258             switch(command)
259             {
260             case 0x10: /* SB */
261                 FIXME("Direct DAC (8-bit) - Not Implemented\n");
262                 break;
263             case 0x14: /* SB */
264                 SamplesCount = DSP_InBuffer[1]+(val<<8)+1;
265                 TRACE("DMA DAC (8-bit) for %x samples\n",SamplesCount);
266                 dma_enable = 1;
267                 break;
268             case 0x20:
269                 FIXME("Direct ADC (8-bit) - Not Implemented\n");
270                 break;
271             case 0x24: /* SB */
272                 FIXME("DMA ADC (8-bit) - Not Implemented\n");
273                 break;
274             case 0x40: /* SB */
275                 SampleRate = 1000000/(256-val);
276                 TRACE("Set Time Constant (%d <-> %d Hz => %d Hz)\n",DSP_InBuffer[0],
277                     SampleRate,SB_StdSampleRate(SampleRate));
278                 SampleRate = SB_StdSampleRate(SampleRate);
279                 wav_fmt.nSamplesPerSec = SampleRate;
280                 wav_fmt.nAvgBytesPerSec = SampleRate;
281                 IDirectSoundBuffer_SetFormat(lpdsbuf,&wav_fmt);
282                 break;
283             /* case 0xBX/0xCX -> See below */
284             case 0xD0: /* SB */
285                 TRACE("Halt DMA operation (8-bit)\n");
286                 dma_enable = 0;
287                 break;
288             case 0xD1: /* SB */
289                 FIXME("Enable Speaker - Not Implemented\n");
290                 break;
291             case 0xD3: /* SB */
292                 FIXME("Disable Speaker - Not Implemented\n");
293                 break;
294             case 0xD4: /* SB */
295                 FIXME("Continue DMA operation (8-bit) - Not Implemented\n");
296                 break;
297             case 0xD8: /* SB */
298                 FIXME("Speaker Status - Not Implemented\n");
299                 break;
300             case 0xE0: /* SB 2.0 */
301                 TRACE("DSP Identification\n");
302                 DSP_OutBuffer[OutSize++] = ~val;
303                 break;
304             case 0xE1: /* SB */
305                TRACE("DSP Version\n");
306                OutSize=2;
307                DSP_OutBuffer[0]=0; /* returns version 1.0 */
308                DSP_OutBuffer[1]=1;
309                 break;
310             case 0xF2: /* SB */
311                 TRACE("IRQ Request (8-bit)\n");
312                 DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
313                 break;
314             default:
315               if (((command&0xF0)==0xB0)||((DSP_InBuffer[0]&0xF0)==0xC0)) {
316                     /* SB16 */
317                     FIXME("Generic DAC/ADC DMA (16-bit, 8-bit) - %d % d\n",command,DSP_InBuffer[1]);
318                     if (command&0x02)
319                         FIXME("Generic DAC/ADC fifo mode not supported\n");
320                     if (command&0x04)
321                         FIXME("Generic DAC/ADC autoinit dma mode not supported\n");
322                     if (command&0x08)
323                         FIXME("Generic DAC/ADC adc mode not supported\n");
324                     switch(command>>4) {
325                     case 0xB:
326                         FIXME("Generic DAC/ADC 8-bit not supported\n");
327                         SampleMode = 0;
328                         break;
329                     case 0xC:
330                         FIXME("Generic DAC/ADC 16-bit not supported\n");
331                         SampleMode = 1;
332                         break;
333                     default:
334                         ERR("Generic DAC/ADC resolution unknown\n");
335                         break;
336                     }
337                     if (DSP_InBuffer[1]&0x010)
338                         FIXME("Generic DAC/ADC signed sample mode not supported\n");
339                     if (DSP_InBuffer[1]&0x020)
340                         FIXME("Generic DAC/ADC stereo mode not supported\n");
341                     SamplesCount = DSP_InBuffer[2]+(val<<8)+1;
342                     TRACE("Generic DMA for %x samples\n",SamplesCount);
343                     dma_enable = 1;
344                 }
345                 else
346                     FIXME("DSP command %x not supported\n",val);
347             }
348             /* Empty the input buffer and end the command */
349             InSize = 0;
350             command = -1;
351         }
352     }
353 }
354
355 BYTE SB_ioport_in( WORD port )
356 {
357     BYTE res = 0;
358
359     switch(port)
360     {
361     /* DSP Read Data */
362     case 0x22a:
363         /* Value in the read buffer */
364       if (OutSize)
365           res = DSP_OutBuffer[--OutSize];
366       else
367           /* return the last byte */
368           res = DSP_OutBuffer[0];
369       break;
370     /* DSP - Write Buffer Status */
371     case 0x22c:
372         /* DSP always ready for writing */
373         res = 0x00;
374         break;
375     /* DSP - Data Available Status */
376     /* DSP - IRQ Acknowledge, 8-bit */
377     case 0x22e:
378         /* DSP data availability check */
379         if (OutSize)
380             res = 0x80;
381         else
382             res = 0x00;
383         break;
384     }
385     return res;
386 }