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