Teensy 4.1 work; bios option to invert paddles

This commit is contained in:
Jorj Bauer 2020-07-06 21:31:37 -04:00
parent 677bf3a9b4
commit 235012a411
15 changed files with 148 additions and 46 deletions

View File

@ -6,6 +6,9 @@
#include "physicaldisplay.h" #include "physicaldisplay.h"
#include "cpu.h" #include "cpu.h"
#ifdef TEENSYDUINO
#include "teensy-paddles.h"
#endif
enum { enum {
ACT_EXIT = 1, ACT_EXIT = 1,
@ -23,8 +26,11 @@ enum {
ACT_SUSPEND = 13, ACT_SUSPEND = 13,
ACT_RESTORE = 14, ACT_RESTORE = 14,
ACT_PRIMODE = 15, ACT_PRIMODE = 15,
ACT_SPEED = 16, ACT_PADX_INV = 16,
ACT_ABOUT = 17, ACT_PADY_INV = 17,
ACT_PADDLES = 18,
ACT_SPEED = 19,
ACT_ABOUT = 20,
}; };
#define NUM_TITLES 4 #define NUM_TITLES 4
@ -36,7 +42,8 @@ const uint8_t aiieActions[] = { ACT_ABOUT };
const uint8_t vmActions[] = { ACT_EXIT, ACT_RESET, ACT_COLDBOOT, ACT_MONITOR, const uint8_t vmActions[] = { ACT_EXIT, ACT_RESET, ACT_COLDBOOT, ACT_MONITOR,
ACT_DEBUG, ACT_SUSPEND, ACT_RESTORE }; ACT_DEBUG, ACT_SUSPEND, ACT_RESTORE };
const uint8_t hardwareActions[] = { ACT_DISPLAYTYPE, ACT_SPEED, const uint8_t hardwareActions[] = { ACT_DISPLAYTYPE, ACT_SPEED,
ACT_PRIMODE, ACT_VOLPLUS, ACT_VOLMINUS }; ACT_PRIMODE, ACT_PADX_INV, ACT_PADY_INV,
ACT_PADDLES, ACT_VOLPLUS, ACT_VOLMINUS };
const uint8_t diskActions[] = { ACT_DISK1, ACT_DISK2, const uint8_t diskActions[] = { ACT_DISK1, ACT_DISK2,
ACT_HD1, ACT_HD2 }; ACT_HD1, ACT_HD2 };
@ -210,6 +217,21 @@ bool BIOS::runUntilDone()
} }
} }
break; break;
case ACT_PADX_INV:
g_invertPaddleX = !g_invertPaddleX;
#ifdef TEENSYDUINO
((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY);
#endif
break;
case ACT_PADY_INV:
g_invertPaddleY = !g_invertPaddleY;
#ifdef TEENSYDUINO
((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY);
#endif
break;
case ACT_PADDLES:
ConfigurePaddles();
break;
case ACT_VOLPLUS: case ACT_VOLPLUS:
g_volume ++; g_volume ++;
if (g_volume > 15) { if (g_volume > 15) {
@ -363,6 +385,9 @@ bool BIOS::isActionActive(int8_t action)
case ACT_HD2: case ACT_HD2:
case ACT_SUSPEND: case ACT_SUSPEND:
case ACT_RESTORE: case ACT_RESTORE:
case ACT_PADX_INV:
case ACT_PADY_INV:
case ACT_PADDLES:
return true; return true;
case ACT_VOLPLUS: case ACT_VOLPLUS:
@ -524,6 +549,21 @@ void BIOS::DrawHardwareMenu()
else else
strcpy(buf, "Prioritize audio over display"); strcpy(buf, "Prioritize audio over display");
break; break;
case ACT_PADX_INV:
if (g_invertPaddleX)
strcpy(buf, "Paddle X inverted");
else
strcpy(buf, "Paddle X normal");
break;
case ACT_PADY_INV:
if (g_invertPaddleY)
strcpy(buf, "Paddle Y inverted");
else
strcpy(buf, "Paddle Y normal");
break;
case ACT_PADDLES:
strcpy(buf, "Configure paddles");
break;
case ACT_VOLPLUS: case ACT_VOLPLUS:
strcpy(buf, "Volume +"); strcpy(buf, "Volume +");
break; break;
@ -641,6 +681,39 @@ void BIOS::DrawMainMenu()
g_display->flush(); g_display->flush();
} }
void BIOS::ConfigurePaddles()
{
while (1) {
bool needsUpdate = true;
uint8_t lastPaddleX = g_paddles->paddle0();
uint8_t lastPaddleY = g_paddles->paddle1();
if (g_paddles->paddle0() != lastPaddleX) {
lastPaddleX = g_paddles->paddle0();
needsUpdate = true;
}
if (g_paddles->paddle1() != lastPaddleY) {
lastPaddleY = g_paddles->paddle1();
needsUpdate = true;
}
if (needsUpdate) {
char buf[50];
g_display->clrScr();
sprintf(buf, "Paddle X: %d ", lastPaddleX);
g_display->drawString(M_NORMAL, 0, 12, buf);
sprintf(buf, "Paddle Y: %d ", lastPaddleY);
g_display->drawString(M_NORMAL, 0, 42, buf);
g_display->drawString(M_NORMAL, 0, 92, "Exit with any key");
g_display->flush();
}
if (g_keyboard->kbhit()) {
g_keyboard->read(); // throw out keypress
return;
}
}
}
// return true if the user selects an image // return true if the user selects an image
// sets selectedFile (index; -1 = "nope") and fileDirectory[][] (names of up to BIOS_MAXFILES files) // sets selectedFile (index; -1 = "nope") and fileDirectory[][] (names of up to BIOS_MAXFILES files)

2
bios.h
View File

@ -39,6 +39,8 @@ class BIOS {
void DrawDiskNames(uint8_t page, int8_t selection, const char *filter); void DrawDiskNames(uint8_t page, int8_t selection, const char *filter);
uint8_t GatherFilenames(uint8_t pageOffset, const char *filter); uint8_t GatherFilenames(uint8_t pageOffset, const char *filter);
void ConfigurePaddles();
void stripDirectory(); void stripDirectory();
void showAbout(); void showAbout();

View File

@ -17,3 +17,5 @@ volatile uint8_t g_debugMode = D_NONE;
bool g_prioritizeDisplay = false; bool g_prioritizeDisplay = false;
volatile bool g_biosInterrupt = false; volatile bool g_biosInterrupt = false;
uint32_t g_speed = 1023000; // Hz uint32_t g_speed = 1023000; // Hz
bool g_invertPaddleX = false;
bool g_invertPaddleY = false;

View File

@ -52,5 +52,6 @@ extern volatile uint8_t g_debugMode;
extern bool g_prioritizeDisplay; extern bool g_prioritizeDisplay;
extern volatile bool g_biosInterrupt; extern volatile bool g_biosInterrupt;
extern uint32_t g_speed; extern uint32_t g_speed;
extern bool g_invertPaddleX;
extern bool g_invertPaddleY;
#endif #endif

View File

@ -12,7 +12,8 @@ class PhysicalDisplay {
virtual void flush() = 0; // flush any pending drawings virtual void flush() = 0; // flush any pending drawings
virtual void redraw() = 0; // total redraw, assuming nothing virtual void redraw() = 0; // total redraw, assuming nothing
virtual void blit(AiieRect r) = 0; // redraw just the VM display area virtual void blit() = 0; // blit everything to the display (including UI area)
virtual void blit(AiieRect r) = 0; // blit a piece of the VM area to the display
virtual void drawImageOfSizeAt(const uint8_t *img, uint16_t sizex, uint8_t sizey, uint16_t wherex, uint8_t wherey) = 0; virtual void drawImageOfSizeAt(const uint8_t *img, uint16_t sizex, uint8_t sizey, uint16_t wherex, uint8_t wherey) = 0;

View File

@ -6,7 +6,7 @@
// Fun trivia: the Apple //e was in production from January 1983 to // Fun trivia: the Apple //e was in production from January 1983 to
// November 1993. And the 65C02 in them supported weird BCD math modes. // November 1993. And the 65C02 in them supported weird BCD math modes.
#define PREFSMAGIC 0x01831093 #define PREFSMAGIC 0x01831093
#define PREFSVERSION 1 #define PREFSVERSION 2
#ifndef MAXPATH #ifndef MAXPATH
#define MAXPATH 255 #define MAXPATH 255
@ -25,6 +25,9 @@ typedef struct _prefs {
uint8_t priorityMode; uint8_t priorityMode;
uint8_t speed; uint8_t speed;
uint8_t invertPaddleX;
uint8_t invertPaddleY;
char reserved[MAXPATH]; // 255 is the Teensy MAXPATH size char reserved[MAXPATH]; // 255 is the Teensy MAXPATH size
char disk1[MAXPATH]; char disk1[MAXPATH];

View File

@ -91,6 +91,11 @@ void SDLDisplay::drawImageOfSizeAt(const uint8_t *img,
} }
} }
void SDLDisplay::blit()
{
// Whole-screen blit - unimplemented here
}
// Blit the videoBuffer to the display device, in the rect given // Blit the videoBuffer to the display device, in the rect given
void SDLDisplay::blit(AiieRect r) void SDLDisplay::blit(AiieRect r)
{ {

View File

@ -17,6 +17,7 @@ class SDLDisplay : public PhysicalDisplay {
SDLDisplay(); SDLDisplay();
virtual ~SDLDisplay(); virtual ~SDLDisplay();
virtual void blit();
virtual void blit(AiieRect r); virtual void blit(AiieRect r);
virtual void redraw(); virtual void redraw();

View File

@ -7,7 +7,7 @@
#include "appleui.h" #include "appleui.h"
#include <SPI.h> #include <SPI.h>
#define _clock 65000000 #define _clock 50000000
#define PIN_RST 8 #define PIN_RST 8
#define PIN_DC 9 #define PIN_DC 9
@ -175,7 +175,7 @@ void TeensyDisplay::flush()
blit({0,0,191,279}); blit({0,0,191,279});
} }
void TeensyDisplay::blit(AiieRect r) void TeensyDisplay::blit()
{ {
// The goal here is for blitting to happen automatically in DMA transfers. // The goal here is for blitting to happen automatically in DMA transfers.
@ -196,6 +196,19 @@ void TeensyDisplay::blit(AiieRect r)
} }
} }
void TeensyDisplay::blit(AiieRect r)
{
// It's probably faster to just blit the whole thing, rather than a piece,
// because of how it streams data easily when the buffer aligns properly.
tft.writeRect(0,0,320,240,(const uint16_t *)dmaBuffer);
// ... but if we wanted to blit just part, we'd have to create a new
// subset of teh dmaBuffer that has the right row length to match
// the rect width we're blitting, and then do something like this:
//
// tft.writeRect(r.left+HOFFSET,,r.top+VOFFSET,r.right-r.left+HOFFSET,r.bottom-r.top+VOFFSET,(const uint16_t *)some_subset_of_dmaBuffer);
}
void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c) void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c)
{ {
int8_t xsize = 8, int8_t xsize = 8,

View File

@ -16,6 +16,7 @@ class TeensyDisplay : public PhysicalDisplay {
TeensyDisplay(); TeensyDisplay();
virtual ~TeensyDisplay(); virtual ~TeensyDisplay();
virtual void blit();
virtual void blit(AiieRect r); virtual void blit(AiieRect r);
virtual void redraw(); virtual void redraw();

View File

@ -47,3 +47,9 @@ void TeensyPaddles::startReading()
g_vm->triggerPaddleInCycles(0, 12 * paddle0()); g_vm->triggerPaddleInCycles(0, 12 * paddle0());
g_vm->triggerPaddleInCycles(1, 12 * paddle1()); g_vm->triggerPaddleInCycles(1, 12 * paddle1());
} }
void TeensyPaddles::setRev(bool p0rev, bool p1rev)
{
this->p0rev = p0rev;
this->p1rev = p1rev;
}

View File

@ -7,6 +7,8 @@ class TeensyPaddles : public PhysicalPaddles {
TeensyPaddles(uint8_t p0pin, uint8_t p1pin, bool p0rev, bool p1rev); TeensyPaddles(uint8_t p0pin, uint8_t p1pin, bool p0rev, bool p1rev);
virtual ~TeensyPaddles(); virtual ~TeensyPaddles();
void setRev(bool p0rev, bool p1rev);
virtual uint8_t paddle0(); virtual uint8_t paddle0();
virtual uint8_t paddle1(); virtual uint8_t paddle1();
virtual void startReading(); virtual void startReading();

View File

@ -22,9 +22,7 @@ void TeensySpeaker::toggle(uint32_t c)
mixerValue = (toggleState ? 0x1FF : 0x00); mixerValue = (toggleState ? 0x1FF : 0x00);
mixerValue >>= (16-g_volume); mixerValue >>= (16-g_volume);
// FIXME: glad it's DAC0 and all, but... how does that relate to the pin passed in the constructor? analogWrite(speakerPin, mixerValue);
// FIXME: really doesn't work for the Teensy 4 at all
// analogWriteDAC0(mixerValue);
} }
void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t runtimeInMicros) void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t runtimeInMicros)

