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 "cpu.h"
#ifdef TEENSYDUINO
#include "teensy-paddles.h"
#endif
enum {
ACT_EXIT = 1,
@ -23,8 +26,11 @@ enum {
ACT_SUSPEND = 13,
ACT_RESTORE = 14,
ACT_PRIMODE = 15,
ACT_SPEED = 16,
ACT_ABOUT = 17,
ACT_PADX_INV = 16,
ACT_PADY_INV = 17,
ACT_PADDLES = 18,
ACT_SPEED = 19,
ACT_ABOUT = 20,
};
#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,
ACT_DEBUG, ACT_SUSPEND, ACT_RESTORE };
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,
ACT_HD1, ACT_HD2 };
@ -210,6 +217,21 @@ bool BIOS::runUntilDone()
}
}
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:
g_volume ++;
if (g_volume > 15) {
@ -363,6 +385,9 @@ bool BIOS::isActionActive(int8_t action)
case ACT_HD2:
case ACT_SUSPEND:
case ACT_RESTORE:
case ACT_PADX_INV:
case ACT_PADY_INV:
case ACT_PADDLES:
return true;
case ACT_VOLPLUS:
@ -524,6 +549,21 @@ void BIOS::DrawHardwareMenu()
else
strcpy(buf, "Prioritize audio over display");
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:
strcpy(buf, "Volume +");
break;
@ -641,6 +681,39 @@ void BIOS::DrawMainMenu()
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
// 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);
uint8_t GatherFilenames(uint8_t pageOffset, const char *filter);
void ConfigurePaddles();
void stripDirectory();
void showAbout();

View File

@ -17,3 +17,5 @@ volatile uint8_t g_debugMode = D_NONE;
bool g_prioritizeDisplay = false;
volatile bool g_biosInterrupt = false;
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 volatile bool g_biosInterrupt;
extern uint32_t g_speed;
extern bool g_invertPaddleX;
extern bool g_invertPaddleY;
#endif

View File

@ -12,7 +12,8 @@ class PhysicalDisplay {
virtual void flush() = 0; // flush any pending drawings
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;

View File

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

View File

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

View File

@ -7,7 +7,7 @@
#include "appleui.h"
#include <SPI.h>
#define _clock 65000000
#define _clock 50000000
#define PIN_RST 8
#define PIN_DC 9
@ -175,7 +175,7 @@ void TeensyDisplay::flush()
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.
@ -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)
{
int8_t xsize = 8,

View File

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

View File

@ -47,3 +47,9 @@ void TeensyPaddles::startReading()
g_vm->triggerPaddleInCycles(0, 12 * paddle0());
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);
virtual ~TeensyPaddles();
void setRev(bool p0rev, bool p1rev);
virtual uint8_t paddle0();
virtual uint8_t paddle1();
virtual void startReading();

View File

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

View File

@ -20,7 +20,7 @@
#define RESETPIN 39
#define BATTERYPIN 32
#define SPEAKERPIN 19 // FIXME this isn't right
#define SPEAKERPIN A17 // aka digital 41
#include "globals.h"
#include "teensy-crash.h"
@ -62,16 +62,11 @@ void setup()
pinMode(RESETPIN, INPUT);
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
analogReadAveraging(4); // ?? dunno if we need this or not.
analogWriteResolution(12);
pinMode(SPEAKERPIN, OUTPUT); // analog speaker output, used as digital volume control
pinMode(BATTERYPIN, INPUT);
*/
Serial.print("Free RAM: ");
println(FreeRamEstimate());
@ -139,7 +134,7 @@ void setup()
println(FreeRamEstimate());
println(" paddles");
g_paddles = new TeensyPaddles(A3, A4, 1, 1);
g_paddles = new TeensyPaddles(A3, A4, g_invertPaddleX, g_invertPaddleY);
Serial.print("Free RAM: ");
println(FreeRamEstimate());
@ -148,8 +143,7 @@ void setup()
println("Resetting VM");
g_vm->Reset();
g_display->redraw();
// g_display->blit();
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
@ -212,6 +206,10 @@ void biosInterrupt()
if (bios.runUntilDone()) {
// if it returned true, we have something to store persistently in EEPROM.
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
@ -227,8 +225,8 @@ void biosInterrupt()
g_speaker->maintainSpeaker(-1, -1);
// Force the display to redraw
g_display->redraw();
((AppleDisplay*)(g_vm->vmdisplay))->modeChange();
g_display->redraw(); // Redraw the UI
((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
g_keyboard->maintainKeyboard();
@ -285,35 +283,17 @@ void loop()
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_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();
// g_vm->vmdisplay->didRedraw();
g_vm->vmdisplay->didRedraw();
// 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();
if (g_prioritizeDisplay)
Timer1.start();
static unsigned long nextBattCheck = millis() + 30;// debugging
static int batteryLevel = 0; // static for debugging code! When done
@ -442,6 +422,12 @@ void readPrefs()
((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()
@ -457,6 +443,9 @@ void writePrefs()
p.prefsSize = sizeof(prefs_t);
p.version = PREFSVERSION;
p.invertPaddleX = g_invertPaddleX;
p.invertPaddleY = g_invertPaddleY;
p.volume = g_volume;
p.displayType = g_displayType;
p.debug = g_debugMode;

View File

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