// to prevent things like the DSP filling only one side and such. Do such
// mono modes exist on the Jag? Seems to according to Super Burnout.
+// After testing on a real Jaguar, it seems clear that the I2S interrupt drives
+// the audio subsystem. So while you can drive the audio at a *slower* rate than
+// set by SCLK, you can't drive it any *faster*. Also note, that if the I2S
+// interrupt is not enabled/running on the DSP, then there is no audio. Also,
+// audio can be muted by clearing bit 8 of JOYSTICK (JOY1).
+//
+// Approach: We can run the DSP in the host system's audio IRQ, by running the
+// DSP for the alloted time (depending on the host buffer size & sample rate)
+// by simply reading the L/R_I2S (L/RTXD) registers at regular intervals. We
+// would also have to time the I2S/TIMER0/TIMER1 interrupts in the DSP as well.
+// This way, we can run the host audio IRQ at, say, 48 KHz and not have to care
+// so much about SCLK and running a separate buffer and all the attendant
+// garbage that comes with that awful approach.
+//
+// There would still be potential gotchas, as the SCLK can theoretically drive
+// the I2S at 26590906 / 2 (for SCLK == 0) = 13.3 MHz which corresponds to an
+// audio rate 416 KHz (dividing the I2S rate by 32, for 16-bit stereo). It
+// seems doubtful that anything useful could come of such a high rate, and we
+// can probably safely ignore any such ridiculously high audio rates. It won't
+// sound the same as on a real Jaguar, but who cares? :-)
+
#include "dac.h"
#include "SDL.h"
WriteLog("DAC: Done.\n");
}
+
+// Approach: Run the DSP for however many cycles needed to correspond to whatever sample rate
+// we've set the audio to run at. So, e.g., if we run it at 48 KHz, then we would run the DSP
+// for however much time it takes to fill the buffer. So with a 2K buffer, this would correspond
+// to running the DSP for 0.042666... seconds. At 26590906 Hz, this would correspond to
+// running the DSP for 1134545 cycles. You would then sample the L/RTXD registers every
+// 1134545 / 2048 = 554 cycles to fill the buffer. You would also have to manage interrupt
+// timing as well (generating them at the proper times), but that shouldn't be too difficult...
+// If the DSP isn't running, then fill the buffer with L/RTXD and exit.
+
//
// SDL callback routine to fill audio buffer
//
#include <SDL.h> // Used only for SDL_GetTicks...
#include <stdlib.h>
+#include <time.h>
#include "dac.h"
#include "gpu.h"
#include "jagdasm.h"
case 0x00:
{
#ifdef DSP_DEBUG
- WriteLog("DSP: Writing %08X to DSP_FLAGS by %s (REGPAGE is %s)...\n", data, whoName[who], (dsp_flags & REGPAGE ? "set" : "not set"));
+ WriteLog("DSP: Writing %08X to DSP_FLAGS by %s (REGPAGE is %sset)...\n", data, whoName[who], (dsp_flags & REGPAGE ? "" : "not "));
#endif
// bool IMASKCleared = (dsp_flags & IMASK) && !(data & IMASK);
IMASKCleared = (dsp_flags & IMASK) && !(data & IMASK);
// interrupt, so that's what we check for here. Just know that this approach
// can be easily fooled!
// Note also that if both interrupts are enabled, the I2S freq will win. :-P
+
+// Further Note:
+// The impetus for this "fix" was Cybermorph, which sets the SCLK to 7 which is an
+// audio frequency > 48 KHz. However, it stuffs the L/RTXD registers using TIMER0.
+// So, while this works, it's a by-product of the lame way in which audio is currently
+// handled. Hopefully, once we run the DSP in the host audio IRQ, this problem will
+// go away of its own accord. :-P
+// Or does it? It seems the I2S interrupt isn't on with Cybermorph, so something
+// weird is going on here...
+// Maybe it works like this: It acknowledges the 1st interrupt, but never clears it.
+// So subsequent interrupts come into the chip, but they're never serviced but the
+// I2S subsystem keeps going.
+
if (data & INT_ENA1) // I2S interrupt
{
int freq = GetCalculatedFrequency();
case 0x14:
{
//#ifdef DSP_DEBUG
-WriteLog("Write to DSP CTRL by %s: %08X\n", whoName[who], data);
+WriteLog("Write to DSP CTRL by %s: %08X (DSP PC=$%08X)\n", whoName[who], data, dsp_pc);
//#endif
bool wasRunning = DSP_RUNNING;
// uint32 dsp_was_running = DSP_RUNNING;
dsp_build_branch_condition_table();
DSPReset();
+ srandom(time(NULL)); // For randomizing local RAM
}
void DSPReset(void)
IMASKCleared = false;
FlushDSPPipeline();
dsp_reset_stats();
- memset(dsp_ram_8, 0xFF, 0x2000);
+// memset(dsp_ram_8, 0xFF, 0x2000);
+ // Contents of local RAM are quasi-stable; we simulate this by randomizing RAM contents
+ for(uint32 i=0; i<8192; i+=4)
+ {
+ *((uint32 *)(&dsp_ram_8[i])) = random();
+ }
}
void DSPDumpDisassembly(void)
(j << 2) + 1, dsp_reg_bank_1[(j << 2) + 1],
(j << 2) + 2, dsp_reg_bank_1[(j << 2) + 2],
(j << 2) + 3, dsp_reg_bank_1[(j << 2) + 3]);
-
}
+ WriteLog("\n");
+
static char buffer[512];
j = DSP_WORK_RAM_BASE;
- while (j <= 0xF1BFFF)
+ while (j <= 0xF1CFFF)
{
uint32 oldj = j;
j += dasmjag(JAGUAR_DSP, buffer, j);
//
// EVENT.H: System timing support functionality
//
-// by James L. Hammons
+// by James Hammons
//
#ifndef __EVENT_H__
#undef main
#endif
+//hm. :-/
+// This is stuff we pass into the mainWindow...
+bool noUntunedTankPlease = false;
+
// Here's the main application loop--short and simple...
int main(int argc, char * argv[])
{
// Normally, this would be read in from the settings module... :-P
vjs.hardwareTypeAlpine = false;
+ // This is stuff we pass into the mainWindow...
+// noUntunedTankPlease = false;
if (argc > 1)
{
"there is enough demand for it. :-)\n");
return 0;
}
+
if (strcmp(argv[1], "--yarrr") == 0)
{
printf("\n");
printf("\n");
return 0;
}
+
if ((strcmp(argv[1], "--alpine") == 0) || (strcmp(argv[1], "-a") == 0))
{
printf("Alpine Mode enabled.\n");
vjs.hardwareTypeAlpine = true;
}
+
+ if (strcmp(argv[1], "--please-dont-kill-my-computer") == 0)
+ {
+ noUntunedTankPlease = true;
+ }
}
Q_INIT_RESOURCE(virtualjaguar); // This must the same name as the exe filename
App::App(int argc, char * argv[]): QApplication(argc, argv)
{
mainWindow = new MainWin();
+ mainWindow->plzDontKillMyComputer = noUntunedTankPlease;
mainWindow->show();
}
private:
MainWin * mainWindow;
+// bool noUntunedTankPlease;
// Globally accessible stuff goes here...
// Although... Globally accessible stuff should go into settings.cpp...
MainWin::MainWin(): running(true), powerButtonOn(false), showUntunedTankCircuit(true),
cartridgeLoaded(false), CDActive(false),//, alpineLoadSuccessful(false),
- pauseForFileSelector(false)
+ pauseForFileSelector(false), plzDontKillMyComputer(false)
{
videoWidget = new GLWidget(this);
setCentralWidget(videoWidget);
if (showUntunedTankCircuit)
{
+ // Some machines can't handle this, so we give them the option to disable it. :-)
+ if (!plzDontKillMyComputer)
+ {
// Random hash & trash
// We try to simulate an untuned tank circuit here... :-)
for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
= (rand() & 0xFF) << 8 | (rand() & 0xFF) << 16 | (rand() & 0xFF) << 24;
}
}
+ }
}
else
{
bool CDActive;
// bool alpineLoadSuccessful;
bool pauseForFileSelector;
-
+ public:
+ bool plzDontKillMyComputer;
+ private:
QMenu * fileMenu;
QMenu * helpMenu;
QToolBar * toolbar;
#define OBJECT_TYPE_BRANCH 3 // 011
#define OBJECT_TYPE_STOP 4 // 100
-#define CONDITION_EQUAL 0
-#define CONDITION_LESS_THAN 1
-#define CONDITION_GREATER_THAN 2
+#define CONDITION_EQUAL 0 // VC == YPOS
+#define CONDITION_LESS_THAN 1 // VC < YPOS
+#define CONDITION_GREATER_THAN 2 // VC > YPOS
#define CONDITION_OP_FLAG_SET 3
#define CONDITION_SECOND_HALF_LINE 4