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 L. Hammons
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
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?
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.
33 #define BUFFER_SIZE 0x10000 // Make the DAC buffers 64K x 16 bits
35 // Jaguar memory locations
42 #define SMODE 0xF1A154
46 uint16 lrxd, rrxd; // I2S ports (into Jaguar)
50 static uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
51 static SDL_AudioSpec desired;
52 static bool SDLSoundInitialized = false;
54 // We can get away with using native endian here because we can tell SDL to use the native
55 // endian when looking at the sample buffer, i.e., no need to worry about it.
57 static uint16 DACBuffer[BUFFER_SIZE];
58 static uint8 SCLKFrequencyDivider = 19; // Default is roughly 22 KHz (20774 Hz in NTSC mode)
59 /*static*/ uint16 serialMode = 0;
61 // Private function prototypes
63 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
64 int GetCalculatedFrequency(void);
67 // Initialize the SDL sound system
71 // memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
72 // DACBuffer = (uint16 *)memory_malloc(BUFFER_SIZE * sizeof(uint16), "DAC buffer");
74 desired.freq = GetCalculatedFrequency(); // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
75 desired.format = AUDIO_S16SYS; // This uses the native endian (for portability)...
77 // desired.samples = 4096; // Let's try a 4K buffer (can always go lower)
78 desired.samples = 2048; // Let's try a 2K buffer (can always go lower)
79 desired.callback = SDLSoundCallback;
81 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
82 WriteLog("DAC: Failed to initialize SDL sound...\n");
85 SDLSoundInitialized = true;
87 SDL_PauseAudio(false); // Start playback!
88 WriteLog("DAC: Successfully initialized.\n");
93 // Reset the sound buffer FIFOs
97 LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
101 // Close down the SDL sound subsystem
105 if (SDLSoundInitialized)
107 SDL_PauseAudio(true);
111 // memory_free(DACBuffer);
112 WriteLog("DAC: Done.\n");
116 // SDL callback routine to fill audio buffer
118 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
120 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
122 // Clear the buffer to silence, in case the DAC buffer is empty (or short)
123 //This causes choppy sound... Ick.
124 memset(buffer, desired.silence, length);
125 //WriteLog("DAC: Inside callback...\n");
126 if (LeftFIFOHeadPtr != LeftFIFOTailPtr)
128 //WriteLog("DAC: About to write some data!\n");
129 int numLeftSamplesReady
130 = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
132 int numRightSamplesReady
133 = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
135 //This waits for the slower side to catch up. If writing only one side, then this
136 //causes the buffer not to drain...
138 = (numLeftSamplesReady < numRightSamplesReady
139 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
141 //Kludge, until I can figure out WTF is going on WRT Super Burnout.
142 if (numLeftSamplesReady == 0 || numRightSamplesReady == 0)
143 numSamplesReady = numLeftSamplesReady + numRightSamplesReady;
145 //The numbers look good--it's just that the DSP can't get enough samples in the DAC buffer!
146 //WriteLog("DAC: Left/RightFIFOHeadPtr: %u/%u, Left/RightFIFOTailPtr: %u/%u\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
147 //WriteLog(" numLeft/RightSamplesReady: %i/%i, numSamplesReady: %i, length of buffer: %i\n", numLeftSamplesReady, numRightSamplesReady, numSamplesReady, length);
149 /* if (numSamplesReady > length)
150 numSamplesReady = length;//*/
151 if (numSamplesReady > length / 2) // length / 2 because we're comparing 16-bit lengths
152 numSamplesReady = length / 2;
154 // WriteLog(" Not enough samples to fill the buffer (short by %u L/R samples)...\n", (length / 2) - numSamplesReady);
155 //WriteLog("DAC: %u samples ready.\n", numSamplesReady);
157 // Actually, it's a bit more involved than this, but this is the general idea:
158 // memcpy(buffer, DACBuffer, length);
159 for(int i=0; i<numSamplesReady; i++)
160 ((uint16 *)buffer)[i] = DACBuffer[(LeftFIFOHeadPtr + i) % BUFFER_SIZE];
161 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
162 // buffer[i] = DACBuffer[(LeftFIFOHeadPtr + i) & (BUFFER_SIZE - 1)];
164 LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
165 RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
166 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
167 // LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
168 // RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
169 //WriteLog(" -> Left/RightFIFOHeadPtr: %04X/%04X, Left/RightFIFOTailPtr: %04X/%04X\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
171 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
173 // WriteLog("DAC: Silence...!\n");
177 // Calculate the freq9uency of SCLK * 32 using the divider
179 int GetCalculatedFrequency(void)
181 int systemClockFrequency = (vjs.hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
183 // We divide by 32 here in order to find the frequency of 32 SCLKs in a row (transferring
184 // 16 bits of left data + 16 bits of right data = 32 bits, 1 SCLK = 1 bit transferred).
185 return systemClockFrequency / (32 * (2 * (SCLKFrequencyDivider + 1)));
189 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
191 void DACWriteByte(uint32 offset, uint8 data, uint32 who/*= UNKNOWN*/)
193 WriteLog("DAC: %s writing BYTE %02X at %08X\n", whoName[who], data, offset);
194 if (offset == SCLK + 3)
195 DACWriteWord(offset - 3, (uint16)data);
198 void DACWriteWord(uint32 offset, uint16 data, uint32 who/*= UNKNOWN*/)
200 if (offset == LTXD + 2)
202 // Spin until buffer has been drained (for too fast processors!)...
203 //Small problem--if Head == 0 and Tail == buffer end, then this will fail... !!! FIX !!!
205 // Also, we're taking advantage of the fact that the buffer is a multiple of two
208 while (((LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == LeftFIFOHeadPtr)//;
211 //if ((spin & 0x0FFFFFFF) == 0)
212 // WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
214 if (spin == 0xFFFF0000)
216 uint32 ltail = LeftFIFOTailPtr, lhead = LeftFIFOHeadPtr;
217 WriteLog("Tail=%X, Head=%X", ltail, lhead);
219 WriteLog("\nStuck in left DAC spinlock! Aborting!\n");
220 WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
221 WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
222 WriteLog("From while: Tail=%X, Head=%X", (LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1), LeftFIFOHeadPtr);
225 GUICrashGracefully("Stuck in left DAC spinlock!");
230 SDL_LockAudio(); // Is it necessary to do this? Mebbe.
231 // We use a circular buffer 'cause it's easy. Note that the callback function
232 // takes care of dumping audio to the soundcard...! Also note that we're writing
233 // the samples in the buffer in an interleaved L/R format.
234 LeftFIFOTailPtr = (LeftFIFOTailPtr + 2) % BUFFER_SIZE;
235 DACBuffer[LeftFIFOTailPtr] = data;
238 else if (offset == RTXD + 2)
241 Here's what's happening now:
243 Stuck in right DAC spinlock!
246 Tail=681, Head=681, BUFFER_SIZE-1=FFFF
247 From while: Tail=683, Head=681
249 ????? What the FUCK ?????
251 & when I uncomment the lines below spin++; it *doesn't* lock here... WTF?????
253 I think it was missing parentheses causing the fuckup... Seems to work now...
255 Except for Super Burnout now...! Aarrrgggghhhhh!
258 Stuck in left DAC spinlock! Aborting!
259 Tail=AC, Head=AE, BUFFER_SIZE-1=FFFF
260 From while: Tail=AE, Head=AE
262 So it's *really* stuck here in the left FIFO. Figure out why!!!
264 Prolly 'cause it doesn't set the sample rate right away--betcha it works with the BIOS...
265 It gets farther, but then locks here (weird!):
268 Stuck in left DAC spinlock! Aborting!
269 Tail=2564, Head=2566, BUFFER_SIZE-1=FFFF
270 From while: Tail=2566, Head=2566
272 Weird--recompile with more WriteLog() entries and it *doesn't* lock...
273 Yeah, because there was no DSP running. Duh!
276 Stuck in left DAC spinlock! Aborting!
277 LTail=AC, LHead=AE, BUFFER_SIZE-1=FFFF
278 RTail=AF, RHead=AF, BUFFER_SIZE-1=FFFF
279 From while: Tail=AE, Head=AE
281 Odd: The right FIFO is empty, but the left FIFO is full!
282 And this is what is causing the lockup--the DAC callback waits for the side with
283 less samples ready and in this case it's the right channel (that never fills up)
284 that it's waiting for...!
286 Okay, with the kludge in place for the right channel not being filled, we select
287 a track and then it locks here:
290 Stuck in left DAC spinlock! Aborting!
291 LTail=60D8, LHead=60D8, BUFFER_SIZE-1=FFFF
292 RTail=DB, RHead=60D9, BUFFER_SIZE-1=FFFF
293 From while: Tail=60DA, Head=60D8
295 #warning Spinlock problem--!!! FIX !!!
296 #warning Odd: The right FIFO is empty, but the left FIFO is full!
297 // Spin until buffer has been drained (for too fast processors!)...
299 while (((RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == RightFIFOHeadPtr)//;
302 //if ((spin & 0x0FFFFFFF) == 0)
303 // WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
305 if (spin == 0xFFFF0000)
307 uint32 rtail = RightFIFOTailPtr, rhead = RightFIFOHeadPtr;
308 WriteLog("Tail=%X, Head=%X", rtail, rhead);
310 WriteLog("\nStuck in right DAC spinlock! Aborting!\n");
311 WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
312 WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
313 WriteLog("From while: Tail=%X, Head=%X", (RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1), RightFIFOHeadPtr);
316 GUICrashGracefully("Stuck in right DAC spinlock!");
322 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
323 DACBuffer[RightFIFOTailPtr] = data;
327 WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
330 else if (offset == SCLK + 2) // Sample rate
332 WriteLog("DAC: Writing %u to SCLK...\n", data);
333 if ((uint8)data != SCLKFrequencyDivider)
335 SCLKFrequencyDivider = (uint8)data;
336 //Of course a better way would be to query the hardware to find the upper limit...
337 if (data > 7) // Anything less than 8 is too high!
339 if (SDLSoundInitialized)
342 desired.freq = GetCalculatedFrequency();// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
343 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
345 if (SDLSoundInitialized)
347 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
349 // This is bad, Bad, BAD !!! DON'T ABORT BECAUSE WE DIDN'T GET OUR FREQ! !!! FIX !!!
350 #warning !!! FIX !!! Aborting because of SDL audio problem is bad!
351 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
354 GUICrashGracefully("Failed to initialize SDL sound!");
361 if (SDLSoundInitialized)
362 SDL_PauseAudio(false); // Start playback!
366 else if (offset == SMODE + 2)
369 WriteLog("DAC: %s writing to SMODE. Bits: %s%s%s%s%s%s [68K PC=%08X]\n", whoName[who],
370 (data & 0x01 ? "INTERNAL " : ""), (data & 0x02 ? "MODE " : ""),
371 (data & 0x04 ? "WSEN " : ""), (data & 0x08 ? "RISING " : ""),
372 (data & 0x10 ? "FALLING " : ""), (data & 0x20 ? "EVERYWORD" : ""),
373 m68k_get_reg(NULL, M68K_REG_PC));
378 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
380 uint8 DACReadByte(uint32 offset, uint32 who/*= UNKNOWN*/)
382 // WriteLog("DAC: %s reading byte from %08X\n", whoName[who], offset);
386 //static uint16 fakeWord = 0;
387 uint16 DACReadWord(uint32 offset, uint32 who/*= UNKNOWN*/)
389 // WriteLog("DAC: %s reading word from %08X\n", whoName[who], offset);
391 // WriteLog("DAC: %s reading WORD %04X from %08X\n", whoName[who], fakeWord, offset);
392 // return fakeWord++;
393 //NOTE: This only works if a bunch of things are set in BUTCH which we currently don't
394 // check for. !!! FIX !!!
395 // Partially fixed: We check for I2SCNTRL in the JERRY I2S routine...
396 // return GetWordFromButchSSI(offset, who);
397 if (offset == LRXD || offset == RRXD)
399 else if (offset == LRXD + 2)
401 else if (offset == RRXD + 2)
404 return 0xFFFF; // May need SSTAT as well... (but may be a Jaguar II only feature)