From efc36d40a841b730f662ca03231b9f7d471fa4c9 Mon Sep 17 00:00:00 2001 From: Jorj Bauer Date: Tue, 19 Jan 2021 15:37:54 -0500 Subject: [PATCH] merging display paths between SDL and Teensy --- sdl/sdl-display.cpp | 53 +++--- sdl/sdl-display.h | 2 +- teensy/teensy-display.cpp | 337 ++++++++++++++++---------------------- teensy/teensy-display.h | 4 + 4 files changed, 176 insertions(+), 220 deletions(-) diff --git a/sdl/sdl-display.cpp b/sdl/sdl-display.cpp index 0fc43a0..b2835f4 100644 --- a/sdl/sdl-display.cpp +++ b/sdl/sdl-display.cpp @@ -263,38 +263,41 @@ 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. The SDLDISPLAY_SCALE is -// basically half the X scale - so a 320-pixel-wide screen can show 40 -// columns fine, which means that we need to be creative for 80 columns, -// which need to be alpha-blended... +// 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) { - // we need to alpha blend the X because there aren't enough screen pixels. - // This takes advantage of the fact that we always call this linearly - // for the 80-column text -- we never (?) do partial screen blits, but - // always wind up redrawing the entirety. So we can look at the pixel in - // the "shared" cell of RAM, and come up with a color between the two. - if (x&1) { - uint32_t origColor = videoBuffer[y][(x>>1)*SDLDISPLAY_SCALE]; - uint32_t newColor = blendPackedColor(origColor, packColor32(loresPixelColors[color])); - cacheDoubleWidePixel(x>>1,y,newColor); - // Else if it's black, we leave whatever was in the other pixel. +#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. + + 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)); } else { - // The even pixels always draw. - cacheDoubleWidePixel(x>>1,y,color); + cacheDoubleWidePixel(x>>1,y,(uint32_t)blendPackedColor(origColor, newColor)); } - + // Else if it's black, we leave whatever was in the other pixel. } else { - // we have enough resolution to show all the pixels, so just do it - x = (x * SDLDISPLAY_SCALE)/2; - for (int yoff=0; yoff>1,y,color); + } +#else + // we have enough resolution to show all the pixels, so just do it + x = (x * SDLDISPLAY_SCALE)/2; + for (int yoff=0; yoff +#include "globals.h" +#include "applevm.h" +#include #define _clock 75000000 @@ -24,33 +26,8 @@ extern const unsigned char interface_glyphs[256]; #define PIN_MISO 1 #define PIN_SCK 27 -// Inside the 320x240 display, the Apple display is 280x192. -// (That's half the "correct" width, b/c of double-hi-res.) -#define apple_display_w 280 -#define apple_display_h 192 - -// Inset inside the apple2 "frame" where we draw the display -// remember these are "starts at pixel number" values, where 0 is the first. -#define HOFFSET 18 -#define VOFFSET 13 - -#include "globals.h" -#include "applevm.h" - -#define PHYSMAXX 320 -#define PHYSMAXY 240 -DMAMEM uint16_t dmaBuffer[PHYSMAXY][PHYSMAXX]; // 240 rows, 320 columns - -#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) ) - -//ILI9341_t3 tft = ILI9341_t3(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO); -ILI9341_t3n tft = ILI9341_t3n(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO); - -DMAChannel dmatx; -DMASetting dmaSetting; +#define SCREENINSET_X (18*TEENSYDISPLAY_SCALE) +#define SCREENINSET_Y (13*TEENSYDISPLAY_SCALE) // RGB map of each of the lowres colors const uint16_t loresPixelColors[16] = { 0x0000, // 0 black @@ -71,41 +48,20 @@ const uint16_t loresPixelColors[16] = { 0x0000, // 0 black 0xFFFF // 15 white }; -const uint16_t loresPixelColorsGreen[16] = { 0x0000, - 0x0140, - 0x0040, - 0x0280, - 0x0300, - 0x0340, - 0x0300, - 0x0480, - 0x02C0, - 0x0240, - 0x0500, - 0x0540, - 0x0580, - 0x0700, - 0x0680, - 0x07C0 -}; +// This definition can't live in the class header because of the +// DMAMEM adornment +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) ) + +ILI9341_t3n tft = ILI9341_t3n(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO); + +DMAChannel dmatx; +DMASetting dmaSetting; -const uint16_t loresPixelColorsWhite[16] = { 0x0000, - 0x2945, - 0x0841, - 0x528A, - 0x630C, - 0x6B4D, - 0x630C, - 0x9492, - 0x5ACB, - 0x4A49, - 0xA514, - 0xAD55, - 0xB596, - 0xE71C, - 0xD69A, - 0xFFDF -}; TeensyDisplay::TeensyDisplay() { @@ -125,36 +81,60 @@ TeensyDisplay::~TeensyDisplay() { } +void TeensyDisplay::flush() +{ + // Nothing to flush, b/c the DMA driver is regularly flushing everything +} + void TeensyDisplay::redraw() { g_ui->drawStaticUIElement(UIeOverlay); - if (g_vm) { + if (g_vm && g_ui) { g_ui->drawOnOffUIElement(UIeDisk1_state, ((AppleVM *)g_vm)->DiskName(0)[0] == '\0'); g_ui->drawOnOffUIElement(UIeDisk2_state, ((AppleVM *)g_vm)->DiskName(1)[0] == '\0'); } } -void TeensyDisplay::clrScr(uint8_t coloridx) +void TeensyDisplay::drawImageOfSizeAt(const uint8_t *img, + uint16_t sizex, uint8_t sizey, + uint16_t wherex, uint8_t wherey) { - if (coloridx == c_black) { - memset(dmaBuffer, 0x00, sizeof(dmaBuffer)); - } else if (coloridx == c_white) { - memset(dmaBuffer, 0xFF, sizeof(dmaBuffer)); - } else { - uint16_t color16 = loresPixelColors[c_black]; - if (coloridx < 16) - color16 = loresPixelColors[coloridx]; - // This could be faster - make one line, then memcpy the line to the other - // lines? - for (uint8_t y=0; y= nextMessageTime) { + if (overlayMessage[0]) { + drawString(M_SELECTDISABLED, 1, TEENSYDISPLAY_HEIGHT - (16 + 12)*TEENSYDISPLAY_SCALE, overlayMessage); + } + nextMessageTime = millis() + 1000; + } + } +} + +void TeensyDisplay::blit(AiieRect r) +{ + // Nothing to do here, since we're regularly blitting the whole screen via DMA +} + void TeensyDisplay::drawUIPixel(uint16_t x, uint16_t y, uint16_t color) { // These pixels are just cached in the buffer; they're not drawn directly. @@ -173,34 +153,6 @@ void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint drawPixel(x,y,color16); } -void TeensyDisplay::flush() -{ - blit({0,0,191,279}); -} - -void TeensyDisplay::blit() -{ - // Start DMA transfers if they aren't running - if (!tft.asyncUpdateActive()) - tft.updateScreenAsync(true); - - // draw overlay, if any, occasionally - { - static uint32_t nextMessageTime = 0; - if (millis() >= nextMessageTime) { - if (overlayMessage[0]) { - drawString(M_SELECTDISABLED, 1, PHYSMAXY - 16 - 12, overlayMessage); - } - nextMessageTime = millis() + 1000; - } - } -} - -void TeensyDisplay::blit(AiieRect r) -{ - // Nothing to do here, since we're regularly blitting the whole screen via DMA -} - void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c) { int8_t xsize = 8, @@ -234,15 +186,11 @@ void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c) // This does not scale when drawing, because drawPixel scales. const unsigned char *ch = asciiToAppleGlyph(c); for (int8_t y_off = 0; y_off <= ysize; y_off++) { - if (y + y_off < PHYSMAXY) { - for (int8_t x_off = 0; x_off <= xsize; x_off++) { - if (x+x_off < PHYSMAXX) { - if (*ch & (1 << (x_off))) { - dmaBuffer[y+y_off][x+x_off] = onPixel; - } else { - dmaBuffer[y+y_off][x+x_off] = offPixel; - } - } + for (int8_t x_off = 0; x_off <= xsize; x_off++) { + if (*ch & (1 << (x_off))) { + drawUIPixel(x + x_off, y + y_off, onPixel); + } else { + drawUIPixel(x + x_off, y + y_off, offPixel); } } ch++; @@ -251,58 +199,35 @@ void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c) void TeensyDisplay::drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str) { - int8_t xsize = 8; // width of a char in this font + int8_t xsize = 8; // width of a char in this font for (int8_t i=0; i= PHYSMAXX) break; + x += xsize; // fixme: any inter-char spacing? + if (x >= 320) break; // FIXME constant - and pre-scaling, b/c that's in drawCharacter } } -void TeensyDisplay::drawImageOfSizeAt(const uint8_t *img, - uint16_t sizex, uint8_t sizey, - uint16_t wherex, uint8_t wherey) +void TeensyDisplay::clrScr(uint8_t coloridx) { - uint8_t r, g, b; - - for (uint8_t y=0; y>1)+HOFFSET]; - uint16_t newColor = loresPixelColors[color]; + 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; if (g_displayType == m_blackAndWhite) { cacheDoubleWidePixel(x>>1, y, (origColor && newColor) ? 0xFFFF : 0x0000); } else { - cacheDoubleWidePixel(x>>1, y, blendColors(origColor, newColor)); + cacheDoubleWidePixel(x>>1,y,blendColors(origColor, newColor)); + // Else if it's black, we leave whatever was in the other pixel. } #endif - -#if 0 - // The model we use for the SDL display works better, strangely - it keeps - // the lores pixel index color (black, magenda, dark blue, purple, dark - // green, etc.) until render time; so when it does the blend here, it's - // actually blending in a very nonlinear way - e.g. "black + white / 2" - // is actually "black(0) + white(15) / 2 = 15/2 = 7 (light blue)". Weird, - // but definitely legible in a mini laptop SDL window with the same scale. - // Unfortunately, it doesn't translate well to a ILI9341 panel; the pixels - // are kind of muddy and indistinct, so the blue spills over and makes it - // very difficult to read. - uint8_t origColor = previousColor; - uint8_t newColor = (uint16_t)(origColor + color) / 2; - cacheDoubleWidePixel(x>>1, y, (uint16_t)color + (uint16_t)previousColor/2); -#endif } else { -#if 0 - previousColor = color; // used for blending + // The even pixels always draw. + cacheDoubleWidePixel(x>>1,y,color); + } +#else + // we have enough resolution to show all the pixels, so just do it + x = (x * TEENSYDISPLAY_SCALE)/2; + for (int yoff=0; yoff>1, y, color); +} + + + +// "DoubleWide" means "please double the X because I'm in low-res +// width mode". +void TeensyDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color) +{ + uint16_t color16; + color16 = loresPixelColors[(( color & 0x0F ) )]; + + for (int yoff=0; yoff