]> Shamusworld >> Repos - virtualjaguar/blob - src/dac.cpp
Added temporary patch to remove libcdio dependency.
[virtualjaguar] / src / dac.cpp
1 //
2 // DAC (really, Synchronous Serial Interface) Handler
3 //
4 // Originally by David Raingeard
5 // GCC/SDL port by Niels Wagenaar (Linux/WIN32) and Caz (BeOS)
6 // Rewritten by James L. Hammons
7 //
8
9 // Need to set up defaults that the BIOS sets for the SSI here in DACInit()... !!! FIX !!!
10 // or something like that... Seems like it already does, but it doesn't seem to
11 // work correctly...! Perhaps just need to set up SSI stuff so BUTCH doesn't get
12 // confused...
13
14 // ALSO: Need to implement some form of proper locking to replace the clusterfuck
15 //       that is the current spinlock implementation. Since the DSP is a separate
16 //       entity, could we get away with running it in the sound IRQ?
17
18 // ALSO: It may be a good idea to physically separate the left and right buffers
19 //       to prevent things like the DSP filling only one side and such. Do such
20 //       mono modes exist on the Jag? Seems to according to Super Burnout.
21
22 #include "dac.h"
23
24 #include "SDL.h"
25 #include "m68k.h"
26 #include "jaguar.h"
27 #include "settings.h"
28 #include "log.h"
29
30 //#define DEBUG_DAC
31
32 #define BUFFER_SIZE             0x10000                                         // Make the DAC buffers 64K x 16 bits
33
34 // Jaguar memory locations
35
36 #define LTXD                    0xF1A148
37 #define RTXD                    0xF1A14C
38 #define LRXD                    0xF1A148
39 #define RRXD                    0xF1A14C
40 #define SCLK                    0xF1A150
41 #define SMODE                   0xF1A154
42
43 // Global variables
44
45 uint16 lrxd, rrxd;                                                                      // I2S ports (into Jaguar)
46
47 // Local variables
48
49 static uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
50 static SDL_AudioSpec desired;
51 static bool SDLSoundInitialized = false;
52
53 // We can get away with using native endian here because we can tell SDL to use the native
54 // endian when looking at the sample buffer, i.e., no need to worry about it.
55
56 static uint16 DACBuffer[BUFFER_SIZE];
57 static uint8 SCLKFrequencyDivider = 19;                         // Default is roughly 22 KHz (20774 Hz in NTSC mode)
58 /*static*/ uint16 serialMode = 0;
59
60 // Private function prototypes
61
62 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
63 int GetCalculatedFrequency(void);
64
65 //
66 // Initialize the SDL sound system
67 //
68 void DACInit(void)
69 {
70 //      memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
71 //      DACBuffer = (uint16 *)memory_malloc(BUFFER_SIZE * sizeof(uint16), "DAC buffer");
72
73         desired.freq = GetCalculatedFrequency();                // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
74         desired.format = AUDIO_S16SYS;                                  // This uses the native endian (for portability)...
75         desired.channels = 2;
76 //      desired.samples = 4096;                                                 // Let's try a 4K buffer (can always go lower)
77         desired.samples = 2048;                                                 // Let's try a 2K buffer (can always go lower)
78         desired.callback = SDLSoundCallback;
79
80         if (SDL_OpenAudio(&desired, NULL) < 0)                  // NULL means SDL guarantees what we want
81                 WriteLog("DAC: Failed to initialize SDL sound...\n");
82         else
83         {
84                 SDLSoundInitialized = true;
85                 DACReset();
86                 SDL_PauseAudio(false);                                                  // Start playback!
87                 WriteLog("DAC: Successfully initialized.\n");
88         }
89 }
90
91 //
92 // Reset the sound buffer FIFOs
93 //
94 void DACReset(void)
95 {
96         LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
97 }
98
99 //
100 // Close down the SDL sound subsystem
101 //
102 void DACDone(void)
103 {
104         if (SDLSoundInitialized)
105         {
106                 SDL_PauseAudio(true);
107                 SDL_CloseAudio();
108         }
109
110 //      memory_free(DACBuffer);
111         WriteLog("DAC: Done.\n");
112 }
113
114 //
115 // SDL callback routine to fill audio buffer
116 //
117 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
118 //
119 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
120 {
121         // Clear the buffer to silence, in case the DAC buffer is empty (or short)
122 //This causes choppy sound... Ick.
123         memset(buffer, desired.silence, length);
124 //WriteLog("DAC: Inside callback...\n");
125         if (LeftFIFOHeadPtr != LeftFIFOTailPtr)
126         {
127 //WriteLog("DAC: About to write some data!\n");
128                 int numLeftSamplesReady
129                         = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
130                                 - LeftFIFOHeadPtr;
131                 int numRightSamplesReady
132                         = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
133                                 - RightFIFOHeadPtr;
134 //This waits for the slower side to catch up. If writing only one side, then this
135 //causes the buffer not to drain...
136                 int numSamplesReady
137                         = (numLeftSamplesReady < numRightSamplesReady
138                                 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
139
140 //Kludge, until I can figure out WTF is going on WRT Super Burnout.
141 if (numLeftSamplesReady == 0 || numRightSamplesReady == 0)
142         numSamplesReady = numLeftSamplesReady + numRightSamplesReady;
143
144 //The numbers look good--it's just that the DSP can't get enough samples in the DAC buffer!
145 //WriteLog("DAC: Left/RightFIFOHeadPtr: %u/%u, Left/RightFIFOTailPtr: %u/%u\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
146 //WriteLog("     numLeft/RightSamplesReady: %i/%i, numSamplesReady: %i, length of buffer: %i\n", numLeftSamplesReady, numRightSamplesReady, numSamplesReady, length);
147
148 /*              if (numSamplesReady > length)
149                         numSamplesReady = length;//*/
150                 if (numSamplesReady > length / 2)       // length / 2 because we're comparing 16-bit lengths
151                         numSamplesReady = length / 2;
152 //else
153 //      WriteLog("     Not enough samples to fill the buffer (short by %u L/R samples)...\n", (length / 2) - numSamplesReady);
154 //WriteLog("DAC: %u samples ready.\n", numSamplesReady);
155
156                 // Actually, it's a bit more involved than this, but this is the general idea:
157 //              memcpy(buffer, DACBuffer, length);
158                 for(int i=0; i<numSamplesReady; i++)
159                         ((uint16 *)buffer)[i] = DACBuffer[(LeftFIFOHeadPtr + i) % BUFFER_SIZE];
160                         // Could also use (as long as BUFFER_SIZE is a multiple of 2):
161 //                      buffer[i] = DACBuffer[(LeftFIFOHeadPtr + i) & (BUFFER_SIZE - 1)];
162
163                 LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
164                 RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
165                 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
166 //              LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
167 //              RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
168 //WriteLog("  -> Left/RightFIFOHeadPtr: %04X/%04X, Left/RightFIFOTailPtr: %04X/%04X\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
169         }
170 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
171 //      else
172 //              WriteLog("DAC: Silence...!\n");
173 }
174
175 //
176 // Calculate the frequency of SCLK * 32 using the divider
177 //
178 int GetCalculatedFrequency(void)
179 {
180         int systemClockFrequency = (vjs.hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
181
182         // We divide by 32 here in order to find the frequency of 32 SCLKs in a row (transferring
183         // 16 bits of left data + 16 bits of right data = 32 bits, 1 SCLK = 1 bit transferred).
184         return systemClockFrequency / (32 * (2 * (SCLKFrequencyDivider + 1)));
185 }
186
187 //
188 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
189 //
190 void DACWriteByte(uint32 offset, uint8 data, uint32 who/*= UNKNOWN*/)
191 {
192         WriteLog("DAC: %s writing BYTE %02X at %08X\n", whoName[who], data, offset);
193         if (offset == SCLK + 3)
194                 DACWriteWord(offset - 3, (uint16)data);
195 }
196
197 void DACWriteWord(uint32 offset, uint16 data, uint32 who/*= UNKNOWN*/)
198 {
199         if (offset == LTXD + 2)
200         {
201                 // Spin until buffer has been drained (for too fast processors!)...
202 //Small problem--if Head == 0 and Tail == buffer end, then this will fail... !!! FIX !!!
203 //[DONE]
204                 // Also, we're taking advantage of the fact that the buffer is a multiple of two
205                 // in this check...
206 uint32 spin = 0;
207                 while (((LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == LeftFIFOHeadPtr)//;
208                 {
209 spin++;
210 //if ((spin & 0x0FFFFFFF) == 0)
211 //      WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
212
213 if (spin == 0xFFFF0000)
214 {
215 uint32 ltail = LeftFIFOTailPtr, lhead = LeftFIFOHeadPtr;
216 WriteLog("Tail=%X, Head=%X", ltail, lhead);
217
218         WriteLog("\nStuck in left DAC spinlock! Aborting!\n");
219         WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
220         WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
221         WriteLog("From while: Tail=%X, Head=%X", (LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1), LeftFIFOHeadPtr);
222         log_done();
223         exit(0);
224 }
225                 }//*/
226
227                 SDL_LockAudio();                                                        // Is it necessary to do this? Mebbe.
228                 // We use a circular buffer 'cause it's easy. Note that the callback function
229                 // takes care of dumping audio to the soundcard...! Also note that we're writing
230                 // the samples in the buffer in an interleaved L/R format.
231                 LeftFIFOTailPtr = (LeftFIFOTailPtr + 2) % BUFFER_SIZE;
232                 DACBuffer[LeftFIFOTailPtr] = data;
233                 SDL_UnlockAudio();
234         }
235         else if (offset == RTXD + 2)
236         {
237 /*
238 Here's what's happening now:
239
240 Stuck in right DAC spinlock!
241 Aborting!
242
243 Tail=681, Head=681, BUFFER_SIZE-1=FFFF
244 From while: Tail=683, Head=681
245
246 ????? What the FUCK ?????
247
248 & when I uncomment the lines below spin++; it *doesn't* lock here... WTF?????
249
250 I think it was missing parentheses causing the fuckup... Seems to work now...
251
252 Except for Super Burnout now...! Aarrrgggghhhhh!
253
254 Tail=AC, Head=AE
255 Stuck in left DAC spinlock! Aborting!
256 Tail=AC, Head=AE, BUFFER_SIZE-1=FFFF
257 From while: Tail=AE, Head=AE
258
259 So it's *really* stuck here in the left FIFO. Figure out why!!!
260
261 Prolly 'cause it doesn't set the sample rate right away--betcha it works with the BIOS...
262 It gets farther, but then locks here (weird!):
263
264 Tail=2564, Head=2566
265 Stuck in left DAC spinlock! Aborting!
266 Tail=2564, Head=2566, BUFFER_SIZE-1=FFFF
267 From while: Tail=2566, Head=2566
268
269 Weird--recompile with more WriteLog() entries and it *doesn't* lock...
270 Yeah, because there was no DSP running. Duh!
271
272 Tail=AC, Head=AE
273 Stuck in left DAC spinlock! Aborting!
274 LTail=AC, LHead=AE, BUFFER_SIZE-1=FFFF
275 RTail=AF, RHead=AF, BUFFER_SIZE-1=FFFF
276 From while: Tail=AE, Head=AE
277
278 Odd: The right FIFO is empty, but the left FIFO is full!
279 And this is what is causing the lockup--the DAC callback waits for the side with
280 less samples ready and in this case it's the right channel (that never fills up)
281 that it's waiting for...!
282
283 Okay, with the kludge in place for the right channel not being filled, we select
284 a track and then it locks here:
285
286 Tail=60D8, Head=60DA
287 Stuck in left DAC spinlock! Aborting!
288 LTail=60D8, LHead=60D8, BUFFER_SIZE-1=FFFF
289 RTail=DB, RHead=60D9, BUFFER_SIZE-1=FFFF
290 From while: Tail=60DA, Head=60D8
291 */
292                 // Spin until buffer has been drained (for too fast processors!)...
293 uint32 spin = 0;
294                 while (((RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == RightFIFOHeadPtr)//;
295                 {
296 spin++;
297 //if ((spin & 0x0FFFFFFF) == 0)
298 //      WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
299
300 if (spin == 0xFFFF0000)
301 {
302 uint32 rtail = RightFIFOTailPtr, rhead = RightFIFOHeadPtr;
303 WriteLog("Tail=%X, Head=%X", rtail, rhead);
304
305         WriteLog("\nStuck in right DAC spinlock! Aborting!\n");
306         WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
307         WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
308         WriteLog("From while: Tail=%X, Head=%X", (RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1), RightFIFOHeadPtr);
309         log_done();
310         exit(0);
311 }
312                 }//*/
313
314                 SDL_LockAudio();
315                 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
316                 DACBuffer[RightFIFOTailPtr] = data;
317                 SDL_UnlockAudio();
318 /*#ifdef DEBUG_DAC
319                 else
320                         WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
321 #endif*/
322         }
323         else if (offset == SCLK + 2)                                    // Sample rate
324         {
325                 WriteLog("DAC: Writing %u to SCLK...\n", data);
326                 if ((uint8)data != SCLKFrequencyDivider)
327                 {
328                         SCLKFrequencyDivider = (uint8)data;
329 //Of course a better way would be to query the hardware to find the upper limit...
330                         if (data > 7)   // Anything less than 8 is too high!
331                         {
332                                 if (SDLSoundInitialized)
333                                         SDL_CloseAudio();
334
335                                 desired.freq = GetCalculatedFrequency();// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
336                                 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
337
338                                 if (SDLSoundInitialized)
339                                 {
340                                         if (SDL_OpenAudio(&desired, NULL) < 0)  // NULL means SDL guarantees what we want
341                                         {
342 // This is bad, Bad, BAD !!! DON'T ABORT BECAUSE WE DIDN'T GET OUR FREQ! !!! FIX !!!
343 #warning !!! FIX !!! Aborting because of SDL audio problem is bad!
344                                                 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
345                                                 log_done();
346                                                 exit(1);
347                                         }
348                                 }
349
350                                 DACReset();
351
352                                 if (SDLSoundInitialized)
353                                         SDL_PauseAudio(false);                  // Start playback!
354                         }
355                 }
356         }
357         else if (offset == SMODE + 2)
358         {
359                 serialMode = data;
360                 WriteLog("DAC: %s writing to SMODE. Bits: %s%s%s%s%s%s [68K PC=%08X]\n", whoName[who],
361                         (data & 0x01 ? "INTERNAL " : ""), (data & 0x02 ? "MODE " : ""),
362                         (data & 0x04 ? "WSEN " : ""), (data & 0x08 ? "RISING " : ""),
363                         (data & 0x10 ? "FALLING " : ""), (data & 0x20 ? "EVERYWORD" : ""),
364                         m68k_get_reg(NULL, M68K_REG_PC));
365         }
366 }
367
368 //
369 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
370 //
371 uint8 DACReadByte(uint32 offset, uint32 who/*= UNKNOWN*/)
372 {
373 //      WriteLog("DAC: %s reading byte from %08X\n", whoName[who], offset);
374         return 0xFF;
375 }
376
377 //static uint16 fakeWord = 0;
378 uint16 DACReadWord(uint32 offset, uint32 who/*= UNKNOWN*/)
379 {
380 //      WriteLog("DAC: %s reading word from %08X\n", whoName[who], offset);
381 //      return 0xFFFF;
382 //      WriteLog("DAC: %s reading WORD %04X from %08X\n", whoName[who], fakeWord, offset);
383 //      return fakeWord++;
384 //NOTE: This only works if a bunch of things are set in BUTCH which we currently don't
385 //      check for. !!! FIX !!!
386 // Partially fixed: We check for I2SCNTRL in the JERRY I2S routine...
387 //      return GetWordFromButchSSI(offset, who);
388         if (offset == LRXD || offset == RRXD)
389                 return 0x0000;
390         else if (offset == LRXD + 2)
391                 return lrxd;
392         else if (offset == RRXD + 2)
393                 return rrxd;
394
395         return 0xFFFF;  // May need SSTAT as well... (but may be a Jaguar II only feature)
396 }