]> Shamusworld >> Repos - virtualjaguar/blob - src/dac.cpp
4b4bfab2e8b2254dbd4a9fac852e902a530b5f7c
[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 #include "dac.h"
15
16 #include "SDL.h"
17 #include "m68k.h"
18 #include "jaguar.h"
19 #include "settings.h"
20 #include "log.h"
21
22 //#define DEBUG_DAC
23
24 #define BUFFER_SIZE             0x10000                                         // Make the DAC buffers 64K x 16 bits
25
26 // Jaguar memory locations
27
28 #define LTXD                    0xF1A148
29 #define RTXD                    0xF1A14C
30 #define LRXD                    0xF1A148
31 #define RRXD                    0xF1A14C
32 #define SCLK                    0xF1A150
33 #define SMODE                   0xF1A154
34
35 // Global variables
36
37 uint16 lrxd, rrxd;                                                                      // I2S ports (into Jaguar)
38
39 // Local variables
40
41 static uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
42 static SDL_AudioSpec desired;
43 static bool SDLSoundInitialized = false;
44
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.
47
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;
51
52 // Private function prototypes
53
54 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
55 int GetCalculatedFrequency(void);
56
57 //
58 // Initialize the SDL sound system
59 //
60 void DACInit(void)
61 {
62 //      memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
63 //      DACBuffer = (uint16 *)memory_malloc(BUFFER_SIZE * sizeof(uint16), "DAC buffer");
64
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)...
67         desired.channels = 2;
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;
71
72         if (SDL_OpenAudio(&desired, NULL) < 0)                  // NULL means SDL guarantees what we want
73                 WriteLog("DAC: Failed to initialize SDL sound...\n");
74         else
75         {
76                 SDLSoundInitialized = true;
77                 DACReset();
78                 SDL_PauseAudio(false);                                                  // Start playback!
79                 WriteLog("DAC: Successfully initialized.\n");
80         }
81 }
82
83 //
84 // Reset the sound buffer FIFOs
85 //
86 void DACReset(void)
87 {
88         LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
89 }
90
91 //
92 // Close down the SDL sound subsystem
93 //
94 void DACDone(void)
95 {
96         if (SDLSoundInitialized)
97         {
98                 SDL_PauseAudio(true);
99                 SDL_CloseAudio();
100         }
101
102 //      memory_free(DACBuffer);
103         WriteLog("DAC: Done.\n");
104 }
105
106 //
107 // SDL callback routine to fill audio buffer
108 //
109 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
110 //
111 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
112 {
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)
118         {
119 //WriteLog("DAC: About to write some data!\n");
120                 int numLeftSamplesReady
121                         = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
122                                 - LeftFIFOHeadPtr;
123                 int numRightSamplesReady
124                         = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
125                                 - RightFIFOHeadPtr;
126 //This waits for the slower side to catch up. If writing only one side, then this
127 //causes the buffer not to drain...
128                 int numSamplesReady
129                         = (numLeftSamplesReady < numRightSamplesReady
130                                 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
131
132 //Kludge, until I can figure out WTF is going on WRT Super Burnout.
133 if (numLeftSamplesReady == 0 || numRightSamplesReady == 0)
134         numSamplesReady = numLeftSamplesReady + numRightSamplesReady;
135
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);
139
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;
144 //else
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);
147
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)];
154
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);
161         }
162 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
163 //      else
164 //              WriteLog("DAC: Silence...!\n");
165 }
166
167 //
168 // Calculate the frequency of SCLK * 32 using the divider
169 //
170 int GetCalculatedFrequency(void)
171 {
172         int systemClockFrequency = (vjs.hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
173
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)));
177 }
178
179 //
180 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
181 //
182 void DACWriteByte(uint32 offset, uint8 data, uint32 who/*= UNKNOWN*/)
183 {
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);
187 }
188
189 void DACWriteWord(uint32 offset, uint16 data, uint32 who/*= UNKNOWN*/)
190 {
191         if (offset == LTXD + 2)
192         {
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 !!!
195 //[DONE]
196                 // Also, we're taking advantage of the fact that the buffer is a multiple of two
197                 // in this check...
198 uint32 spin = 0;
199                 while (((LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == LeftFIFOHeadPtr)//;
200                 {
201 spin++;
202 //if ((spin & 0x0FFFFFFF) == 0)
203 //      WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
204
205 if (spin == 0xFFFF0000)
206 {
207 uint32 ltail = LeftFIFOTailPtr, lhead = LeftFIFOHeadPtr;
208 WriteLog("Tail=%X, Head=%X", ltail, lhead);
209
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);
214         log_done();
215         exit(0);
216 }
217                 }//*/
218
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;
225                 SDL_UnlockAudio();
226         }
227         else if (offset == RTXD + 2)
228         {
229 /*
230 Here's what's happening now:
231
232 Stuck in right DAC spinlock!
233 Aborting!
234
235 Tail=681, Head=681, BUFFER_SIZE-1=FFFF
236 From while: Tail=683, Head=681
237
238 ????? What the FUCK ?????
239
240 & when I uncomment the lines below spin++; it *doesn't* lock here... WTF?????
241
242 I think it was missing parentheses causing the fuckup... Seems to work now...
243
244 Except for Super Burnout now...! Aarrrgggghhhhh!
245
246 Tail=AC, Head=AE
247 Stuck in left DAC spinlock! Aborting!
248 Tail=AC, Head=AE, BUFFER_SIZE-1=FFFF
249 From while: Tail=AE, Head=AE
250
251 So it's *really* stuck here in the left FIFO. Figure out why!!!
252
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!):
255
256 Tail=2564, Head=2566
257 Stuck in left DAC spinlock! Aborting!
258 Tail=2564, Head=2566, BUFFER_SIZE-1=FFFF
259 From while: Tail=2566, Head=2566
260
261 Weird--recompile with more WriteLog() entries and it *doesn't* lock...
262 Yeah, because there was no DSP running. Duh!
263
264 Tail=AC, Head=AE
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
269
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...!
274
275 Okay, with the kludge in place for the right channel not being filled, we select
276 a track and then it locks here:
277
278 Tail=60D8, Head=60DA
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
283 */
284                 // Spin until buffer has been drained (for too fast processors!)...
285 uint32 spin = 0;
286                 while (((RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == RightFIFOHeadPtr)//;
287                 {
288 spin++;
289 //if ((spin & 0x0FFFFFFF) == 0)
290 //      WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
291
292 if (spin == 0xFFFF0000)
293 {
294 uint32 rtail = RightFIFOTailPtr, rhead = RightFIFOHeadPtr;
295 WriteLog("Tail=%X, Head=%X", rtail, rhead);
296
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);
301         log_done();
302         exit(0);
303 }
304                 }//*/
305
306                 SDL_LockAudio();
307                 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
308                 DACBuffer[RightFIFOTailPtr] = data;
309                 SDL_UnlockAudio();
310 /*#ifdef DEBUG_DAC
311                 else
312                         WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
313 #endif*/
314         }
315         else if (offset == SCLK + 2)                                    // Sample rate
316         {
317                 WriteLog("DAC: Writing %u to SCLK...\n", data);
318                 if ((uint8)data != SCLKFrequencyDivider)
319                 {
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!
323                         {
324                                 if (SDLSoundInitialized)
325                                         SDL_CloseAudio();
326
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);
329
330                                 if (SDLSoundInitialized)
331                                 {
332                                         if (SDL_OpenAudio(&desired, NULL) < 0)  // NULL means SDL guarantees what we want
333                                         {
334                                                 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
335                                                 log_done();
336                                                 exit(1);
337                                         }
338                                 }
339
340                                 DACReset();
341
342                                 if (SDLSoundInitialized)
343                                         SDL_PauseAudio(false);                  // Start playback!
344                         }
345                 }
346         }
347         else if (offset == SMODE + 2)
348         {
349                 serialMode = data;
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));
355         }
356 }
357
358 //
359 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
360 //
361 uint8 DACReadByte(uint32 offset, uint32 who/*= UNKNOWN*/)
362 {
363 //      WriteLog("DAC: %s reading byte from %08X\n", whoName[who], offset);
364         return 0xFF;
365 }
366
367 //static uint16 fakeWord = 0;
368 uint16 DACReadWord(uint32 offset, uint32 who/*= UNKNOWN*/)
369 {
370 //      WriteLog("DAC: %s reading word from %08X\n", whoName[who], offset);
371 //      return 0xFFFF;
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)
379                 return 0x0000;
380         else if (offset == LRXD + 2)
381                 return lrxd;
382         else if (offset == RRXD + 2)
383                 return rrxd;
384
385         return 0xFFFF;  // May need SSTAT as well... (but may be a Jaguar II only feature)              
386 }