threading fixes

This commit is contained in:
Jorj Bauer 2020-07-08 17:44:25 -04:00
parent e1288db403
commit 0024764fc4
6 changed files with 33 additions and 81 deletions

View File

@ -130,11 +130,11 @@ void AppleUI::blit()
uint8_t yoff = 213; uint8_t yoff = 213;
for (int x=0; x<6; x++) { for (int x=0; x<6; x++) {
g_display->drawUIPixel(x + xoff, yoff, driveActivity[0] ? 0xF800 : 0x8AA9); g_display->drawUIPixel(x + xoff, yoff, driveActivity[0] ? 0xFA00 : 0x0000);
g_display->drawUIPixel(x + xoff, yoff + 1, driveActivity[0] ? 0xF800 : 0x8AA9); 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, driveActivity[1] ? 0xFA00 : 0x0000);
g_display->drawUIPixel(x + xoff + 135, yoff + 1, driveActivity[1] ? 0xF800 : 0x8AA9); g_display->drawUIPixel(x + xoff + 135, yoff + 1, driveActivity[1] ? 0xFA00 : 0x0000);
} }
} }

View File

@ -27,9 +27,9 @@ class AppleUI : public VMui {
virtual void blit(); virtual void blit();
private: private:
bool redrawFrame; volatile bool redrawFrame;
bool redrawDriveLatches; volatile bool redrawDriveLatches;
bool redrawDriveActivity; volatile bool redrawDriveActivity;
bool driveInserted[2]; bool driveInserted[2];
bool driveActivity[2]; bool driveActivity[2];
}; };

View File

@ -257,8 +257,7 @@ void DiskII::driveOn()
} }
// FIXME: does the sequencer get reset? Maybe if it's the selected disk? Or no? // FIXME: does the sequencer get reset? Maybe if it's the selected disk? Or no?
// sequencer = 0; // sequencer = 0;
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true);
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: do we really want to update the UI from inside this thread?
} }
uint8_t DiskII::readSwitches(uint8_t s) uint8_t DiskII::readSwitches(uint8_t s)
@ -595,11 +594,11 @@ void DiskII::select(int8_t which)
diskIsSpinningUntil[selectedDisk] = 0; diskIsSpinningUntil[selectedDisk] = 0;
// FIXME: consume any disk bits that need to be consumed, and // FIXME: consume any disk bits that need to be consumed, and
// spin it down // 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 // Spin up the other one though
diskIsSpinningUntil[which] = -1; 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 // 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 // Stop the given disk drive spinning
diskIsSpinningUntil[i] = 0; diskIsSpinningUntil[i] = 0;
// FIXME: consume any disk bits that need to be consumed, and spin it down // 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] && if (flushAt[i] &&

View File