View File

@ -20,7 +20,7 @@
#define RESETPIN 39 #define RESETPIN 39
#define BATTERYPIN 32 #define BATTERYPIN 32
#define SPEAKERPIN 19 // FIXME this isn't right #define SPEAKERPIN A17 // aka digital 41
#include "globals.h" #include "globals.h"
#include "teensy-crash.h" #include "teensy-crash.h"
@ -62,16 +62,11 @@ void setup()
pinMode(RESETPIN, INPUT); pinMode(RESETPIN, INPUT);
digitalWrite(RESETPIN, HIGH); digitalWrite(RESETPIN, HIGH);
println("FIXME: skipping analogReference, speaker, battery for Teensy 4");
/*
analogReference(EXTERNAL); // 3.3v external, or 1.7v internal. We need 1.7 internal for the battery level, which means we're gonna have to do something about the paddles :/
analogReadRes(8); // We only need 8 bits of resolution (0-255) for battery & paddles analogReadRes(8); // We only need 8 bits of resolution (0-255) for battery & paddles
analogReadAveraging(4); // ?? dunno if we need this or not. analogReadAveraging(4); // ?? dunno if we need this or not.
analogWriteResolution(12);
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: "); Serial.print("Free RAM: ");
println(FreeRamEstimate()); println(FreeRamEstimate());
@ -139,7 +134,7 @@ void setup()
println(FreeRamEstimate()); println(FreeRamEstimate());
println(" paddles"); println(" paddles");
g_paddles = new TeensyPaddles(A3, A4, 1, 1); g_paddles = new TeensyPaddles(A3, A4, g_invertPaddleX, g_invertPaddleY);
Serial.print("Free RAM: "); Serial.print("Free RAM: ");
println(FreeRamEstimate()); println(FreeRamEstimate());
@ -148,8 +143,7 @@ void setup()
println("Resetting VM"); println("Resetting VM");
g_vm->Reset(); g_vm->Reset();
g_display->redraw(); g_display->redraw(); // Redraw the UI; don't blit to the physical device
// g_display->blit();
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
@ -212,6 +206,10 @@ void biosInterrupt()
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.
writePrefs(); writePrefs();
// Also might have changed the paddles state
TeensyPaddles *tmp = (TeensyPaddles *)g_paddles;
tmp->setRev(g_invertPaddleX, g_invertPaddleY);
} }
// if we turned off debugMode, make sure to clear the debugMsg // if we turned off debugMode, make sure to clear the debugMsg
@ -227,8 +225,8 @@ void biosInterrupt()
g_speaker->maintainSpeaker(-1, -1); g_speaker->maintainSpeaker(-1, -1);
// Force the display to redraw // Force the display to redraw
g_display->redraw(); g_display->redraw(); // Redraw the UI
((AppleDisplay*)(g_vm->vmdisplay))->modeChange(); ((AppleDisplay*)(g_vm->vmdisplay))->modeChange(); // force a full re-draw and blit
// 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();
@ -285,35 +283,17 @@ void loop()
doDebugging(); doDebugging();
// Only redraw if the CPU is caught up; and then we'll suspend the
// CPU to draw a full frame.
// Note that this breaks audio, b/c it's real-time and requires the
// CPU running to change the audio line's value. So we need to EITHER
//
// - delay the audio line by at least the time it takes for one
// display update, OR
// - lock display updates so the CPU can update the memory, but we
// keep drawing what was going to be displayed
//
// The Timer1.stop()/start() is bad. Using it, the display doesn't
// tear; but the audio is also broken. Taking it out, audio is good
// but the display tears. So there's a global - g_prioritizeDisplay -
// which lets the user pick which they want.
if (g_prioritizeDisplay)
Timer1.stop();
g_ui->blit(); g_ui->blit();
g_vm->vmdisplay->lockDisplay(); g_vm->vmdisplay->lockDisplay();
if (g_vm->vmdisplay->needsRedraw()) { if (g_vm->vmdisplay->needsRedraw()) { // necessary for the VM to redraw
// Used to get the dirty rect and blit just that rect. Could still do,
// but instead, I'm just wildly wasting resources. MWAHAHAHA
// AiieRect what = g_vm->vmdisplay->getDirtyRect(); // AiieRect what = g_vm->vmdisplay->getDirtyRect();
// g_vm->vmdisplay->didRedraw(); g_vm->vmdisplay->didRedraw();
// g_display->blit(what); // g_display->blit(what);
} }
g_display->blit({0,0,191,279}); g_display->blit(); // Blit the whole thing, including UI area
g_vm->vmdisplay->unlockDisplay(); g_vm->vmdisplay->unlockDisplay();
if (g_prioritizeDisplay)
Timer1.start();
static unsigned long nextBattCheck = millis() + 30;// debugging static unsigned long nextBattCheck = millis() + 30;// debugging
static int batteryLevel = 0; // static for debugging code! When done static int batteryLevel = 0; // static for debugging code! When done
@ -442,6 +422,12 @@ void readPrefs()
((AppleVM *)g_vm)->insertHD(1, p.hd2); ((AppleVM *)g_vm)->insertHD(1, p.hd2);
} }
} }
g_invertPaddleX = p.invertPaddleX;
g_invertPaddleY = p.invertPaddleY;
// Update the paddles with the new inversion state
((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY);
} }
void writePrefs() void writePrefs()
@ -457,6 +443,9 @@ void writePrefs()
p.prefsSize = sizeof(prefs_t); p.prefsSize = sizeof(prefs_t);
p.version = PREFSVERSION; p.version = PREFSVERSION;
p.invertPaddleX = g_invertPaddleX;
p.invertPaddleY = g_invertPaddleY;
p.volume = g_volume; p.volume = g_volume;
p.displayType = g_displayType; p.displayType = g_displayType;
p.debug = g_debugMode; p.debug = g_debugMode;

View File

@ -1,13 +1,18 @@
#ifdef TEENSYDUINO #ifdef TEENSYDUINO
#include <Arduino.h> #include <Arduino.h>
#include "teensy-println.h" #include "teensy-println.h"
EXTMEM uint8_t preallocatedRam[591*256];
#endif #endif
#include "vmram.h" #include "vmram.h"
#include <string.h> #include <string.h>
#include "globals.h" #include "globals.h"
#ifdef TEENSYDUINO
EXTMEM uint8_t preallocatedRam[591*256];
#else
uint8_t preallocatedRam[591*256];
#endif
#ifndef TEENSYDUINO #ifndef TEENSYDUINO
#include <assert.h> #include <assert.h>
#else #else