From 97059a0a5bbd266e01024a61185eaae4981467d5 Mon Sep 17 00:00:00 2001 From: Jorj Bauer Date: Tue, 19 Jan 2021 18:40:13 -0500 Subject: [PATCH] add luminance cutoff for B&W mode --- bios.cpp | 60 +++++++++++++++++++++++++++++---------- globals.cpp | 4 ++- globals.h | 2 ++ prefsstore.h | 6 +++- sdl/aiie.cpp | 5 ++++ sdl/sdl-display.cpp | 39 ++++++++++++++++--------- teensy/teensy-display.cpp | 39 ++++++++++++++++++------- teensy/teensy-display.h | 1 + teensy/teensy-prefs.cpp | 3 +- teensy/teensy.ino | 32 +++++++++++++++++---- 10 files changed, 144 insertions(+), 47 deletions(-) diff --git a/bios.cpp b/bios.cpp index e3a091e..e139721 100644 --- a/bios.cpp +++ b/bios.cpp @@ -60,20 +60,22 @@ enum { ACT_REBOOTANDEJECT = 4, ACT_MONITOR = 5, ACT_DISPLAYTYPE = 6, - ACT_DEBUG = 7, - ACT_DISK1 = 8, - ACT_DISK2 = 9, - ACT_HD1 = 10, - ACT_HD2 = 11, - ACT_VOLPLUS = 12, - ACT_VOLMINUS = 13, - ACT_SUSPEND = 14, - ACT_RESTORE = 15, - ACT_PADX_INV = 16, - ACT_PADY_INV = 17, - ACT_PADDLES = 18, - ACT_SPEED = 19, - ACT_ABOUT = 20, + ACT_LUMINANCEUP = 7, + ACT_LUMINANCEDOWN = 8, + ACT_DEBUG = 9, + ACT_DISK1 = 10, + ACT_DISK2 = 11, + ACT_HD1 = 12, + ACT_HD2 = 13, + ACT_VOLPLUS = 14, + ACT_VOLMINUS = 15, + ACT_SUSPEND = 16, + ACT_RESTORE = 17, + ACT_PADX_INV = 18, + ACT_PADY_INV = 19, + ACT_PADDLES = 20, + ACT_SPEED = 21, + ACT_ABOUT = 22, }; #define NUM_TITLES 4 @@ -85,7 +87,8 @@ const uint8_t aiieActions[] = { ACT_ABOUT }; const uint8_t vmActions[] = { ACT_EXIT, ACT_RESET, ACT_REBOOT, ACT_REBOOTANDEJECT, ACT_MONITOR, ACT_DEBUG, ACT_SUSPEND, ACT_RESTORE }; -const uint8_t hardwareActions[] = { ACT_DISPLAYTYPE, ACT_SPEED, +const uint8_t hardwareActions[] = { ACT_DISPLAYTYPE, ACT_LUMINANCEUP, + ACT_LUMINANCEDOWN, ACT_SPEED, ACT_PADX_INV, ACT_PADY_INV, ACT_PADDLES, ACT_VOLPLUS, ACT_VOLMINUS }; const uint8_t diskActions[] = { ACT_DISK1, ACT_DISK2, @@ -386,6 +389,20 @@ uint16_t BIOS::HardwareMenuHandler(bool needsRedraw, bool performAction) ((AppleDisplay*)g_display)->displayTypeChanged(); localRedraw = true; break; + + case ACT_LUMINANCEUP: + if (g_luminanceCutoff < 255) + g_luminanceCutoff++; + ((AppleDisplay*)g_display)->displayTypeChanged(); + localRedraw = true; + break; + + case ACT_LUMINANCEDOWN: + if (g_luminanceCutoff > 0) + g_luminanceCutoff--; + ((AppleDisplay*)g_display)->displayTypeChanged(); + localRedraw = true; + break; case ACT_SPEED: currentCPUSpeedIndex++; @@ -797,6 +814,11 @@ bool BIOS::isActionActive(int8_t action) case ACT_PADDLES: return true; + case ACT_LUMINANCEUP: + return (g_luminanceCutoff < 255); + case ACT_LUMINANCEDOWN: + return (g_luminanceCutoff > 0); + case ACT_VOLPLUS: return (g_volume < 15); case ACT_VOLMINUS: @@ -934,6 +956,14 @@ void BIOS::DrawHardwareMenu() } } break; + + case ACT_LUMINANCEUP: + sprintf(buf, "Luminance+: %d", g_luminanceCutoff); + break; + case ACT_LUMINANCEDOWN: + sprintf(buf, "Luminance-: %d", g_luminanceCutoff); + break; + case ACT_SPEED: { const char *templateString = "CPU Speed: %s"; diff --git a/globals.cpp b/globals.cpp index 8f7f2ea..0627b98 100644 --- a/globals.cpp +++ b/globals.cpp @@ -10,7 +10,7 @@ PhysicalSpeaker *g_speaker = NULL; PhysicalPaddles *g_paddles = NULL; PhysicalPrinter *g_printer = NULL; VMui *g_ui; -int8_t g_volume = 15; +int8_t g_volume = 7; uint8_t g_displayType = 3; // FIXME m_perfectcolor VMRam g_ram; volatile uint8_t g_debugMode = D_NONE; @@ -19,6 +19,8 @@ uint32_t g_speed = 1023000; // Hz bool g_invertPaddleX = false; bool g_invertPaddleY = false; +uint8_t g_luminanceCutoff = 122; // reasonable values are 127 and 128 for 32-bit (SDL); and 122/123 for 16-bit (teensy) depending on whether we're talking about white-on-black or black-on-white + char debugBuf[255]; #ifdef TEENSYDUINO diff --git a/globals.h b/globals.h index 634c8fa..6d7572d 100644 --- a/globals.h +++ b/globals.h @@ -56,6 +56,8 @@ extern uint32_t g_speed; extern bool g_invertPaddleX; extern bool g_invertPaddleY; +extern uint8_t g_luminanceCutoff; + extern char debugBuf[255]; #ifdef TEENSYDUINO diff --git a/prefsstore.h b/prefsstore.h index d4dcce9..5b5c89f 100644 --- a/prefsstore.h +++ b/prefsstore.h @@ -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 3 +#define PREFSVERSION 4 #ifndef MAXPATH #define MAXPATH 255 @@ -21,6 +21,8 @@ typedef struct _prefs { uint8_t volume; uint8_t displayType; + uint8_t luminanceCutoff; + uint8_t debug; uint8_t speed; @@ -33,6 +35,8 @@ typedef struct _prefs { char disk2[MAXPATH]; char hd1[MAXPATH]; char hd2[MAXPATH]; + + uint32_t magicFooter; } prefs_t; class PrefsStore { diff --git a/sdl/aiie.cpp b/sdl/aiie.cpp index 4b66a80..4126cc2 100644 --- a/sdl/aiie.cpp +++ b/sdl/aiie.cpp @@ -451,6 +451,8 @@ void readPrefs() if (np.readPrefs(&p)) { g_volume = p.volume; g_displayType = p.displayType; + g_luminanceCutoff = p.luminanceCutoff; + g_debugMode = p.debug; g_speed = (p.speed * (1023000/2)); // steps of half normal speed if (g_speed < (1023000/2)) @@ -484,7 +486,10 @@ void writePrefs() p.version = PREFSVERSION; p.volume = g_volume; + p.displayType = g_displayType; + p.luminanceCutoff = g_luminanceCutoff; + p.debug = g_debugMode; p.speed = g_speed / (1023000/2); strcpy(p.disk1, ((AppleVM *)g_vm)->DiskName(0)); diff --git a/sdl/sdl-display.cpp b/sdl/sdl-display.cpp index b2835f4..671ff17 100644 --- a/sdl/sdl-display.cpp +++ b/sdl/sdl-display.cpp @@ -39,10 +39,14 @@ const uint8_t loresPixelColors[16][3] = { { 0, 0, 0 }, // black #define color16To32(x) ( (((x) & 0xF800) << 8) | (((x) & 0x07E0) << 5) | (((x) & 0x001F)<<3) ) #define packColor32(x) ( (x[0] << 16) | (x[1] << 8) | (x[2]) ) +#define unpackRed(x) ( ((x) & 0xFF0000) >> 16 ) +#define unpackGreen(x) ( ((x) & 0xFF00) >> 8 ) +#define unpackBlue(x) ( ((x) & 0xFF) ) // FIXME this blend could be optimized - and it's a dumb blend that // just averages RGB values individually, rather than trying to find a // sane blend of 2 pixels. Needs thought. -#define blendPackedColor(x,y) ( (((x) & 0xFF0000) + ((y) & 0xFF0000) / 2) + (((x) & 0x00FF00) + ((y) & 0x00FF00)) / 2 + (((x) & 0x0000FF) + ((y) & 0x0000FF)) / 2 ) +#define blendPackedColor(x,y) ( (((unpackRed(x) + unpackRed(y))/2) << 16) + (((unpackGreen(x) + unpackGreen(y))/2) << 8) + ((unpackBlue(x) + unpackBlue(y))/2) ) +#define luminanceFromRGB(r,g,b) ( ((r)*0.2126) + ((g)*0.7152) + ((b)*0.0722) ) SDLDisplay::SDLDisplay() { @@ -128,9 +132,9 @@ void SDLDisplay::blit(AiieRect r) b = (colorIdx & 0x0000FF); if (g_displayType == m_monochrome) { - float fv = 0.2125 * r + 0.7154 * g + 0.0721 * b; - r = b = 0; - g = fv; + // float fv = 0.2125 * r + 0.7154 * g + 0.0721 * b; + // r = b = 0; + // g = fv; } else if (g_displayType == m_blackAndWhite) { // Used to reduce to B&W in this driver, but now it's up in the apple display @@ -263,23 +267,32 @@ void SDLDisplay::clrScr(uint8_t coloridx) SDL_RenderPresent(renderer); // perform the render } -// This was called with the expectation that it can draw every one of -// the 560x192 pixels that could be addressed. If TEENSYDISPLAY_SCALE -// is 1, then we have half of that horizontal resolution - so we need -// to be creative and blend neighboring pixels together. +// This was called with the expectation that it can draw every one of +// the 560x192 pixels that could be addressed. If TEENSYDISPLAY_SCALE +// is 1, then we have half of that horizontal resolution - so we need +// to be creative and blend neighboring pixels together. void SDLDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color) { #if SDLDISPLAY_SCALE == 1 - // This is the case where we need to blend together neighboring - // pixels, because we don't have enough physical screen resoultion. + // This is the case where we need to blend together neighboring + // pixels, because we don't have enough physical screen resoultion. if (x&1) { uint32_t origColor = videoBuffer[y][(x>>1)*SDLDISPLAY_SCALE]; uint32_t newColor = packColor32(loresPixelColors[color]); if (g_displayType == m_blackAndWhite) { - // FIXME: the two possible sets here of 'origColor && newColor' or 'origColor||newColor' - // work well for black-on-white and white-on-black. But neither is good in the other. - cacheDoubleWidePixel(x>>1,y,(uint32_t)((origColor && newColor) ? 0xFFFFFF : 0x000000)); + // There are four reasonable decisions here: if either pixel + // *was* on, then it's on; if both pixels *were* on, then it's + // on; and if the blended value of the two pixels were on, then + // it's on; or if the blended value of the two is above some + // certain overall brightness, then it's on. This is the last of + // those - where the brightness cutoff is defined in the bios as + // g_luminanceCutoff. + uint32_t blendedColor = blendPackedColor(origColor, newColor); + uint32_t luminance = luminanceFromRGB(unpackRed(blendedColor), + unpackGreen(blendedColor), + unpackBlue(blendedColor)); + cacheDoubleWidePixel(x>>1,y,(uint32_t)((luminance >= g_luminanceCutoff) ? 0xFFFFFF : 0x000000)); } else { cacheDoubleWidePixel(x>>1,y,(uint32_t)blendPackedColor(origColor, newColor)); } diff --git a/teensy/teensy-display.cpp b/teensy/teensy-display.cpp index ce14c09..db3ed66 100644 --- a/teensy/teensy-display.cpp +++ b/teensy/teensy-display.cpp @@ -2,6 +2,7 @@ #include #include "teensy-display.h" +#include "iocompat.h" #include "appleui.h" // FIXME should be able to omit this include and rely on the xterns, which @@ -54,8 +55,10 @@ DMAMEM uint16_t dmaBuffer[TEENSYDISPLAY_HEIGHT][TEENSYDISPLAY_WIDTH]; #define RGBto565(r,g,b) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | ((b) >> 3)) #define _565toR(c) ( ((c) & 0xF800) >> 8 ) -#define _565toG(c) ( ((c) & 0x07E0) >> 5 ) -#define _565toB(c) ( ((c) & 0x001F) ) +#define _565toG(c) ( ((c) & 0x07E0) >> 3 ) +#define _565toB(c) ( ((c) & 0x001F) << 3 ) +#define luminanceFromRGB(r,g,b) ( ((r)*0.2126) + ((g)*0.7152) + ((b)*0.0722) ) + ILI9341_t3n tft = ILI9341_t3n(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO); @@ -125,7 +128,7 @@ void TeensyDisplay::blit() if (overlayMessage[0]) { drawString(M_SELECTDISABLED, 1, TEENSYDISPLAY_HEIGHT - (16 + 12)*TEENSYDISPLAY_SCALE, overlayMessage); } - nextMessageTime = millis() + 1000; + nextMessageTime = millis() + 10; // DEBUGGING FIXME make 1000 again } } } @@ -262,17 +265,25 @@ void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color) // This is the case where we need to blend together neighboring // pixels, because we don't have enough physical screen resoultion. if (x&1) { - uint8_t origColor = dmaBuffer[y+SCREENINSET_Y][(x>>1)*TEENSYDISPLAY_SCALE+SCREENINSET_X]; - cacheDoubleWidePixel(x>>1, y, color); -#if 0 - uint8_t newColor = (uint16_t) (origColor + color) / 2; + uint16_t origColor = dmaBuffer[y+SCREENINSET_Y][(x>>1)*TEENSYDISPLAY_SCALE+SCREENINSET_X]; + uint16_t newColor = (uint16_t) loresPixelColors[color]; if (g_displayType == m_blackAndWhite) { - cacheDoubleWidePixel(x>>1, y, (origColor && newColor) ? 0xFFFF : 0x0000); + // There are four reasonable decisions here: if either pixel + // *was* on, then it's on; if both pixels *were* on, then it's + // on; and if the blended value of the two pixels were on, then + // it's on; or if the blended value of the two is above some + // certain overall brightness, then it's on. This is the last of + // those - where the brightness cutoff is defined in the bios as + // g_luminanceCutoff. + uint16_t blendedColor = blendColors(origColor, newColor); + uint16_t luminance = luminanceFromRGB(_565toR(blendedColor), + _565toG(blendedColor), + _565toB(blendedColor)); + cacheDoubleWidePixel(x>>1,y,(uint16_t)((luminance >= g_luminanceCutoff) ? 0xFFFF : 0x0000)); } else { - cacheDoubleWidePixel(x>>1,y,blendColors(origColor, newColor)); + cacheDoubleWidePixel(x>>1,y,color); // Else if it's black, we leave whatever was in the other pixel. } -#endif } else { // The even pixels always draw. cacheDoubleWidePixel(x>>1,y,color); @@ -289,6 +300,14 @@ void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color) } +void TeensyDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint16_t color16) +{ + for (int yoff=0; yoffmagic != PREFSMAGIC) { + if (readTo->magic != PREFSMAGIC || + readTo->magicFooter != PREFSMAGIC) { return false; } if (readTo->prefsSize != sizeof(prefs_t)) { diff --git a/teensy/teensy.ino b/teensy/teensy.ino index 0b22b97..ef87258 100644 --- a/teensy/teensy.ino +++ b/teensy/teensy.ino @@ -16,6 +16,8 @@ #include "teensy-println.h" #include "smalloc.h" +#include "iocompat.h" + //#define DEBUG_TIMING #if F_CPU < 240000000 @@ -127,7 +129,12 @@ static uint8_t usb_scanmap[256] = { void onKeypress(uint8_t keycode) { - ((AppleVM *)g_vm)->getKeyboard()->keyDepressed(usb_scanmap[keycode]); + if (keycode == 67) { + // F10 is our interrupt button; FIXME this probably needs to be adjustable + g_biosInterrupt = true; + } else { + ((AppleVM *)g_vm)->getKeyboard()->keyDepressed(usb_scanmap[keycode]); + } } void onKeyrelease(uint8_t keycode) @@ -510,7 +517,7 @@ void loop() wasBios = false; } } - + if (!g_biosInterrupt) { runCPU(now); } @@ -588,11 +595,22 @@ void readPrefs() if (p.hd2[0]) { ((AppleVM *)g_vm)->insertHD(1, p.hd2); } + + g_luminanceCutoff = p.luminanceCutoff; + + g_invertPaddleX = p.invertPaddleX; + g_invertPaddleY = p.invertPaddleY; + + } else { + // Set some defaults! + g_volume = 7; + g_displayType = 3; // FIXME constant + g_debugMode = D_NONE; + g_speed = 1023000; + g_luminanceCutoff = 127; + g_invertPaddleX = g_invertPaddleY = false; + } - - g_invertPaddleX = p.invertPaddleX; - g_invertPaddleY = p.invertPaddleY; - // Update the paddles with the new inversion state ((TeensyPaddles *)g_paddles)->setRev(g_invertPaddleX, g_invertPaddleY); } @@ -603,6 +621,7 @@ void writePrefs() prefs_t p; p.magic = PREFSMAGIC; + p.magicFooter = PREFSMAGIC; p.prefsSize = sizeof(prefs_t); p.version = PREFSVERSION; @@ -611,6 +630,7 @@ void writePrefs() p.volume = g_volume; p.displayType = g_displayType; + p.luminanceCutoff = g_luminanceCutoff; p.debug = g_debugMode; p.speed = g_speed / (1023000/2); strcpy(p.disk1, ((AppleVM *)g_vm)->DiskName(0));