From d9abe1a157bbd35e8b282927489b687f56048ce6 Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Sun, 8 Apr 2012 16:26:53 +0000 Subject: [PATCH] Added switch to disable untuned tank circuit, for slower computers. --- src/dac.cpp | 31 +++++++++++++++++++++++++++++++ src/dsp.cpp | 31 ++++++++++++++++++++++++++----- src/event.h | 2 +- src/gui/app.cpp | 14 ++++++++++++++ src/gui/app.h | 1 + src/gui/mainwin.cpp | 6 +++++- src/gui/mainwin.h | 4 +++- src/op.cpp | 6 +++--- 8 files changed, 84 insertions(+), 11 deletions(-) diff --git a/src/dac.cpp b/src/dac.cpp index 617918a..9836807 100644 --- a/src/dac.cpp +++ b/src/dac.cpp @@ -26,6 +26,27 @@ // 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" @@ -127,6 +148,16 @@ void DACDone(void) 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 // diff --git a/src/dsp.cpp b/src/dsp.cpp index aa12cf5..a620775 100644 --- a/src/dsp.cpp +++ b/src/dsp.cpp @@ -18,6 +18,7 @@ #include // Used only for SDL_GetTicks... #include +#include #include "dac.h" #include "gpu.h" #include "jagdasm.h" @@ -779,7 +780,7 @@ SET32(ram2, offset, data); 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); @@ -797,6 +798,19 @@ SET32(ram2, offset, data); // 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(); @@ -872,7 +886,7 @@ if (who != DSP) 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; @@ -1271,6 +1285,7 @@ void DSPInit(void) dsp_build_branch_condition_table(); DSPReset(); + srandom(time(NULL)); // For randomizing local RAM } void DSPReset(void) @@ -1297,7 +1312,12 @@ 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) @@ -1377,13 +1397,14 @@ void DSPDone(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); diff --git a/src/event.h b/src/event.h index 9014dfb..5565bec 100644 --- a/src/event.h +++ b/src/event.h @@ -1,7 +1,7 @@ // // EVENT.H: System timing support functionality // -// by James L. Hammons +// by James Hammons // #ifndef __EVENT_H__ diff --git a/src/gui/app.cpp b/src/gui/app.cpp index 7c2037f..6abfe15 100644 --- a/src/gui/app.cpp +++ b/src/gui/app.cpp @@ -27,11 +27,17 @@ #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) { @@ -44,6 +50,7 @@ int main(int argc, char * argv[]) "there is enough demand for it. :-)\n"); return 0; } + if (strcmp(argv[1], "--yarrr") == 0) { printf("\n"); @@ -51,11 +58,17 @@ int main(int argc, char * argv[]) 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 @@ -94,5 +107,6 @@ int main(int argc, char * argv[]) App::App(int argc, char * argv[]): QApplication(argc, argv) { mainWindow = new MainWin(); + mainWindow->plzDontKillMyComputer = noUntunedTankPlease; mainWindow->show(); } diff --git a/src/gui/app.h b/src/gui/app.h index 2b57845..9de170a 100644 --- a/src/gui/app.h +++ b/src/gui/app.h @@ -21,6 +21,7 @@ class App: public QApplication private: MainWin * mainWindow; +// bool noUntunedTankPlease; // Globally accessible stuff goes here... // Although... Globally accessible stuff should go into settings.cpp... diff --git a/src/gui/mainwin.cpp b/src/gui/mainwin.cpp index 56299f3..ba0b21e 100644 --- a/src/gui/mainwin.cpp +++ b/src/gui/mainwin.cpp @@ -72,7 +72,7 @@ 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); @@ -442,6 +442,9 @@ void MainWin::Timer(void) 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; xrasterWidth; x++) @@ -452,6 +455,7 @@ void MainWin::Timer(void) = (rand() & 0xFF) << 8 | (rand() & 0xFF) << 16 | (rand() & 0xFF) << 24; } } + } } else { diff --git a/src/gui/mainwin.h b/src/gui/mainwin.h index 30d9eb2..37902b2 100644 --- a/src/gui/mainwin.h +++ b/src/gui/mainwin.h @@ -71,7 +71,9 @@ class MainWin: public QMainWindow bool CDActive; // bool alpineLoadSuccessful; bool pauseForFileSelector; - + public: + bool plzDontKillMyComputer; + private: QMenu * fileMenu; QMenu * helpMenu; QToolBar * toolbar; diff --git a/src/op.cpp b/src/op.cpp index 682c023..f9264c5 100644 --- a/src/op.cpp +++ b/src/op.cpp @@ -36,9 +36,9 @@ #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 -- 2.37.2