]> Shamusworld >> Repos - virtualjaguar/blob - src/dac.cpp
Renamed clock.cpp/h to event.cpp/h, did some long overdue cleanups to
[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 //
8
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
12 // confused...
13
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?
17
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.
21
22 #include "dac.h"
23
24 #include "SDL.h"
25 #include "gui.h"
26 #include "jaguar.h"
27 #include "log.h"
28 #include "m68k.h"
29 #include "settings.h"
30
31 //#define DEBUG_DAC
32
33 #define BUFFER_SIZE             0x10000                                         // Make the DAC buffers 64K x 16 bits
34
35 // Jaguar memory locations
36
37 #define LTXD                    0xF1A148
38 #define RTXD                    0xF1A14C
39 #define LRXD                    0xF1A148
40 #define RRXD                    0xF1A14C
41 #define SCLK                    0xF1A150
42 #define SMODE                   0xF1A154
43
44 // Global variables
45
46 uint16 lrxd, rrxd;                                                                      // I2S ports (into Jaguar)
47
48 // Local variables
49
50 static uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
51 static SDL_AudioSpec desired;
52 static bool SDLSoundInitialized = false;
53
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.
56
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;
60
61 // Private function prototypes
62
63 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
64 int GetCalculatedFrequency(void);
65
66 //
67 // Initialize the SDL sound system
68 //
69 void DACInit(void)
70 {
71 //      memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
72 //      DACBuffer = (uint16 *)memory_malloc(BUFFER_SIZE * sizeof(uint16), "DAC buffer");
73
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)...
76         desired.channels = 2;
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;
80
81         if (SDL_OpenAudio(&desired, NULL) < 0)                  // NULL means SDL guarantees what we want
82                 WriteLog("DAC: Failed to initialize SDL sound...\n");
83         else
84         {
85                 SDLSoundInitialized = true;
86                 DACReset();
87                 SDL_PauseAudio(false);                                                  // Start playback!
88                 WriteLog("DAC: Successfully initialized.\n");
89         }
90 }
91
92 //
93 // Reset the sound buffer FIFOs
94 //
95 void DACReset(void)
96 {
97         LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
98 }
99
100 //
101 // Close down the SDL sound subsystem
102 //
103 void DACDone(void)
104 {
105         if (SDLSoundInitialized)
106         {
107                 SDL_PauseAudio(true);
108                 SDL_CloseAudio();
109         }
110
111 //      memory_free(DACBuffer);
112         WriteLog("DAC: Done.\n");
113 }
114
115 //
116 // SDL callback routine to fill audio buffer
117 //
118 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
119 //
120 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
121 {
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)
127         {
128 //WriteLog("DAC: About to write some data!\n");
129                 int numLeftSamplesReady
130                         = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
131                                 - LeftFIFOHeadPtr;
132                 int numRightSamplesReady
133                         = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
134                                 - RightFIFOHeadPtr;
135 //This waits for the slower side to catch up. If writing only one side, then this
136 //causes the buffer not to drain...
137                 int numSamplesReady
138                         = (numLeftSamplesReady < numRightSamplesReady
139                                 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
140
141 //Kludge, until I can figure out WTF is going on WRT Super Burnout.
142 if (numLeftSamplesReady == 0 || numRightSamplesReady == 0)
143         numSamplesReady = numLeftSamplesReady + numRightSamplesReady;
144
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);
148
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;
153 //else
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);
156
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)];
163
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);
170         }
171 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
172 //      else
173 //              WriteLog("DAC: Silence...!\n");
174 }
175
176 //
177 // Calculate the frequency of SCLK * 32 using the divider
178 //
179 int GetCalculatedFrequency(void)
180 {
181         int systemClockFrequency = (vjs.hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
182
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)));
186 }
187
188 //
189 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
190 //
191 void DACWriteByte(uint32 offset, uint8 data, uint32 who/*= UNKNOWN*/)
192 {
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);
196 }
197
198 void DACWriteWord(uint32 offset, uint16 data, uint32 who/*= UNKNOWN*/)
199 {
200         if (offset == LTXD + 2)
201         {
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 !!!
204 //[DONE]
205                 // Also, we're taking advantage of the fact that the buffer is a multiple of two
206                 // in this check...
207 uint32 spin = 0;
208                 while (((LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == LeftFIFOHeadPtr)//;
209                 {
210 spin++;
211 //if ((spin & 0x0FFFFFFF) == 0)
212 //      WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
213
214 if (spin == 0xFFFF0000)
215 {
216 uint32 ltail = LeftFIFOTailPtr, lhead = LeftFIFOHeadPtr;
217 WriteLog("Tail=%X, Head=%X", ltail, lhead);
218
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);
223 //      LogDone();
224 //      exit(0);
225         GUICrashGracefully("Stuck in left DAC spinlock!");
226         return;
227 }
228                 }//*/
229
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;
236                 SDL_UnlockAudio();
237         }
238         else if (offset == RTXD + 2)
239         {
240 /*
241 Here's what's happening now:
242
243 Stuck in right DAC spinlock!
244 Aborting!
245
246 Tail=681, Head=681, BUFFER_SIZE-1=FFFF
247 From while: Tail=683, Head=681
248
249 ????? What the FUCK ?????
250
251 & when I uncomment the lines below spin++; it *doesn't* lock here... WTF?????
252
253 I think it was missing parentheses causing the fuckup... Seems to work now...
254
255 Except for Super Burnout now...! Aarrrgggghhhhh!
256
257 Tail=AC, Head=AE
258 Stuck in left DAC spinlock! Aborting!
259 Tail=AC, Head=AE, BUFFER_SIZE-1=FFFF
260 From while: Tail=AE, Head=AE
261
262 So it's *really* stuck here in the left FIFO. Figure out why!!!
263
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!):
266
267 Tail=2564, Head=2566
268 Stuck in left DAC spinlock! Aborting!
269 Tail=2564, Head=2566, BUFFER_SIZE-1=FFFF
270 From while: Tail=2566, Head=2566
271
272 Weird--recompile with more WriteLog() entries and it *doesn't* lock...
273 Yeah, because there was no DSP running. Duh!
274
275 Tail=AC, Head=AE
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
280
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...!
285
286 Okay, with the kludge in place for the right channel not being filled, we select
287 a track and then it locks here:
288
289 Tail=60D8, Head=60DA
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
294 */
295                 // Spin until buffer has been drained (for too fast processors!)...
296 uint32 spin = 0;
297                 while (((RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == RightFIFOHeadPtr)//;
298                 {
299 spin++;
300 //if ((spin & 0x0FFFFFFF) == 0)
301 //      WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
302
303 if (spin == 0xFFFF0000)
304 {
305 uint32 rtail = RightFIFOTailPtr, rhead = RightFIFOHeadPtr;
306 WriteLog("Tail=%X, Head=%X", rtail, rhead);
307
308         WriteLog("\nStuck in right DAC spinlock! Aborting!\n");
309         WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
310         WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
311         WriteLog("From while: Tail=%X, Head=%X", (RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1), RightFIFOHeadPtr);
312 //      LogDone();
313 //      exit(0);
314         GUICrashGracefully("Stuck in right DAC spinlock!");
315         return;
316 }
317                 }//*/
318
319                 SDL_LockAudio();
320                 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
321                 DACBuffer[RightFIFOTailPtr] = data;
322                 SDL_UnlockAudio();
323 /*#ifdef DEBUG_DAC
324                 else
325                         WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
326 #endif*/
327         }
328         else if (offset == SCLK + 2)                                    // Sample rate
329         {
330                 WriteLog("DAC: Writing %u to SCLK...\n", data);
331                 if ((uint8)data != SCLKFrequencyDivider)
332                 {
333                         SCLKFrequencyDivider = (uint8)data;
334 //Of course a better way would be to query the hardware to find the upper limit...
335                         if (data > 7)   // Anything less than 8 is too high!
336                         {
337                                 if (SDLSoundInitialized)
338                                         SDL_CloseAudio();
339
340                                 desired.freq = GetCalculatedFrequency();// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
341                                 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
342
343                                 if (SDLSoundInitialized)
344                                 {
345                                         if (SDL_OpenAudio(&desired, NULL) < 0)  // NULL means SDL guarantees what we want
346                                         {
347 // This is bad, Bad, BAD !!! DON'T ABORT BECAUSE WE DIDN'T GET OUR FREQ! !!! FIX !!!
348 #warning !!! FIX !!! Aborting because of SDL audio problem is bad!
349                                                 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
350 //                                              LogDone();
351 //                                              exit(1);
352                                                 GUICrashGracefully("Failed to initialize SDL sound!");
353                                                 return;
354                                         }
355                                 }
356
357                                 DACReset();
358
359                                 if (SDLSoundInitialized)
360                                         SDL_PauseAudio(false);                  // Start playback!
361                         }
362                 }
363         }
364         else if (offset == SMODE + 2)
365         {
366                 serialMode = data;
367                 WriteLog("DAC: %s writing to SMODE. Bits: %s%s%s%s%s%s [68K PC=%08X]\n", whoName[who],
368                         (data & 0x01 ? "INTERNAL " : ""), (data & 0x02 ? "MODE " : ""),
369                         (data & 0x04 ? "WSEN " : ""), (data & 0x08 ? "RISING " : ""),
370                         (data & 0x10 ? "FALLING " : ""), (data & 0x20 ? "EVERYWORD" : ""),
371                         m68k_get_reg(NULL, M68K_REG_PC));
372         }
373 }
374
375 //
376 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
377 //
378 uint8 DACReadByte(uint32 offset, uint32 who/*= UNKNOWN*/)
379 {
380 //      WriteLog("DAC: %s reading byte from %08X\n", whoName[who], offset);
381         return 0xFF;
382 }
383
384 //static uint16 fakeWord = 0;
385 uint16 DACReadWord(uint32 offset, uint32 who/*= UNKNOWN*/)
386 {
387 //      WriteLog("DAC: %s reading word from %08X\n", whoName[who], offset);
388 //      return 0xFFFF;
389 //      WriteLog("DAC: %s reading WORD %04X from %08X\n", whoName[who], fakeWord, offset);
390 //      return fakeWord++;
391 //NOTE: This only works if a bunch of things are set in BUTCH which we currently don't
392 //      check for. !!! FIX !!!
393 // Partially fixed: We check for I2SCNTRL in the JERRY I2S routine...
394 //      return GetWordFromButchSSI(offset, who);
395         if (offset == LRXD || offset == RRXD)
396                 return 0x0000;
397         else if (offset == LRXD + 2)
398                 return lrxd;
399         else if (offset == RRXD + 2)
400                 return rrxd;
401
402         return 0xFFFF;  // May need SSTAT as well... (but may be a Jaguar II only feature)
403 }