From 0024764fc4671d11f1e1c15b9d2d00a9d728e963 Mon Sep 17 00:00:00 2001 From: Jorj Bauer Date: Wed, 8 Jul 2020 17:44:25 -0400 Subject: [PATCH] threading fixes --- apple/appleui.cpp | 8 ++--- apple/appleui.h | 6 ++-- apple/diskii.cpp | 9 +++-- bios.cpp | 12 +++---- teensy/teensy-display.cpp | 7 ++-- teensy/teensy.ino | 72 +++++++-------------------------------- 6 files changed, 33 insertions(+), 81 deletions(-) diff --git a/apple/appleui.cpp b/apple/appleui.cpp index 240a6ce..238065b 100644 --- a/apple/appleui.cpp +++ b/apple/appleui.cpp @@ -130,11 +130,11 @@ void AppleUI::blit() uint8_t yoff = 213; for (int x=0; x<6; x++) { - g_display->drawUIPixel(x + xoff, yoff, driveActivity[0] ? 0xF800 : 0x8AA9); - g_display->drawUIPixel(x + xoff, yoff + 1, driveActivity[0] ? 0xF800 : 0x8AA9); + g_display->drawUIPixel(x + xoff, yoff, driveActivity[0] ? 0xFA00 : 0x0000); + g_display->drawUIPixel(x + xoff, yoff + 1, driveActivity[0] ? 0xFA00 : 0x0000); - g_display->drawUIPixel(x + xoff + 135, yoff, driveActivity[1] ? 0xF800 : 0x8AA9); - g_display->drawUIPixel(x + xoff + 135, yoff + 1, driveActivity[1] ? 0xF800 : 0x8AA9); + g_display->drawUIPixel(x + xoff + 135, yoff, driveActivity[1] ? 0xFA00 : 0x0000); + g_display->drawUIPixel(x + xoff + 135, yoff + 1, driveActivity[1] ? 0xFA00 : 0x0000); } } diff --git a/apple/appleui.h b/apple/appleui.h index c6ff11e..605f285 100644 --- a/apple/appleui.h +++ b/apple/appleui.h @@ -27,9 +27,9 @@ class AppleUI : public VMui { virtual void blit(); private: - bool redrawFrame; - bool redrawDriveLatches; - bool redrawDriveActivity; + volatile bool redrawFrame; + volatile bool redrawDriveLatches; + volatile bool redrawDriveActivity; bool driveInserted[2]; bool driveActivity[2]; }; diff --git a/apple/diskii.cpp b/apple/diskii.cpp index f5e0068..536be92 100644 --- a/apple/diskii.cpp +++ b/apple/diskii.cpp @@ -257,8 +257,7 @@ void DiskII::driveOn() } // FIXME: does the sequencer get reset? Maybe if it's the selected disk? Or no? // sequencer = 0; - - g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: do we really want to update the UI from inside this thread? + g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); } uint8_t DiskII::readSwitches(uint8_t s) @@ -595,11 +594,11 @@ void DiskII::select(int8_t which) diskIsSpinningUntil[selectedDisk] = 0; // FIXME: consume any disk bits that need to be consumed, and // spin it down - g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing? + g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // Spin up the other one though diskIsSpinningUntil[which] = -1; - g_ui->drawOnOffUIElement(UIeDisk1_activity + which, false); // FIXME: queue for later drawing? + g_ui->drawOnOffUIElement(UIeDisk1_activity + which, true); } // Queue flushing the cache of the disk that's no longer selected @@ -751,7 +750,7 @@ void DiskII::maintenance(uint32_t cycle) // Stop the given disk drive spinning diskIsSpinningUntil[i] = 0; // FIXME: consume any disk bits that need to be consumed, and spin it down - g_ui->drawOnOffUIElement(UIeDisk1_activity + i, false); // FIXME: queue for later drawing? + g_ui->drawOnOffUIElement(UIeDisk1_activity + i, false); } if (flushAt[i] && diff --git a/bios.cpp b/bios.cpp index 9d4f758..a663399 100644 --- a/bios.cpp +++ b/bios.cpp @@ -8,7 +8,9 @@ #ifdef TEENSYDUINO #include +#include #include "teensy-paddles.h" +extern Bounce resetButtonDebouncer; #endif enum { @@ -53,9 +55,6 @@ const uint8_t diskActions[] = { ACT_DISK1, ACT_DISK2, #define CPUSPEED_DOUBLE 2 #define CPUSPEED_QUAD 3 -// FIXME: abstract the pin # rather than repeating it here -#define RESETPIN 39 - const char *staticPathConcat(const char *rootPath, const char *filePath) { static char buf[MAXPATH]; @@ -284,7 +283,7 @@ uint8_t BIOS::GetAction(int8_t selection) while (!g_keyboard->kbhit() #ifdef TEENSYDUINO && - (digitalRead(RESETPIN) == HIGH) + (resetButtonDebouncer.read() == HIGH) #endif ) { #ifndef TEENSYDUINO @@ -296,10 +295,9 @@ uint8_t BIOS::GetAction(int8_t selection) } #ifdef TEENSYDUINO - // FIXME: debounce! - if (digitalRead(RESETPIN) == LOW) { + if (resetButtonDebouncer.read() == LOW) { // wait until it's no longer pressed - while (digitalRead(RESETPIN) == HIGH) + while (resetButtonDebouncer.read() == HIGH) ; threads.delay(100); // wait long enough for it to debounce // then return an exit code diff --git a/teensy/teensy-display.cpp b/teensy/teensy-display.cpp index 3603543..34911ae 100644 --- a/teensy/teensy-display.cpp +++ b/teensy/teensy-display.cpp @@ -29,9 +29,9 @@ #include "globals.h" #include "applevm.h" -DMAMEM uint16_t dmaBuffer[240][320]; // 240 rows, 320 columns +volatile DMAMEM uint16_t dmaBuffer[240][320]; // 240 rows, 320 columns -#define RGBto565(r,g,b) (((r & 0x3E00) << 2) | ((g & 0x3F00) >>3) | ((b & 0x3E00) >> 9)) +#define RGBto565(r,g,b) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | ((b) >> 3)) ILI9341_t3 tft = ILI9341_t3(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO); @@ -155,7 +155,8 @@ void TeensyDisplay::clrScr() void TeensyDisplay::drawUIPixel(uint16_t x, uint16_t y, uint16_t color) { - tft.drawPixel(x,y,color); + // These pixels are just cached in the buffer; they're not drawn directly. + dmaBuffer[y][x] = color; } void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color) diff --git a/teensy/teensy.ino b/teensy/teensy.ino index 257f26e..71a592f 100644 --- a/teensy/teensy.ino +++ b/teensy/teensy.ino @@ -80,7 +80,7 @@ void onKeyrelease(int unicode) void setup() { Serial.begin(230400); -#if 0 +#if 1 // Wait for USB serial connection before booting while debugging while (!Serial) { yield(); @@ -110,37 +110,22 @@ void setup() pinMode(SPEAKERPIN, OUTPUT); // analog speaker output, used as digital volume control pinMode(BATTERYPIN, INPUT); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - println("creating virtual hardware"); g_speaker = new TeensySpeaker(SPEAKERPIN); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - println(" fm"); // First create the filemanager - the interface to the host file system. g_filemanager = new TeensyFileManager(); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - // Construct the interface to the host display. This will need the // VM's video buffer in order to draw the VM, but we don't have that // yet. println(" display"); g_display = new TeensyDisplay(); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - println(" UI"); g_ui = new AppleUI(); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - // Next create the virtual CPU. This needs the VM's MMU in order to // run, but we don't have that yet. println(" cpu"); @@ -151,47 +136,30 @@ void setup() usb.attachKeypress(onKeypress); usb.attachKeyrelease(onKeyrelease); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - // Create the virtual machine. This may read from g_filemanager to // get ROMs if necessary. (The actual Apple VM we've built has them // compiled in, though.) It will create its virutal hardware (MMU, // video driver, floppy, paddles, whatever). println(" vm"); + Serial.flush(); g_vm = new AppleVM(); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - // Now that the VM exists and it has created an MMU, we tell the CPU // how to access memory through the MMU. - println(" [setMMU]"); + println(" [setMMU]"); g_cpu->SetMMU(g_vm->getMMU()); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - - // And the physical keyboard needs hooks in to the virtual keyboard... println(" keyboard"); g_keyboard = new TeensyKeyboard(g_vm->getKeyboard()); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - println(" paddles"); g_paddles = new TeensyPaddles(A3, A4, g_invertPaddleX, g_invertPaddleY); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - // Now that all the virtual hardware is glued together, reset the VM println("Resetting VM"); g_vm->Reset(); - g_display->redraw(); // Redraw the UI; don't blit to the physical device - println("Reading prefs"); readPrefs(); // read from eeprom and set anything we need setting @@ -200,13 +168,11 @@ void setup() //((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/JORJ/disk_s6d1.dsk", false); // ((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/GAMES/ALIBABA.DSK", false); - Serial.print("Free RAM: "); - println(FreeRamEstimate()); - resetButtonDebouncer.attach(RESETPIN); resetButtonDebouncer.interval(5); // ms println("free-running"); + Serial.flush(); threads.setMicroTimer(); // use a 100uS timer instead of a 1mS timer cpuThreadId = threads.addThread(runCPU); @@ -252,30 +218,24 @@ void biosInterrupt() Threads::Scope lock1(cpulock); Threads::Scope lock2(displaylock); - Serial.println("Waiting for button to be released"); // wait for the interrupt button to be released while (!resetButtonDebouncer.read()) ; - Serial.println("Invoking BIOS"); // invoke the BIOS if (bios.runUntilDone()) { // if it returned true, we have something to store persistently in EEPROM. // The EEPROM doesn't like to be written to from a thread? - Serial.println("Writing prefs"); g_writePrefsFromMainLoop = true; while (g_writePrefsFromMainLoop) { - Serial.println("Waiting for prefs to be written"); delay(100); // wait for write to complete } // Also might have changed the paddles state - Serial.println("Updating paddle state"); TeensyPaddles *tmp = (TeensyPaddles *)g_paddles; tmp->setRev(g_invertPaddleX, g_invertPaddleY); } - Serial.println("Cleaning up"); // if we turned off debugMode, make sure to clear the debugMsg if (g_debugMode == D_NONE) { g_display->debugMsg(""); @@ -291,12 +251,10 @@ void biosInterrupt() // Drain the speaker queue (FIXME: a little hacky) g_speaker->maintainSpeaker(-1, -1); - Serial.println("Forcing display redraw"); // Force the display to redraw g_display->redraw(); // Redraw the UI ((AppleDisplay*)(g_vm->vmdisplay))->modeChange(); // force a full re-draw and blit - Serial.println("re-priming keyboard"); // Poll the keyboard before we start, so we can do selftest on startup g_keyboard->maintainKeyboard(); } @@ -322,9 +280,7 @@ void runMaintenance() } } else if (threads.getState(biosThreadId) != Threads::RUNNING) { // When the BIOS thread exits, we clean up - Serial.println("Cleaing up bios thread"); threads.wait(biosThreadId); - Serial.println("BIOS thread is cleaned"); biosThreadId = -1; } @@ -382,12 +338,13 @@ void runMaintenance() // appropriately use threads.yield() void runDisplay() { + g_display->redraw(); // Redraw the UI; don't blit to the physical device + while (1) { { Threads::Scope lock(displaylock); doDebugging(); - - // FIXME: this is sometimes *VERY* slow. + uint32_t startDisp = millis(); uint32_t cpuBefore = g_cpu->cycles; g_ui->blit(); @@ -404,10 +361,10 @@ void runDisplay() uint32_t dispTime = millis() - startDisp; uint32_t cpuAfter = g_cpu->cycles; if (dispTime > 75) { - Serial.print("Slow blit: "); - Serial.print(dispTime); - Serial.print(" cpu ran: "); - Serial.println(cpuAfter - cpuBefore); + print("Slow blit: "); + print(dispTime); + print(" cpu ran: "); + println(cpuAfter - cpuBefore); } } } @@ -421,7 +378,6 @@ void runCPU() uint32_t startMillis = millis(); - Serial.println("CPU thread is started"); while (1) { // Relatively critical timing: CPU needs to run ahead at least 4 // cycles, b/c we're calling this interrupt (runCPU, that is) just @@ -440,14 +396,14 @@ void runCPU() // was ((1000/1023) * numberOfCycles) - which is about 97.8%. if (expectedCycles > g_cpu->cycles) { nextInstructionMicros = micros(); -#if 0 +#if 1 // show a warning on serial about our current performance double percentage = ((double)g_cpu->cycles / (double)expectedCycles) * 100.0; static uint32_t nextWarningTime = 0; if (millis() > nextWarningTime) { static char buf[100]; sprintf(buf, "CPU running at %f%% of %d", percentage, g_speed); - Serial.println(buf); + println(buf); nextWarningTime = millis() + 1000; } #endif @@ -468,7 +424,6 @@ void loop() resetButtonDebouncer.update(); if (g_writePrefsFromMainLoop) { - Serial.println("Writing prefs"); writePrefs(); g_writePrefsFromMainLoop = false; } @@ -568,7 +523,6 @@ void writePrefs() TeensyPrefs np; prefs_t p; - Serial.println("writePrefs()"); p.magic = PREFSMAGIC; p.prefsSize = sizeof(prefs_t); p.version = PREFSVERSION;