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
24 #define BUFFER_SIZE 0x10000 // Make the DAC buffers 64K x 16 bits
26 // Jaguar memory locations
33 #define SMODE 0xF1A154
37 uint16 lrxd, rrxd; // I2S ports (into Jaguar)
41 static uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
42 static SDL_AudioSpec desired;
43 static bool SDLSoundInitialized = false;
45 // We can get away with using native endian here because we can tell SDL to use the native
46 // endian when looking at the sample buffer, i.e., no need to worry about it.
48 static uint16 DACBuffer[BUFFER_SIZE];
49 static uint8 SCLKFrequencyDivider = 19; // Default is roughly 22 KHz (20774 Hz in NTSC mode)
50 /*static*/ uint16 serialMode = 0;
52 // Private function prototypes
54 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
55 int GetCalculatedFrequency(void);
58 // Initialize the SDL sound system
62 // memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
63 // DACBuffer = (uint16 *)memory_malloc(BUFFER_SIZE * sizeof(uint16), "DAC buffer");
65 desired.freq = GetCalculatedFrequency(); // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
66 desired.format = AUDIO_S16SYS; // This uses the native endian (for portability)...
68 // desired.samples = 4096; // Let's try a 4K buffer (can always go lower)
69 desired.samples = 2048; // Let's try a 2K buffer (can always go lower)
70 desired.callback = SDLSoundCallback;
72 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
73 WriteLog("DAC: Failed to initialize SDL sound...\n");
76 SDLSoundInitialized = true;
78 SDL_PauseAudio(false); // Start playback!
79 WriteLog("DAC: Successfully initialized.\n");
84 // Reset the sound buffer FIFOs
88 LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
92 // Close down the SDL sound subsystem
96 if (SDLSoundInitialized)
102 // memory_free(DACBuffer);
103 WriteLog("DAC: Done.\n");
107 // SDL callback routine to fill audio buffer
109 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
111 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
113 // Clear the buffer to silence, in case the DAC buffer is empty (or short)
114 //This causes choppy sound... Ick.
115 memset(buffer, desired.silence, length);
116 //WriteLog("DAC: Inside callback...\n");
117 if (LeftFIFOHeadPtr != LeftFIFOTailPtr)
119 //WriteLog("DAC: About to write some data!\n");
120 int numLeftSamplesReady
121 = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
123 int numRightSamplesReady
124 = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
126 //This waits for the slower side to catch up. If writing only one side, then this
127 //causes the buffer not to drain...
129 = (numLeftSamplesReady < numRightSamplesReady
130 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
132 //Kludge, until I can figure out WTF is going on WRT Super Burnout.
133 if (numLeftSamplesReady == 0 || numRightSamplesReady == 0)
134 numSamplesReady = numLeftSamplesReady + numRightSamplesReady;
136 //The numbers look good--it's just that the DSP can't get enough samples in the DAC buffer!
137 //WriteLog("DAC: Left/RightFIFOHeadPtr: %u/%u, Left/RightFIFOTailPtr: %u/%u\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
138 //WriteLog(" numLeft/RightSamplesReady: %i/%i, numSamplesReady: %i, length of buffer: %i\n", numLeftSamplesReady, numRightSamplesReady, numSamplesReady, length);
140 /* if (numSamplesReady > length)
141 numSamplesReady = length;//*/
142 if (numSamplesReady > length / 2) // length / 2 because we're comparing 16-bit lengths
143 numSamplesReady = length / 2;
145 // WriteLog(" Not enough samples to fill the buffer (short by %u L/R samples)...\n", (length / 2) - numSamplesReady);
146 //WriteLog("DAC: %u samples ready.\n", numSamplesReady);
148 // Actually, it's a bit more involved than this, but this is the general idea:
149 // memcpy(buffer, DACBuffer, length);
150 for(int i=0; i<numSamplesReady; i++)
151 ((uint16 *)buffer)[i] = DACBuffer[(LeftFIFOHeadPtr + i) % BUFFER_SIZE];
152 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
153 // buffer[i] = DACBuffer[(LeftFIFOHeadPtr + i) & (BUFFER_SIZE - 1)];
155 LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
156 RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
157 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
158 // LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
159 // RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
160 //WriteLog(" -> Left/RightFIFOHeadPtr: %04X/%04X, Left/RightFIFOTailPtr: %04X/%04X\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
162 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
164 // WriteLog("DAC: Silence...!\n");
168 // Calculate the frequency of SCLK * 32 using the divider
170 int GetCalculatedFrequency(void)
172 int systemClockFrequency = (vjs.hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
174 // We divide by 32 here in order to find the frequency of 32 SCLKs in a row (transferring
175 // 16 bits of left data + 16 bits of right data = 32 bits, 1 SCLK = 1 bit transferred).
176 return systemClockFrequency / (32 * (2 * (SCLKFrequencyDivider + 1)));
180 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
182 void DACWriteByte(uint32 offset, uint8 data, uint32 who/*= UNKNOWN*/)
184 WriteLog("DAC: %s writing BYTE %02X at %08X\n", whoName[who], data, offset);
185 if (offset == SCLK + 3)
186 DACWriteWord(offset - 3, (uint16)data);
189 void DACWriteWord(uint32 offset, uint16 data, uint32 who/*= UNKNOWN*/)
191 if (offset == LTXD + 2)
193 // Spin until buffer has been drained (for too fast processors!)...
194 //Small problem--if Head == 0 and Tail == buffer end, then this will fail... !!! FIX !!!
196 // Also, we're taking advantage of the fact that the buffer is a multiple of two
199 while (((LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == LeftFIFOHeadPtr)//;
202 //if ((spin & 0x0FFFFFFF) == 0)
203 // WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
205 if (spin == 0xFFFF0000)
207 uint32 ltail = LeftFIFOTailPtr, lhead = LeftFIFOHeadPtr;
208 WriteLog("Tail=%X, Head=%X", ltail, lhead);
210 WriteLog("\nStuck in left DAC spinlock! Aborting!\n");
211 WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
212 WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
213 WriteLog("From while: Tail=%X, Head=%X", (LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1), LeftFIFOHeadPtr);
219 SDL_LockAudio(); // Is it necessary to do this? Mebbe.
220 // We use a circular buffer 'cause it's easy. Note that the callback function
221 // takes care of dumping audio to the soundcard...! Also note that we're writing
222 // the samples in the buffer in an interleaved L/R format.
223 LeftFIFOTailPtr = (LeftFIFOTailPtr + 2) % BUFFER_SIZE;
224 DACBuffer[LeftFIFOTailPtr] = data;
227 else if (offset == RTXD + 2)
230 Here's what's happening now:
232 Stuck in right DAC spinlock!
235 Tail=681, Head=681, BUFFER_SIZE-1=FFFF
236 From while: Tail=683, Head=681
238 ????? What the FUCK ?????
240 & when I uncomment the lines below spin++; it *doesn't* lock here... WTF?????
242 I think it was missing parentheses causing the fuckup... Seems to work now...
244 Except for Super Burnout now...! Aarrrgggghhhhh!
247 Stuck in left DAC spinlock! Aborting!
248 Tail=AC, Head=AE, BUFFER_SIZE-1=FFFF
249 From while: Tail=AE, Head=AE
251 So it's *really* stuck here in the left FIFO. Figure out why!!!
253 Prolly 'cause it doesn't set the sample rate right away--betcha it works with the BIOS...
254 It gets farther, but then locks here (weird!):
257 Stuck in left DAC spinlock! Aborting!
258 Tail=2564, Head=2566, BUFFER_SIZE-1=FFFF
259 From while: Tail=2566, Head=2566
261 Weird--recompile with more WriteLog() entries and it *doesn't* lock...
262 Yeah, because there was no DSP running. Duh!
265 Stuck in left DAC spinlock! Aborting!
266 LTail=AC, LHead=AE, BUFFER_SIZE-1=FFFF
267 RTail=AF, RHead=AF, BUFFER_SIZE-1=FFFF
268 From while: Tail=AE, Head=AE
270 Odd: The right FIFO is empty, but the left FIFO is full!
271 And this is what is causing the lockup--the DAC callback waits for the side with
272 less samples ready and in this case it's the right channel (that never fills up)
273 that it's waiting for...!
275 Okay, with the kludge in place for the right channel not being filled, we select
276 a track and then it locks here:
279 Stuck in left DAC spinlock! Aborting!
280 LTail=60D8, LHead=60D8, BUFFER_SIZE-1=FFFF
281 RTail=DB, RHead=60D9, BUFFER_SIZE-1=FFFF
282 From while: Tail=60DA, Head=60D8
284 // Spin until buffer has been drained (for too fast processors!)...
286 while (((RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == RightFIFOHeadPtr)//;
289 //if ((spin & 0x0FFFFFFF) == 0)
290 // WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
292 if (spin == 0xFFFF0000)
294 uint32 rtail = RightFIFOTailPtr, rhead = RightFIFOHeadPtr;
295 WriteLog("Tail=%X, Head=%X", rtail, rhead);
297 WriteLog("\nStuck in right DAC spinlock! Aborting!\n");
298 WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
299 WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
300 WriteLog("From while: Tail=%X, Head=%X", (RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1), RightFIFOHeadPtr);
307 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
308 DACBuffer[RightFIFOTailPtr] = data;
312 WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
315 else if (offset == SCLK + 2) // Sample rate
317 WriteLog("DAC: Writing %u to SCLK...\n", data);
318 if ((uint8)data != SCLKFrequencyDivider)
320 SCLKFrequencyDivider = (uint8)data;
321 //Of course a better way would be to query the hardware to find the upper limit...
322 if (data > 7) // Anything less than 8 is too high!
324 if (SDLSoundInitialized)
327 desired.freq = GetCalculatedFrequency();// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
328 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
330 if (SDLSoundInitialized)
332 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
334 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
342 if (SDLSoundInitialized)
343 SDL_PauseAudio(false); // Start playback!
347 else if (offset == SMODE + 2)
350 WriteLog("DAC: %s writing to SMODE. Bits: %s%s%s%s%s%s [68K PC=%08X]\n", whoName[who],
351 (data & 0x01 ? "INTERNAL " : ""), (data & 0x02 ? "MODE " : ""),
352 (data & 0x04 ? "WSEN " : ""), (data & 0x08 ? "RISING " : ""),
353 (data & 0x10 ? "FALLING " : ""), (data & 0x20 ? "EVERYWORD" : ""),
354 m68k_get_reg(NULL, M68K_REG_PC));
359 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
361 uint8 DACReadByte(uint32 offset, uint32 who/*= UNKNOWN*/)
363 // WriteLog("DAC: %s reading byte from %08X\n", whoName[who], offset);
367 //static uint16 fakeWord = 0;
368 uint16 DACReadWord(uint32 offset, uint32 who/*= UNKNOWN*/)
370 // WriteLog("DAC: %s reading word from %08X\n", whoName[who], offset);
372 // WriteLog("DAC: %s reading WORD %04X from %08X\n", whoName[who], fakeWord, offset);
373 // return fakeWord++;
374 //NOTE: This only works if a bunch of things are set in BUTCH which we currently don't
375 // check for. !!! FIX !!!
376 // Partially fixed: We check for I2SCNTRL in the JERRY I2S routine...
377 // return GetWordFromButchSSI(offset, who);
378 if (offset == LRXD || offset == RRXD)
380 else if (offset == LRXD + 2)
382 else if (offset == RRXD + 2)
385 return 0xFFFF; // May need SSTAT as well... (but may be a Jaguar II only feature)