@ -8,7 +8,9 @@
#ifdef TEENSYDUINO #ifdef TEENSYDUINO
#include <TeensyThreads.h> #include <TeensyThreads.h>
#include <Bounce2.h>
#include "teensy-paddles.h" #include "teensy-paddles.h"
extern Bounce resetButtonDebouncer;
#endif #endif
enum { enum {
@ -53,9 +55,6 @@ const uint8_t diskActions[] = { ACT_DISK1, ACT_DISK2,
#define CPUSPEED_DOUBLE 2 #define CPUSPEED_DOUBLE 2
#define CPUSPEED_QUAD 3 #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) const char *staticPathConcat(const char *rootPath, const char *filePath)
{ {
static char buf[MAXPATH]; static char buf[MAXPATH];
@ -284,7 +283,7 @@ uint8_t BIOS::GetAction(int8_t selection)
while (!g_keyboard->kbhit() while (!g_keyboard->kbhit()
#ifdef TEENSYDUINO #ifdef TEENSYDUINO
&& &&
(digitalRead(RESETPIN) == HIGH) (resetButtonDebouncer.read() == HIGH)
#endif #endif
) { ) {
#ifndef TEENSYDUINO #ifndef TEENSYDUINO
@ -296,10 +295,9 @@ uint8_t BIOS::GetAction(int8_t selection)
} }
#ifdef TEENSYDUINO #ifdef TEENSYDUINO
// FIXME: debounce! if (resetButtonDebouncer.read() == LOW) {
if (digitalRead(RESETPIN) == LOW) {
// wait until it's no longer pressed // 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 threads.delay(100); // wait long enough for it to debounce
// then return an exit code // then return an exit code

View File

@ -29,9 +29,9 @@
#include "globals.h" #include "globals.h"
#include "applevm.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); 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) 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) void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)

View File

@ -80,7 +80,7 @@ void onKeyrelease(int unicode)
void setup() void setup()
{ {
Serial.begin(230400); Serial.begin(230400);
#if 0 #if 1
// Wait for USB serial connection before booting while debugging // Wait for USB serial connection before booting while debugging
while (!Serial) { while (!Serial) {
yield(); yield();
@ -110,37 +110,22 @@ void setup()
pinMode(SPEAKERPIN, OUTPUT); // analog speaker output, used as digital volume control pinMode(SPEAKERPIN, OUTPUT); // analog speaker output, used as digital volume control
pinMode(BATTERYPIN, INPUT); pinMode(BATTERYPIN, INPUT);
Serial.print("Free RAM: ");
println(FreeRamEstimate());
println("creating virtual hardware"); println("creating virtual hardware");
g_speaker = new TeensySpeaker(SPEAKERPIN); g_speaker = new TeensySpeaker(SPEAKERPIN);
Serial.print("Free RAM: ");
println(FreeRamEstimate());
println(" fm"); println(" fm");
// First create the filemanager - the interface to the host file system. // First create the filemanager - the interface to the host file system.
g_filemanager = new TeensyFileManager(); g_filemanager = new TeensyFileManager();
Serial.print("Free RAM: ");
println(FreeRamEstimate());
// Construct the interface to the host display. This will need the // 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 // VM's video buffer in order to draw the VM, but we don't have that
// yet. // yet.
println(" display"); println(" display");
g_display = new TeensyDisplay(); g_display = new TeensyDisplay();
Serial.print("Free RAM: ");
println(FreeRamEstimate());
println(" UI"); println(" UI");
g_ui = new AppleUI(); g_ui = new AppleUI();
Serial.print("Free RAM: ");
println(FreeRamEstimate());
// Next create the virtual CPU. This needs the VM's MMU in order to // Next create the virtual CPU. This needs the VM's MMU in order to
// run, but we don't have that yet. // run, but we don't have that yet.
println(" cpu"); println(" cpu");
@ -151,47 +136,30 @@ void setup()
usb.attachKeypress(onKeypress); usb.attachKeypress(onKeypress);
usb.attachKeyrelease(onKeyrelease); usb.attachKeyrelease(onKeyrelease);
Serial.print("Free RAM: ");
println(FreeRamEstimate());
// Create the virtual machine. This may read from g_filemanager to // Create the virtual machine. This may read from g_filemanager to
// get ROMs if necessary. (The actual Apple VM we've built has them // get ROMs if necessary. (The actual Apple VM we've built has them
// compiled in, though.) It will create its virutal hardware (MMU, // compiled in, though.) It will create its virutal hardware (MMU,
// video driver, floppy, paddles, whatever). // video driver, floppy, paddles, whatever).
println(" vm"); println(" vm");
Serial.flush();
g_vm = new AppleVM(); 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 // Now that the VM exists and it has created an MMU, we tell the CPU
// how to access memory through the MMU. // how to access memory through the MMU.
println(" [setMMU]"); println(" [setMMU]");
g_cpu->SetMMU(g_vm->getMMU()); g_cpu->SetMMU(g_vm->getMMU());
Serial.print("Free RAM: ");
println(FreeRamEstimate());
// And the physical keyboard needs hooks in to the virtual keyboard... // And the physical keyboard needs hooks in to the virtual keyboard...
println(" keyboard"); println(" keyboard");
g_keyboard = new TeensyKeyboard(g_vm->getKeyboard()); g_keyboard = new TeensyKeyboard(g_vm->getKeyboard());
Serial.print("Free RAM: ");
println(FreeRamEstimate());
println(" paddles"); println(" paddles");
g_paddles = new TeensyPaddles(A3, A4, g_invertPaddleX, g_invertPaddleY); 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 // Now that all the virtual hardware is glued together, reset the VM
println("Resetting VM"); println("Resetting VM");
g_vm->Reset(); g_vm->Reset();
g_display->redraw(); // Redraw the UI; don't blit to the physical device
println("Reading prefs"); println("Reading prefs");
readPrefs(); // read from eeprom and set anything we need setting 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/JORJ/disk_s6d1.dsk", false);
// ((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/GAMES/ALIBABA.DSK", false); // ((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/GAMES/ALIBABA.DSK", false);
Serial.print("Free RAM: ");
println(FreeRamEstimate());
resetButtonDebouncer.attach(RESETPIN); resetButtonDebouncer.attach(RESETPIN);
resetButtonDebouncer.interval(5); // ms resetButtonDebouncer.interval(5); // ms
println("free-running"); println("free-running");
Serial.flush();
threads.setMicroTimer(); // use a 100uS timer instead of a 1mS timer threads.setMicroTimer(); // use a 100uS timer instead of a 1mS timer
cpuThreadId = threads.addThread(runCPU); cpuThreadId = threads.addThread(runCPU);
@ -252,30 +218,24 @@ void biosInterrupt()
Threads::Scope lock1(cpulock); Threads::Scope lock1(cpulock);
Threads::Scope lock2(displaylock); Threads::Scope lock2(displaylock);
Serial.println("Waiting for button to be released");
// wait for the interrupt button to be released // wait for the interrupt button to be released
while (!resetButtonDebouncer.read()) while (!resetButtonDebouncer.read())
; ;
Serial.println("Invoking BIOS");
// invoke the BIOS // invoke the BIOS
if (bios.runUntilDone()) { if (bios.runUntilDone()) {
// if it returned true, we have something to store persistently in EEPROM. // if it returned true, we have something to store persistently in EEPROM.
// The EEPROM doesn't like to be written to from a thread? // The EEPROM doesn't like to be written to from a thread?
Serial.println("Writing prefs");
g_writePrefsFromMainLoop = true; g_writePrefsFromMainLoop = true;
while (g_writePrefsFromMainLoop) { while (g_writePrefsFromMainLoop) {
Serial.println("Waiting for prefs to be written");
delay(100); delay(100);
// wait for write to complete // wait for write to complete
} }
// Also might have changed the paddles state // Also might have changed the paddles state
Serial.println("Updating paddle state");
TeensyPaddles *tmp = (TeensyPaddles *)g_paddles; TeensyPaddles *tmp = (TeensyPaddles *)g_paddles;
tmp->setRev(g_invertPaddleX, g_invertPaddleY); tmp->setRev(g_invertPaddleX, g_invertPaddleY);
} }
Serial.println("Cleaning up");
// if we turned off debugMode, make sure to clear the debugMsg // if we turned off debugMode, make sure to clear the debugMsg
if (g_debugMode == D_NONE) { if (g_debugMode == D_NONE) {
g_display->debugMsg(""); g_display->debugMsg("");
@ -291,12 +251,10 @@ void biosInterrupt()
// Drain the speaker queue (FIXME: a little hacky) // Drain the speaker queue (FIXME: a little hacky)
g_speaker->maintainSpeaker(-1, -1); g_speaker->maintainSpeaker(-1, -1);
Serial.println("Forcing display redraw");
// Force the display to redraw // Force the display to redraw
g_display->redraw(); // Redraw the UI g_display->redraw(); // Redraw the UI
((AppleDisplay*)(g_vm->vmdisplay))->modeChange(); // force a full re-draw and blit ((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 // Poll the keyboard before we start, so we can do selftest on startup
g_keyboard->maintainKeyboard(); g_keyboard->maintainKeyboard();
} }
@ -322,9 +280,7 @@ void runMaintenance()
} }
} else if (threads.getState(biosThreadId) != Threads::RUNNING) { } else if (threads.getState(biosThreadId) != Threads::RUNNING) {
// When the BIOS thread exits, we clean up // When the BIOS thread exits, we clean up
Serial.println("Cleaing up bios thread");
threads.wait(biosThreadId); threads.wait(biosThreadId);
Serial.println("BIOS thread is cleaned");
biosThreadId = -1; biosThreadId = -1;
} }
@ -382,12 +338,13 @@ void runMaintenance()
// appropriately use threads.yield() // appropriately use threads.yield()
void runDisplay() void runDisplay()
{ {
g_display->redraw(); // Redraw the UI; don't blit to the physical device
while (1) { while (1) {
{ {
Threads::Scope lock(displaylock); Threads::Scope lock(displaylock);
doDebugging(); doDebugging();
// FIXME: this is sometimes *VERY* slow.
uint32_t startDisp = millis(); uint32_t startDisp = millis();
uint32_t cpuBefore = g_cpu->cycles; uint32_t cpuBefore = g_cpu->cycles;
g_ui->blit(); g_ui->blit();
@ -404,10 +361,10 @@ void runDisplay()
uint32_t dispTime = millis() - startDisp; uint32_t dispTime = millis() - startDisp;
uint32_t cpuAfter = g_cpu->cycles; uint32_t cpuAfter = g_cpu->cycles;
if (dispTime > 75) { if (dispTime > 75) {
Serial.print("Slow blit: "); print("Slow blit: ");
Serial.print(dispTime); print(dispTime);
Serial.print(" cpu ran: "); print(" cpu ran: ");
Serial.println(cpuAfter - cpuBefore); println(cpuAfter - cpuBefore);
} }
} }
} }
@ -421,7 +378,6 @@ void runCPU()
uint32_t startMillis = millis(); uint32_t startMillis = millis();
Serial.println("CPU thread is started");
while (1) { while (1) {
// Relatively critical timing: CPU needs to run ahead at least 4 // Relatively critical timing: CPU needs to run ahead at least 4
// cycles, b/c we're calling this interrupt (runCPU, that is) just // 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%. // was ((1000/1023) * numberOfCycles) - which is about 97.8%.
if (expectedCycles > g_cpu->cycles) { if (expectedCycles > g_cpu->cycles) {
nextInstructionMicros = micros(); nextInstructionMicros = micros();
#if 0 #if 1
// show a warning on serial about our current performance // show a warning on serial about our current performance
double percentage = ((double)g_cpu->cycles / (double)expectedCycles) * 100.0; double percentage = ((double)g_cpu->cycles / (double)expectedCycles) * 100.0;
static uint32_t nextWarningTime = 0; static uint32_t nextWarningTime = 0;
if (millis() > nextWarningTime) { if (millis() > nextWarningTime) {
static char buf[100]; static char buf[100];
sprintf(buf, "CPU running at %f%% of %d", percentage, g_speed); sprintf(buf, "CPU running at %f%% of %d", percentage, g_speed);
Serial.println(buf); println(buf);
nextWarningTime = millis() + 1000; nextWarningTime = millis() + 1000;
} }
#endif #endif
@ -468,7 +424,6 @@ void loop()
resetButtonDebouncer.update(); resetButtonDebouncer.update();
if (g_writePrefsFromMainLoop) { if (g_writePrefsFromMainLoop) {
Serial.println("Writing prefs");
writePrefs(); writePrefs();
g_writePrefsFromMainLoop = false; g_writePrefsFromMainLoop = false;
} }
@ -568,7 +523,6 @@ void writePrefs()
TeensyPrefs np; TeensyPrefs np;
prefs_t p; prefs_t p;
Serial.println("writePrefs()");
p.magic = PREFSMAGIC; p.magic = PREFSMAGIC;
p.prefsSize = sizeof(prefs_t); p.prefsSize = sizeof(prefs_t);
p.version = PREFSVERSION; p.version = PREFSVERSION;