2 // DAC (really, Synchronous Serial Interface) Handler
5 // GCC/SDL port by Niels Wagenaar (Linux/WIN32) and Caz (BeOS)
6 // Rewritten by James L. Hammons
13 #define BUFFER_SIZE 0x8000 // Make the DAC buffers 32K x 16 bits
15 // Jaguar memory locations
20 #define SMODE 0xF1A154
24 uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
25 SDL_AudioSpec desired;
27 // We can get away with using native endian here because we can tell SDL to use the native
28 // endian when looking at the sample buffer, i.e., no need to worry about it.
31 uint8 SCLKFrequencyDivider = 19; // Default is roughly 22 KHz (20774 Hz in NTSC mode)
32 uint16 serialMode = 0;
34 // Private function prototypes
36 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
37 int GetCalculatedFrequency(void);
40 // Initialize the SDL sound system (?) (!)
44 memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
46 desired.freq = GetCalculatedFrequency(); // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
47 desired.format = AUDIO_S16SYS; // This uses the native endian (for portability)...
49 desired.samples = 4096; // Let's try a 4K buffer (can always go lower)
50 desired.callback = SDLSoundCallback;
52 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
54 WriteLog("DAC: Failed to initialize SDL sound. Shutting down!\n");
60 SDL_PauseAudio(false); // Start playback!
61 WriteLog("DAC: Successfully initialized.\n");
65 // Reset the sound buffer FIFOs
69 LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
73 // Close down the SDL sound subsystem (?) (!)
79 WriteLog("DAC: Done.\n");
83 // SDL callback routine to fill audio buffer
85 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
87 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
89 // Clear the buffer to silence, in case the DAC buffer is empty (or short)
90 memset(buffer, desired.silence, length);
91 //WriteLog("DAC: Inside callback...\n");
92 if (LeftFIFOHeadPtr != LeftFIFOTailPtr)
94 //WriteLog("DAC: About to write some data!\n");
95 int numLeftSamplesReady
96 = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
98 int numRightSamplesReady
99 = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
102 = (numLeftSamplesReady < numRightSamplesReady
103 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
105 //The numbers look good--it's just that the DSP can't get enough samples in the DAC buffer!
106 //WriteLog("DAC: Left/RightFIFOHeadPtr: %u/%u, Left/RightFIFOTailPtr: %u/%u\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
107 //WriteLog(" numLeft/RightSamplesReady: %i/%i, numSamplesReady: %i, length of buffer: %i\n", numLeftSamplesReady, numRightSamplesReady, numSamplesReady, length);
109 /* if (numSamplesReady > length)
110 numSamplesReady = length;//*/
111 if (numSamplesReady > length / 2) // length / 2 because we're comparing 16-bit lengths
112 numSamplesReady = length / 2;
114 // WriteLog(" Not enough samples to fill the buffer (short by %u L/R samples)...\n", (length / 2) - numSamplesReady);
115 //WriteLog("DAC: %u samples ready.\n", numSamplesReady);
117 // Actually, it's a bit more involved than this, but this is the general idea:
118 // memcpy(buffer, DACBuffer, length);
119 for(int i=0; i<numSamplesReady; i++)
120 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
121 ((uint16 *)buffer)[i] = DACBuffer[(LeftFIFOHeadPtr + i) % BUFFER_SIZE];
122 // buffer[i] = DACBuffer[(LeftFIFOHeadPtr + i) & (BUFFER_SIZE - 1)];
124 LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
125 RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
126 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
127 // LeftFIFOHeadPtr = (LeftFIFOHeadPtr + (numSamplesReady)) & (BUFFER_SIZE - 1);
128 // RightFIFOHeadPtr = (RightFIFOHeadPtr + (numSamplesReady)) & (BUFFER_SIZE - 1);
129 //WriteLog(" -> Left/RightFIFOHeadPtr: %u/%u, Left/RightFIFOTailPtr: %u/%u\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
131 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
133 // WriteLog("DAC: Silence...!\n");
137 // Calculate the frequency of SCLK * 32 using the divider
139 int GetCalculatedFrequency(void)
141 extern bool hardwareTypeNTSC;
142 int systemClockFrequency = (hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
144 // We divide by 32 here in order to find the frequency of 32 SCLKs in a row (transferring
145 // 16 bits of left data + 16 bits of right data = 32 bits, 1 SCLK = 1 bit transferred).
146 return systemClockFrequency / (32 * (2 * (SCLKFrequencyDivider + 1)));
150 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
152 void DACWriteByte(uint32 offset, uint8 data)
154 WriteLog("DAC: Writing %02X at %08X\n", data, offset);
155 if (offset == SCLK + 3)
156 DACWriteWord(offset - 3, (uint16)data);
159 void DACWriteWord(uint32 offset, uint16 data)
161 if (offset == LTXD + 2)
163 if (LeftFIFOTailPtr + 2 != LeftFIFOHeadPtr)
165 SDL_LockAudio(); // Is it necessary to do this? Mebbe.
166 // We use a circular buffer 'cause it's easy. Note that the callback function
167 // takes care of dumping audio to the soundcard...! Also note that we're writing
168 // the samples in the buffer in an interleaved L/R format.
169 LeftFIFOTailPtr = (LeftFIFOTailPtr + 2) % BUFFER_SIZE;
170 DACBuffer[LeftFIFOTailPtr] = data;
171 // Aaron's code does this, but I don't know why...
172 //Flipping this bit makes the audio MUCH louder. Need to look at the amplitude of the
173 //waveform to see if any massaging is needed here...
174 //Looks like a cheap & dirty way to convert signed samples to unsigned...
175 // DACBuffer[LeftFIFOTailPtr] = data ^ 0x8000;
179 WriteLog("DAC: Ran into FIFO's left tail pointer!\n");
181 else if (offset == RTXD + 2)
183 if (RightFIFOTailPtr + 2 != RightFIFOHeadPtr)
186 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
187 DACBuffer[RightFIFOTailPtr] = data;
188 // Aaron's code does this, but I don't know why...
189 // DACBuffer[RightFIFOTailPtr] = data ^ 0x8000;
193 WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
195 else if (offset == SCLK + 2) // Sample rate
197 WriteLog("DAC: Writing %u to SCLK...\n", data);
198 if ((uint8)data != SCLKFrequencyDivider)
200 SCLKFrequencyDivider = (uint8)data;
201 //Of course a better way would be to query the hardware to find the upper limit...
202 if (data > 7) // Anything less is too high!
205 desired.freq = GetCalculatedFrequency();// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
206 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
208 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
210 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
216 SDL_PauseAudio(false); // Start playback!
220 else if (offset == SMODE + 2)
223 WriteLog("DAC: Writing to SMODE. Bits: %s%s%s%s%s%s\n",
224 (data & 0x01 ? "INTERNAL " : ""), (data & 0x02 ? "MODE " : ""),
225 (data & 0x04 ? "WSEN " : ""), (data & 0x08 ? "RISING " : ""),
226 (data & 0x10 ? "FALLING " : ""), (data & 0x20 ? "EVERYWORD" : ""));
231 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
233 uint8 DACReadByte(uint32 offset)
235 // WriteLog("DAC: Reading byte from %08X\n", offset);
239 uint16 DACReadWord(uint32 offset)
241 // WriteLog("DAC: Reading word from %08X\n", offset);