2 // DAC (really, Synchronous Serial Interface) Handler
4 // Originally by David Raingeard
5 // GCC/SDL port by Niels Wagenaar (Linux/WIN32) and Caz (BeOS)
6 // Rewritten by James Hammons
7 // (C) 2010 Underground Software
9 // JLH = James Hammons <jlhamm@acm.org>
12 // --- ---------- -------------------------------------------------------------
13 // JLH 01/16/2010 Created this log ;-)
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
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?
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.
29 // After testing on a real Jaguar, it seems clear that the I2S interrupt drives
30 // the audio subsystem. So while you can drive the audio at a *slower* rate than
31 // set by SCLK, you can't drive it any *faster*. Also note, that if the I2S
32 // interrupt is not enabled/running on the DSP, then there is no audio. Also,
33 // audio can be muted by clearing bit 8 of JOYSTICK (JOY1).
35 // Approach: We can run the DSP in the host system's audio IRQ, by running the
36 // DSP for the alloted time (depending on the host buffer size & sample rate)
37 // by simply reading the L/R_I2S (L/RTXD) registers at regular intervals. We
38 // would also have to time the I2S/TIMER0/TIMER1 interrupts in the DSP as well.
39 // This way, we can run the host audio IRQ at, say, 48 KHz and not have to care
40 // so much about SCLK and running a separate buffer and all the attendant
41 // garbage that comes with that awful approach.
43 // There would still be potential gotchas, as the SCLK can theoretically drive
44 // the I2S at 26590906 / 2 (for SCLK == 0) = 13.3 MHz which corresponds to an
45 // audio rate 416 KHz (dividing the I2S rate by 32, for 16-bit stereo). It
46 // seems doubtful that anything useful could come of such a high rate, and we
47 // can probably safely ignore any such ridiculously high audio rates. It won't
48 // sound the same as on a real Jaguar, but who cares? :-)
62 #define BUFFER_SIZE 0x10000 // Make the DAC buffers 64K x 16 bits
64 // Jaguar memory locations
71 #define SMODE 0xF1A154
75 //uint16 lrxd, rrxd; // I2S ports (into Jaguar)
79 static uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
80 static SDL_AudioSpec desired;
81 static bool SDLSoundInitialized;
83 // We can get away with using native endian here because we can tell SDL to use the native
84 // endian when looking at the sample buffer, i.e., no need to worry about it.
86 static uint16 DACBuffer[BUFFER_SIZE];
87 static uint8 SCLKFrequencyDivider = 19; // Default is roughly 22 KHz (20774 Hz in NTSC mode)
88 /*static*/ uint16 serialMode = 0;
90 // Private function prototypes
92 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
95 // Initialize the SDL sound system
99 SDLSoundInitialized = false;
101 if (!vjs.audioEnabled)
103 WriteLog("DAC: Host audio playback disabled.\n");
107 // memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
108 // DACBuffer = (uint16 *)memory_malloc(BUFFER_SIZE * sizeof(uint16), "DAC buffer");
110 desired.freq = GetCalculatedFrequency(); // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
111 desired.format = AUDIO_S16SYS; // This uses the native endian (for portability)...
112 desired.channels = 2;
113 // desired.samples = 4096; // Let's try a 4K buffer (can always go lower)
114 desired.samples = 2048; // Let's try a 2K buffer (can always go lower)
115 desired.callback = SDLSoundCallback;
117 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
118 WriteLog("DAC: Failed to initialize SDL sound...\n");
121 SDLSoundInitialized = true;
123 SDL_PauseAudio(false); // Start playback!
124 WriteLog("DAC: Successfully initialized.\n");
129 // Reset the sound buffer FIFOs
133 LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
137 // Close down the SDL sound subsystem
141 if (SDLSoundInitialized)
143 SDL_PauseAudio(true);
147 // memory_free(DACBuffer);
148 WriteLog("DAC: Done.\n");
152 // Approach: Run the DSP for however many cycles needed to correspond to whatever sample rate
153 // we've set the audio to run at. So, e.g., if we run it at 48 KHz, then we would run the DSP
154 // for however much time it takes to fill the buffer. So with a 2K buffer, this would correspond
155 // to running the DSP for 0.042666... seconds. At 26590906 Hz, this would correspond to
156 // running the DSP for 1134545 cycles. You would then sample the L/RTXD registers every
157 // 1134545 / 2048 = 554 cycles to fill the buffer. You would also have to manage interrupt
158 // timing as well (generating them at the proper times), but that shouldn't be too difficult...
159 // If the DSP isn't running, then fill the buffer with L/RTXD and exit.
162 // SDL callback routine to fill audio buffer
164 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
166 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
168 // Clear the buffer to silence, in case the DAC buffer is empty (or short)
169 //This causes choppy sound... Ick.
170 memset(buffer, desired.silence, length);
171 //WriteLog("DAC: Inside callback...\n");
172 if (LeftFIFOHeadPtr != LeftFIFOTailPtr)
174 //WriteLog("DAC: About to write some data!\n");
175 int numLeftSamplesReady
176 = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
178 int numRightSamplesReady
179 = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
181 //This waits for the slower side to catch up. If writing only one side, then this
182 //causes the buffer not to drain...
184 = (numLeftSamplesReady < numRightSamplesReady
185 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
187 //Kludge, until I can figure out WTF is going on WRT Super Burnout.
188 if (numLeftSamplesReady == 0 || numRightSamplesReady == 0)
189 numSamplesReady = numLeftSamplesReady + numRightSamplesReady;
191 //The numbers look good--it's just that the DSP can't get enough samples in the DAC buffer!
192 //WriteLog("DAC: Left/RightFIFOHeadPtr: %u/%u, Left/RightFIFOTailPtr: %u/%u\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
193 //WriteLog(" numLeft/RightSamplesReady: %i/%i, numSamplesReady: %i, length of buffer: %i\n", numLeftSamplesReady, numRightSamplesReady, numSamplesReady, length);
195 /* if (numSamplesReady > length)
196 numSamplesReady = length;//*/
197 if (numSamplesReady > length / 2) // length / 2 because we're comparing 16-bit lengths
198 numSamplesReady = length / 2;
200 // WriteLog(" Not enough samples to fill the buffer (short by %u L/R samples)...\n", (length / 2) - numSamplesReady);
201 //WriteLog("DAC: %u samples ready.\n", numSamplesReady);
203 // Actually, it's a bit more involved than this, but this is the general idea:
204 // memcpy(buffer, DACBuffer, length);
205 for(int i=0; i<numSamplesReady; i++)
206 ((uint16 *)buffer)[i] = DACBuffer[(LeftFIFOHeadPtr + i) % BUFFER_SIZE];
207 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
208 // buffer[i] = DACBuffer[(LeftFIFOHeadPtr + i) & (BUFFER_SIZE - 1)];
210 LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
211 RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
212 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
213 // LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
214 // RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
215 //WriteLog(" -> Left/RightFIFOHeadPtr: %04X/%04X, Left/RightFIFOTailPtr: %04X/%04X\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
217 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
219 // WriteLog("DAC: Silence...!\n");
223 // Calculate the frequency of SCLK * 32 using the divider
225 int GetCalculatedFrequency(void)
227 int systemClockFrequency = (vjs.hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
229 // We divide by 32 here in order to find the frequency of 32 SCLKs in a row (transferring
230 // 16 bits of left data + 16 bits of right data = 32 bits, 1 SCLK = 1 bit transferred).
231 return systemClockFrequency / (32 * (2 * (SCLKFrequencyDivider + 1)));
234 static int oldFreq = 0;
236 void DACSetNewFrequency(int freq)
243 // Should do some sanity checking on the frequency...
245 if (SDLSoundInitialized)
248 desired.freq = freq;// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
249 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
251 if (SDLSoundInitialized)
253 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
255 // This is bad, Bad, BAD !!! DON'T ABORT BECAUSE WE DIDN'T GET OUR FREQ! !!! FIX !!!
256 #warning !!! FIX !!! Aborting because of SDL audio problem is bad!
257 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
260 #warning "Reimplement GUICrashGracefully!"
261 // GUICrashGracefully("Failed to initialize SDL sound!");
268 if (SDLSoundInitialized)
269 SDL_PauseAudio(false); // Start playback!
273 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
275 void DACWriteByte(uint32 offset, uint8 data, uint32 who/*= UNKNOWN*/)
277 WriteLog("DAC: %s writing BYTE %02X at %08X\n", whoName[who], data, offset);
278 if (offset == SCLK + 3)
279 DACWriteWord(offset - 3, (uint16)data);
282 void DACWriteWord(uint32 offset, uint16 data, uint32 who/*= UNKNOWN*/)
284 if (offset == LTXD + 2)
286 if (!SDLSoundInitialized)
288 // Spin until buffer has been drained (for too fast processors!)...
289 //Small problem--if Head == 0 and Tail == buffer end, then this will fail... !!! FIX !!!
291 // Also, we're taking advantage of the fact that the buffer is a multiple of two
294 while (((LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == LeftFIFOHeadPtr)//;
297 //if ((spin & 0x0FFFFFFF) == 0)
298 // WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
300 if (spin == 0xFFFF0000)
302 uint32 ltail = LeftFIFOTailPtr, lhead = LeftFIFOHeadPtr;
303 WriteLog("Tail=%X, Head=%X", ltail, lhead);
305 WriteLog("\nStuck in left 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", (LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1), LeftFIFOHeadPtr);
311 #warning "Reimplement GUICrashGracefully!"
312 // GUICrashGracefully("Stuck in left DAC spinlock!");
317 SDL_LockAudio(); // Is it necessary to do this? Mebbe.
318 // We use a circular buffer 'cause it's easy. Note that the callback function
319 // takes care of dumping audio to the soundcard...! Also note that we're writing
320 // the samples in the buffer in an interleaved L/R format.
321 LeftFIFOTailPtr = (LeftFIFOTailPtr + 2) % BUFFER_SIZE;
322 DACBuffer[LeftFIFOTailPtr] = data;
325 else if (offset == RTXD + 2)
327 if (!SDLSoundInitialized)
330 Here's what's happening now:
332 Stuck in right DAC spinlock!
335 Tail=681, Head=681, BUFFER_SIZE-1=FFFF
336 From while: Tail=683, Head=681
338 ????? What the FUCK ?????
340 & when I uncomment the lines below spin++; it *doesn't* lock here... WTF?????
342 I think it was missing parentheses causing the fuckup... Seems to work now...
344 Except for Super Burnout now...! Aarrrgggghhhhh!
347 Stuck in left DAC spinlock! Aborting!
348 Tail=AC, Head=AE, BUFFER_SIZE-1=FFFF
349 From while: Tail=AE, Head=AE
351 So it's *really* stuck here in the left FIFO. Figure out why!!!
353 Prolly 'cause it doesn't set the sample rate right away--betcha it works with the BIOS...
354 It gets farther, but then locks here (weird!):
357 Stuck in left DAC spinlock! Aborting!
358 Tail=2564, Head=2566, BUFFER_SIZE-1=FFFF
359 From while: Tail=2566, Head=2566
361 Weird--recompile with more WriteLog() entries and it *doesn't* lock...
362 Yeah, because there was no DSP running. Duh!
365 Stuck in left DAC spinlock! Aborting!
366 LTail=AC, LHead=AE, BUFFER_SIZE-1=FFFF
367 RTail=AF, RHead=AF, BUFFER_SIZE-1=FFFF
368 From while: Tail=AE, Head=AE
370 Odd: The right FIFO is empty, but the left FIFO is full!
371 And this is what is causing the lockup--the DAC callback waits for the side with
372 less samples ready and in this case it's the right channel (that never fills up)
373 that it's waiting for...!
375 Okay, with the kludge in place for the right channel not being filled, we select
376 a track and then it locks here:
379 Stuck in left DAC spinlock! Aborting!
380 LTail=60D8, LHead=60D8, BUFFER_SIZE-1=FFFF
381 RTail=DB, RHead=60D9, BUFFER_SIZE-1=FFFF
382 From while: Tail=60DA, Head=60D8
384 #warning Spinlock problem--!!! FIX !!!
385 #warning Odd: The right FIFO is empty, but the left FIFO is full!
386 // Spin until buffer has been drained (for too fast processors!)...
388 while (((RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == RightFIFOHeadPtr)//;
391 //if ((spin & 0x0FFFFFFF) == 0)
392 // WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
394 if (spin == 0xFFFF0000)
396 uint32 rtail = RightFIFOTailPtr, rhead = RightFIFOHeadPtr;
397 WriteLog("Tail=%X, Head=%X", rtail, rhead);
399 WriteLog("\nStuck in right DAC spinlock! Aborting!\n");
400 WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
401 WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
402 WriteLog("From while: Tail=%X, Head=%X", (RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1), RightFIFOHeadPtr);
405 #warning "Reimplement GUICrashGracefully!"
406 // GUICrashGracefully("Stuck in right DAC spinlock!");
412 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
413 DACBuffer[RightFIFOTailPtr] = data;
417 WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
420 else if (offset == SCLK + 2) // Sample rate
422 WriteLog("DAC: Writing %u to SCLK...\n", data);
423 if ((uint8)data != SCLKFrequencyDivider)
425 SCLKFrequencyDivider = (uint8)data;
426 //Of course a better way would be to query the hardware to find the upper limit...
427 if (data > 7) // Anything less than 8 is too high!
429 if (SDLSoundInitialized)
432 desired.freq = GetCalculatedFrequency();// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
433 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
435 if (SDLSoundInitialized)
437 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
439 // This is bad, Bad, BAD !!! DON'T ABORT BECAUSE WE DIDN'T GET OUR FREQ! !!! FIX !!!
440 #warning !!! FIX !!! Aborting because of SDL audio problem is bad!
441 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
444 #warning "Reimplement GUICrashGracefully!"
445 // GUICrashGracefully("Failed to initialize SDL sound!");
452 if (SDLSoundInitialized)
453 SDL_PauseAudio(false); // Start playback!
457 else if (offset == SMODE + 2)
460 WriteLog("DAC: %s writing to SMODE. Bits: %s%s%s%s%s%s [68K PC=%08X]\n", whoName[who],
461 (data & 0x01 ? "INTERNAL " : ""), (data & 0x02 ? "MODE " : ""),
462 (data & 0x04 ? "WSEN " : ""), (data & 0x08 ? "RISING " : ""),
463 (data & 0x10 ? "FALLING " : ""), (data & 0x20 ? "EVERYWORD" : ""),
464 m68k_get_reg(NULL, M68K_REG_PC));
469 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
471 uint8 DACReadByte(uint32 offset, uint32 who/*= UNKNOWN*/)
473 // WriteLog("DAC: %s reading byte from %08X\n", whoName[who], offset);
477 //static uint16 fakeWord = 0;
478 uint16 DACReadWord(uint32 offset, uint32 who/*= UNKNOWN*/)
480 // WriteLog("DAC: %s reading word from %08X\n", whoName[who], offset);
482 // WriteLog("DAC: %s reading WORD %04X from %08X\n", whoName[who], fakeWord, offset);
483 // return fakeWord++;
484 //NOTE: This only works if a bunch of things are set in BUTCH which we currently don't
485 // check for. !!! FIX !!!
486 // Partially fixed: We check for I2SCNTRL in the JERRY I2S routine...
487 // return GetWordFromButchSSI(offset, who);
488 if (offset == LRXD || offset == RRXD)
490 else if (offset == LRXD + 2)
492 else if (offset == RRXD + 2)
495 return 0xFFFF; // May need SSTAT as well... (but may be a Jaguar II only feature)