From b8d83843b72678b7786f45559b4b2a4b7f03eb72 Mon Sep 17 00:00:00 2001 From: Jorj Bauer Date: Sat, 16 Jun 2018 11:41:18 -0400 Subject: [PATCH] minor improvements on the debugger interface for the SDL build --- nix/debugger.cpp | 83 +++++++++++++------ nix/debugger.h | 3 + sdl/aiie.cpp | 206 ++++++++++++++++++----------------------------- 3 files changed, 143 insertions(+), 149 deletions(-) diff --git a/nix/debugger.cpp b/nix/debugger.cpp index 7de43de..43ddabb 100644 --- a/nix/debugger.cpp +++ b/nix/debugger.cpp @@ -40,6 +40,7 @@ Debugger::Debugger() sd = socket(AF_INET, SOCK_STREAM, 0); cd = -1; + breakpoint = 0; optval=1; setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, @@ -72,29 +73,6 @@ void Debugger::step() static char buf[256]; if (cd != -1) { - bzero(buf,256); - int n = read( cd,buf,255 ); - - if (n < 0) { - // error - close(cd); - cd = -1; - return; - } - - if (n > 0) { - if (buf[0] == 'c') { - // Continue - close connection - close(cd); - return; - } - // ... ? - // b - set breakpoint - // s - step over - // S - step out - // c - continue (close connection) - // d - disassemble @ current PC - } // Print the status back out the socket uint8_t p = g_cpu->flags; @@ -124,11 +102,70 @@ void Debugger::step() buf[1] = 10; write(cd, buf, 2); } + + + if (breakpoint && g_cpu->pc != breakpoint) { + // Running until we reach the breakpoint + return; + } + + bzero(buf,256); + int n = read( cd,buf,255 ); + + if (n < 0) { + // error + close(cd); + cd = -1; + return; + } + + if (n > 0) { + if (buf[0] == 'c') { + // Continue - close connection + close(cd); + return; + } + if (buf[0] == 'b') { + // FIXME: set breakpoint + if (buf[1] == ' ' && + buf[2] == '0' && + buf[3] == 'x') { + // Hex + breakpoint = strtol(&buf[4], NULL, 16); + } else if (buf[1] == ' ' && + buf[2] == '$') { + // Also hex + breakpoint = strtol(&buf[3], NULL, 16); + } else if (sscanf(buf, "b %d", &breakpoint) == 1) { + // decimal + } else { + breakpoint = 0; + } + if (breakpoint) { + snprintf(buf, sizeof(buf), "Breakpoint set to 0x%X\012\015", breakpoint); + write(cd, buf, strlen(buf)); + } + } + + // ... ? + // b - set breakpoint + // s - step over + // S - step out + // c - continue (close connection) + // d - disassemble @ current PC + } + } } void Debugger::setSocket(int fd) { + printf("New debugger session established\n"); cd = fd; } + +bool Debugger::active() +{ + return (cd != -1); +} diff --git a/nix/debugger.h b/nix/debugger.h index 207e8a5..05d7bc7 100644 --- a/nix/debugger.h +++ b/nix/debugger.h @@ -2,6 +2,7 @@ #define __DEBUGGER_H #include +#include class Debugger { public: @@ -10,12 +11,14 @@ class Debugger { void setSocket(int cliSock); void step(); + bool active(); // private: int sd; // server (listener) int cd; // client (connected to us) pthread_t listenThreadID; + uint32_t breakpoint; }; diff --git a/sdl/aiie.cpp b/sdl/aiie.cpp index 16a62c0..b92130e 100644 --- a/sdl/aiie.cpp +++ b/sdl/aiie.cpp @@ -22,7 +22,6 @@ //#define SHOWFPS //#define SHOWPC -//#define DEBUGCPU //#define SHOWMEMPAGE BIOS bios; @@ -51,7 +50,8 @@ void writePrefs(); void sigint_handler(int n) { - send_rst = 1; + // If we want control-C to reset the machine, then set this here... + // send_rst = 1; } void nonblock(int state) @@ -91,7 +91,7 @@ void write(void *arg, uint16_t address, uint8_t v) static void *cpu_thread(void *dummyptr) { struct timespec currentTime; - struct timespec nextCycleTime; + struct timespec nextSpeakerCycleTime; uint32_t nextSpeakerCycle = 0; #if 0 @@ -108,6 +108,12 @@ static void *cpu_thread(void *dummyptr) { do_gettime(&nextInstructionTime); printf("free-running\n"); + + // In this loop, we determine when the next CPU or Speaker event is; + // sleep until that event; and then perform the event. There are a + // number of maintenance tasks that also happen to be sure that + // peripherals are updated appropriately. + while (1) { if (g_biosInterrupt) { printf("BIOS blocking\n"); @@ -134,117 +140,82 @@ static void *cpu_thread(void *dummyptr) { do_gettime(¤tTime); - /* The speaker is our priority. The CPU runs in batches anyway, - sometimes a little behind and sometimes a little ahead; but the - speaker has to be right on time. */ + // FIXME: these first two can go in their respective loops after execution - // Wait until nextSpeakerCycle - timespec_add_cycles(&startTime, nextSpeakerCycle, &nextCycleTime); + // Determine the next speaker runtime (nextSpeakerCycle). + // The speaker runs 48 cycles behind the CPU (an arbitrary number). + timespec_add_cycles(&startTime, nextSpeakerCycle-48, &nextSpeakerCycleTime); - struct timespec diff = tsSubtract(nextCycleTime, currentTime); - if (diff.tv_sec >= 0 || diff.tv_nsec >= 0) { - nanosleep(&diff, NULL); + // Determine the next CPU runtime (nextInstructionTime) + timespec_add_cycles(&startTime, g_cpu->cycles, &nextInstructionTime); + + // Sleep until one of them is ready to run. + + // tsSubtract doesn't return negatives; it bounds at zero. So if + // either result is zero then it's time to run something. + + struct timespec cpudiff = tsSubtract(nextInstructionTime, currentTime); + struct timespec spkrdiff = tsSubtract(nextSpeakerCycleTime, currentTime); + + struct timespec mindiff; + if (cpudiff.tv_sec < spkrdiff.tv_sec) { + memcpy(&mindiff, &cpudiff, sizeof(struct timespec)); + } else if (spkrdiff.tv_sec < cpudiff.tv_sec) { + memcpy(&mindiff, &spkrdiff, sizeof(struct timespec)); + } else if (cpudiff.tv_nsec < spkrdiff.tv_nsec) { + memcpy(&mindiff, &cpudiff, sizeof(struct timespec)); + } else { + memcpy(&mindiff, &spkrdiff, sizeof(struct timespec)); + } + + if (mindiff.tv_sec > 0 || mindiff.tv_nsec > 0) { + // Sleep until the first of them is ready & loop... + nanosleep(&mindiff, NULL); + continue; } - // Speaker runs 48 cycles behind the CPU (an arbitrary number) - if (nextSpeakerCycle >= 48) { - timespec_add_cycles(&startTime, nextSpeakerCycle-48, &nextCycleTime); - uint64_t microseconds = nextCycleTime.tv_sec * 1000000 + - (double)nextCycleTime.tv_nsec / 1000.0; + // Now we know either the speaker or the CPU is ready to + // run. Figure out which and run it. + + if (spkrdiff.tv_sec == 0 && spkrdiff.tv_nsec == 0) { + // Run the speaker + + uint64_t microseconds = nextSpeakerCycleTime.tv_sec * 1000000 + + (double)nextSpeakerCycleTime.tv_nsec / 1000.0; g_speaker->maintainSpeaker(nextSpeakerCycle-48, microseconds); + + nextSpeakerCycle++; } - // Bump speaker cycle for next go-round - nextSpeakerCycle++; + if (cpudiff.tv_sec == 0 && cpudiff.tv_nsec == 0) { + // Run the CPU - - /* Next up is the CPU. */ - - // tsSubtract doesn't return negatives; it bounds at 0. - diff = tsSubtract(nextInstructionTime, currentTime); - - uint8_t executed = 0; - if (diff.tv_sec == 0 && diff.tv_nsec == 0) { -#ifdef DEBUGCPU - executed = g_cpu->Run(1); -#else - executed = g_cpu->Run(24); -#endif - // calculate the real time that we should be at now, and schedule - // that as our next instruction time - timespec_add_cycles(&startTime, g_cpu->cycles, &nextInstructionTime); + uint8_t executed = 0; + if (debugger.active()) { + // With the debugger running, we need to single-step through + // instructions. + executed = g_cpu->Run(1); + } else { + // Otherwise we can run a bunch of instructions at once to + // save on the overhead. + executed = g_cpu->Run(24); + } // The paddles need to be triggered in real-time on the CPU // clock. That happens from the VM's CPU maintenance poller. ((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles); -#ifdef DEBUGCPU - debugger.step(); -#endif - + if (debugger.active()) { + debugger.step(); + } + if (send_rst) { cpuDebuggerRunning = true; - -#if 0 - printf("Scheduling suspend request...\n"); - wantSuspend = true; -#endif -#if 0 - printf("Scheduling resume resume request...\n"); - wantResume = true; -#endif -#if 0 printf("Sending reset\n"); g_cpu->Reset(); - // testing startup keyboard presses - perform Apple //e self-test - //g_vm->getKeyboard()->keyDepressed(RA); - //g_vm->Reset(); - //g_cpu->Reset(); - //((AppleVM *)g_vm)->insertDisk(0, "disks/DIAGS.DSK"); -#endif - -#if 0 - // Swap disks - if (disk1name[0] && disk2name[0]) { - printf("Swapping disks\n"); - - printf("Inserting disk %s in drive 1\n", disk2name); - ((AppleVM *)g_vm)->insertDisk(0, disk2name); - printf("Inserting disk %s in drive 2\n", disk1name); - ((AppleVM *)g_vm)->insertDisk(1, disk1name); - } -#endif - -#if 0 - MMU *mmu = g_vm->getMMU(); - - printf("PC: 0x%X\n", g_cpu->pc); - for (int i=g_cpu->pc; ipc + 0x100; i++) { - printf("0x%X ", mmu->read(i)); - } - printf("\n"); - - printf("Dropping to monitor\n"); - // drop directly to monitor. - g_cpu->pc = 0xff69; // "call -151" - mmu->read(0xC054); // make sure we're in page 1 - mmu->read(0xC056); // and that hires is off - mmu->read(0xC051); // and text mode is on - mmu->read(0xC08A); // and we have proper rom in place - mmu->read(0xc008); // main zero-page - mmu->read(0xc006); // rom from cards - mmu->write(0xc002 + mmu->read(0xc014)? 1 : 0, 0xff); // make sure aux ram read and write match - mmu->write(0x20, 0); // text window - mmu->write(0x21, 40); - mmu->write(0x22, 0); - mmu->write(0x23, 24); - mmu->write(0x33, '>'); - mmu->write(0x48, 0); // from 0xfb2f: part of text init -#endif - - send_rst = 0; + send_rst = 0; } } } @@ -252,34 +223,6 @@ static void *cpu_thread(void *dummyptr) { int main(int argc, char *argv[]) { -#if 0 - // Timing consistency check - - sleep(2); // kinda random, hopefully sloppy? - to make startTime != 0,0 - printf("starting time consistency check\n"); - do_gettime(&startTime); - for (int i=0; i<10000000; i++) { - - // Calculate the time delta from startTime to cycle # i - timespec_add_cycles(&startTime, i, &nextInstructionTime); - - // Recalculate the time difference between nextInstructionTime and startTime - struct timespec runtime = tsSubtract(nextInstructionTime, startTime); - - // See if it's the same as cycles_since_time - double guesstimate = cycles_since_time(&runtime); - printf("cycle %d guesstimate %f\n", i, guesstimate); - if (guesstimate != i) { - printf("FAILED: cycle %d has guesstimate %f\n", i, guesstimate); - exit(1); - } - } - - printf("All ok\n"); - - exit(1); -#endif - SDL_Init(SDL_INIT_EVERYTHING); g_speaker = new SDLSpeaker(); @@ -344,7 +287,9 @@ int main(int argc, char *argv[]) // pthread_setschedparam(cpuThreadID, SCHED_RR, PTHREAD_MAX_PRIORITY); } + uint32_t lastCycleCount = -1; while (1) { + if (g_biosInterrupt) { printf("Invoking BIOS\n"); if (bios.runUntilDone()) { @@ -378,7 +323,7 @@ int main(int argc, char *argv[]) } - static uint32_t usleepcycles = 16384; // step-down for display drawing. Dynamically updated based on FPS calculations. + static uint32_t usleepcycles = 16384*4; // step-down for display drawing. Dynamically updated based on FPS calculations. // fill disk buffer when needed ((AppleVM*)g_vm)->disk6->fillDiskBuffer(); @@ -413,9 +358,9 @@ int main(int argc, char *argv[]) g_display->debugMsg(buf); #endif - if (fps > 60) { + if (fps > 30 && usleepcycles < 0x3FFFFFFF) { usleepcycles *= 2; - } else if (fps < 40) { + } else if (fps < 20 && usleepcycles > 0xF) { usleepcycles /= 2; } @@ -451,6 +396,15 @@ int main(int argc, char *argv[]) #endif + if (g_cpu->cycles == lastCycleCount) { + // If the CPU didn't advance during our last loop, then delay + // here; there can't be any substantial updates, so no need to + // beat up the host machine + + usleep(100000); + } else { + lastCycleCount = g_cpu->cycles; + } } }