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.
41 #define BUFFER_SIZE 0x10000 // Make the DAC buffers 64K x 16 bits
43 // Jaguar memory locations
50 #define SMODE 0xF1A154
54 //uint16 lrxd, rrxd; // I2S ports (into Jaguar)
58 static uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
59 static SDL_AudioSpec desired;
60 static bool SDLSoundInitialized;
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.
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;
69 // Private function prototypes
71 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
74 // Initialize the SDL sound system
78 SDLSoundInitialized = false;
80 if (!vjs.audioEnabled)
82 WriteLog("DAC: Host audio playback disabled.\n");
86 // memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
87 // DACBuffer = (uint16 *)memory_malloc(BUFFER_SIZE * sizeof(uint16), "DAC buffer");
89 desired.freq = GetCalculatedFrequency(); // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
90 desired.format = AUDIO_S16SYS; // This uses the native endian (for portability)...
92 // desired.samples = 4096; // Let's try a 4K buffer (can always go lower)
93 desired.samples = 2048; // Let's try a 2K buffer (can always go lower)
94 desired.callback = SDLSoundCallback;
96 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
97 WriteLog("DAC: Failed to initialize SDL sound...\n");
100 SDLSoundInitialized = true;
102 SDL_PauseAudio(false); // Start playback!
103 WriteLog("DAC: Successfully initialized.\n");
108 // Reset the sound buffer FIFOs
112 LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
116 // Close down the SDL sound subsystem
120 if (SDLSoundInitialized)
122 SDL_PauseAudio(true);
126 // memory_free(DACBuffer);
127 WriteLog("DAC: Done.\n");
131 // SDL callback routine to fill audio buffer
133 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
135 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
137 // Clear the buffer to silence, in case the DAC buffer is empty (or short)
138 //This causes choppy sound... Ick.
139 memset(buffer, desired.silence, length);
140 //WriteLog("DAC: Inside callback...\n");
141 if (LeftFIFOHeadPtr != LeftFIFOTailPtr)
143 //WriteLog("DAC: About to write some data!\n");
144 int numLeftSamplesReady
145 = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
147 int numRightSamplesReady
148 = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
150 //This waits for the slower side to catch up. If writing only one side, then this
151 //causes the buffer not to drain...
153 = (numLeftSamplesReady < numRightSamplesReady
154 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
156 //Kludge, until I can figure out WTF is going on WRT Super Burnout.
157 if (numLeftSamplesReady == 0 || numRightSamplesReady == 0)
158 numSamplesReady = numLeftSamplesReady + numRightSamplesReady;
160 //The numbers look good--it's just that the DSP can't get enough samples in the DAC buffer!
161 //WriteLog("DAC: Left/RightFIFOHeadPtr: %u/%u, Left/RightFIFOTailPtr: %u/%u\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
162 //WriteLog(" numLeft/RightSamplesReady: %i/%i, numSamplesReady: %i, length of buffer: %i\n", numLeftSamplesReady, numRightSamplesReady, numSamplesReady, length);
164 /* if (numSamplesReady > length)
165 numSamplesReady = length;//*/
166 if (numSamplesReady > length / 2) // length / 2 because we're comparing 16-bit lengths
167 numSamplesReady = length / 2;
169 // WriteLog(" Not enough samples to fill the buffer (short by %u L/R samples)...\n", (length / 2) - numSamplesReady);
170 //WriteLog("DAC: %u samples ready.\n", numSamplesReady);
172 // Actually, it's a bit more involved than this, but this is the general idea:
173 // memcpy(buffer, DACBuffer, length);
174 for(int i=0; i<numSamplesReady; i++)
175 ((uint16 *)buffer)[i] = DACBuffer[(LeftFIFOHeadPtr + i) % BUFFER_SIZE];
176 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
177 // buffer[i] = DACBuffer[(LeftFIFOHeadPtr + i) & (BUFFER_SIZE - 1)];
179 LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
180 RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
181 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
182 // LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
183 // RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
184 //WriteLog(" -> Left/RightFIFOHeadPtr: %04X/%04X, Left/RightFIFOTailPtr: %04X/%04X\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
186 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
188 // WriteLog("DAC: Silence...!\n");
192 // Calculate the frequency of SCLK * 32 using the divider
194 int GetCalculatedFrequency(void)
196 int systemClockFrequency = (vjs.hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
198 // We divide by 32 here in order to find the frequency of 32 SCLKs in a row (transferring
199 // 16 bits of left data + 16 bits of right data = 32 bits, 1 SCLK = 1 bit transferred).
200 return systemClockFrequency / (32 * (2 * (SCLKFrequencyDivider + 1)));
203 static int oldFreq = 0;
205 void DACSetNewFrequency(int freq)
212 // Should do some sanity checking on the frequency...
214 if (SDLSoundInitialized)
217 desired.freq = freq;// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
218 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
220 if (SDLSoundInitialized)
222 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
224 // This is bad, Bad, BAD !!! DON'T ABORT BECAUSE WE DIDN'T GET OUR FREQ! !!! FIX !!!
225 #warning !!! FIX !!! Aborting because of SDL audio problem is bad!
226 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
229 #warning "Reimplement GUICrashGracefully!"
230 // GUICrashGracefully("Failed to initialize SDL sound!");
237 if (SDLSoundInitialized)
238 SDL_PauseAudio(false); // Start playback!
242 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
244 void DACWriteByte(uint32 offset, uint8 data, uint32 who/*= UNKNOWN*/)
246 WriteLog("DAC: %s writing BYTE %02X at %08X\n", whoName[who], data, offset);
247 if (offset == SCLK + 3)
248 DACWriteWord(offset - 3, (uint16)data);
251 void DACWriteWord(uint32 offset, uint16 data, uint32 who/*= UNKNOWN*/)
253 if (offset == LTXD + 2)
255 if (!SDLSoundInitialized)
257 // Spin until buffer has been drained (for too fast processors!)...
258 //Small problem--if Head == 0 and Tail == buffer end, then this will fail... !!! FIX !!!
260 // Also, we're taking advantage of the fact that the buffer is a multiple of two
263 while (((LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == LeftFIFOHeadPtr)//;
266 //if ((spin & 0x0FFFFFFF) == 0)
267 // WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
269 if (spin == 0xFFFF0000)
271 uint32 ltail = LeftFIFOTailPtr, lhead = LeftFIFOHeadPtr;
272 WriteLog("Tail=%X, Head=%X", ltail, lhead);
274 WriteLog("\nStuck in left DAC spinlock! Aborting!\n");
275 WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
276 WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
277 WriteLog("From while: Tail=%X, Head=%X", (LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1), LeftFIFOHeadPtr);
280 #warning "Reimplement GUICrashGracefully!"
281 // GUICrashGracefully("Stuck in left DAC spinlock!");
286 SDL_LockAudio(); // Is it necessary to do this? Mebbe.
287 // We use a circular buffer 'cause it's easy. Note that the callback function
288 // takes care of dumping audio to the soundcard...! Also note that we're writing
289 // the samples in the buffer in an interleaved L/R format.
290 LeftFIFOTailPtr = (LeftFIFOTailPtr + 2) % BUFFER_SIZE;
291 DACBuffer[LeftFIFOTailPtr] = data;
294 else if (offset == RTXD + 2)
296 if (!SDLSoundInitialized)
299 Here's what's happening now:
301 Stuck in right DAC spinlock!
304 Tail=681, Head=681, BUFFER_SIZE-1=FFFF
305 From while: Tail=683, Head=681
307 ????? What the FUCK ?????
309 & when I uncomment the lines below spin++; it *doesn't* lock here... WTF?????
311 I think it was missing parentheses causing the fuckup... Seems to work now...
313 Except for Super Burnout now...! Aarrrgggghhhhh!
316 Stuck in left DAC spinlock! Aborting!
317 Tail=AC, Head=AE, BUFFER_SIZE-1=FFFF
318 From while: Tail=AE, Head=AE
320 So it's *really* stuck here in the left FIFO. Figure out why!!!
322 Prolly 'cause it doesn't set the sample rate right away--betcha it works with the BIOS...
323 It gets farther, but then locks here (weird!):
326 Stuck in left DAC spinlock! Aborting!
327 Tail=2564, Head=2566, BUFFER_SIZE-1=FFFF
328 From while: Tail=2566, Head=2566
330 Weird--recompile with more WriteLog() entries and it *doesn't* lock...
331 Yeah, because there was no DSP running. Duh!
334 Stuck in left DAC spinlock! Aborting!
335 LTail=AC, LHead=AE, BUFFER_SIZE-1=FFFF
336 RTail=AF, RHead=AF, BUFFER_SIZE-1=FFFF
337 From while: Tail=AE, Head=AE
339 Odd: The right FIFO is empty, but the left FIFO is full!
340 And this is what is causing the lockup--the DAC callback waits for the side with
341 less samples ready and in this case it's the right channel (that never fills up)
342 that it's waiting for...!
344 Okay, with the kludge in place for the right channel not being filled, we select
345 a track and then it locks here:
348 Stuck in left DAC spinlock! Aborting!
349 LTail=60D8, LHead=60D8, BUFFER_SIZE-1=FFFF
350 RTail=DB, RHead=60D9, BUFFER_SIZE-1=FFFF
351 From while: Tail=60DA, Head=60D8
353 #warning Spinlock problem--!!! FIX !!!
354 #warning Odd: The right FIFO is empty, but the left FIFO is full!
355 // Spin until buffer has been drained (for too fast processors!)...
357 while (((RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == RightFIFOHeadPtr)//;
360 //if ((spin & 0x0FFFFFFF) == 0)
361 // WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
363 if (spin == 0xFFFF0000)
365 uint32 rtail = RightFIFOTailPtr, rhead = RightFIFOHeadPtr;
366 WriteLog("Tail=%X, Head=%X", rtail, rhead);
368 WriteLog("\nStuck in right DAC spinlock! Aborting!\n");
369 WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
370 WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
371 WriteLog("From while: Tail=%X, Head=%X", (RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1), RightFIFOHeadPtr);
374 #warning "Reimplement GUICrashGracefully!"
375 // GUICrashGracefully("Stuck in right DAC spinlock!");
381 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
382 DACBuffer[RightFIFOTailPtr] = data;
386 WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
389 else if (offset == SCLK + 2) // Sample rate
391 WriteLog("DAC: Writing %u to SCLK...\n", data);
392 if ((uint8)data != SCLKFrequencyDivider)
394 SCLKFrequencyDivider = (uint8)data;
395 //Of course a better way would be to query the hardware to find the upper limit...
396 if (data > 7) // Anything less than 8 is too high!
398 if (SDLSoundInitialized)
401 desired.freq = GetCalculatedFrequency();// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
402 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
404 if (SDLSoundInitialized)
406 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
408 // This is bad, Bad, BAD !!! DON'T ABORT BECAUSE WE DIDN'T GET OUR FREQ! !!! FIX !!!
409 #warning !!! FIX !!! Aborting because of SDL audio problem is bad!
410 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
413 #warning "Reimplement GUICrashGracefully!"
414 // GUICrashGracefully("Failed to initialize SDL sound!");
421 if (SDLSoundInitialized)
422 SDL_PauseAudio(false); // Start playback!
426 else if (offset == SMODE + 2)
429 WriteLog("DAC: %s writing to SMODE. Bits: %s%s%s%s%s%s [68K PC=%08X]\n", whoName[who],
430 (data & 0x01 ? "INTERNAL " : ""), (data & 0x02 ? "MODE " : ""),
431 (data & 0x04 ? "WSEN " : ""), (data & 0x08 ? "RISING " : ""),
432 (data & 0x10 ? "FALLING " : ""), (data & 0x20 ? "EVERYWORD" : ""),
433 m68k_get_reg(NULL, M68K_REG_PC));
438 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
440 uint8 DACReadByte(uint32 offset, uint32 who/*= UNKNOWN*/)
442 // WriteLog("DAC: %s reading byte from %08X\n", whoName[who], offset);
446 //static uint16 fakeWord = 0;
447 uint16 DACReadWord(uint32 offset, uint32 who/*= UNKNOWN*/)
449 // WriteLog("DAC: %s reading word from %08X\n", whoName[who], offset);
451 // WriteLog("DAC: %s reading WORD %04X from %08X\n", whoName[who], fakeWord, offset);
452 // return fakeWord++;
453 //NOTE: This only works if a bunch of things are set in BUTCH which we currently don't
454 // check for. !!! FIX !!!
455 // Partially fixed: We check for I2SCNTRL in the JERRY I2S routine...
456 // return GetWordFromButchSSI(offset, who);
457 if (offset == LRXD || offset == RRXD)
459 else if (offset == LRXD + 2)
461 else if (offset == RRXD + 2)
464 return 0xFFFF; // May need SSTAT as well... (but may be a Jaguar II only feature)