diff --git a/physicaldisplay.h b/physicaldisplay.h index 0258752..90adc41 100644 --- a/physicaldisplay.h +++ b/physicaldisplay.h @@ -18,7 +18,7 @@ class PhysicalDisplay { virtual void drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c) = 0; virtual void drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str) = 0; - virtual void debugMsg(const char *msg) { strncpy(overlayMessage, msg, sizeof(overlayMessage)); } + virtual void debugMsg(const char *msg) { strncpy(overlayMessage, msg, sizeof(overlayMessage));overlayMessage[strlen(overlayMessage)] = 0; } virtual void drawPixel(uint16_t x, uint16_t y, uint16_t color) = 0; virtual void drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b) = 0; diff --git a/teensy/teensy-display.cpp b/teensy/teensy-display.cpp index 6ec96d9..410d37a 100644 --- a/teensy/teensy-display.cpp +++ b/teensy/teensy-display.cpp @@ -1,8 +1,11 @@ #include // isgraph +#include + #include "teensy-display.h" #include "bios-font.h" #include "appleui.h" +#include #define _clock 65000000 @@ -13,14 +16,28 @@ #define PIN_MISO 12 #define PIN_SCK 13 -#define disp_x_size 239 -#define disp_y_size 319 +// 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" +DMAMEM uint16_t dmaBuffer[240][320]; // 240 rows, 320 columns + +#define RGBto565(r,g,b) (((r & 0x3E00) << 2) | ((g & 0x3F00) >>3) | ((b & 0x3E00) >> 9)) + ILI9341_t3 tft = ILI9341_t3(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO); +DMAChannel dmatx; +DMASetting dmaSetting; + // RGB map of each of the lowres colors const uint16_t loresPixelColors[16] = { 0x0000, // 0 black 0xC006, // 1 magenta @@ -78,18 +95,40 @@ const uint16_t loresPixelColorsWhite[16] = { 0x0000, TeensyDisplay::TeensyDisplay() { - memset(videoBuffer, 0, sizeof(videoBuffer)); + memset(dmaBuffer, 0x80, sizeof(dmaBuffer)); tft.begin(); tft.setRotation(3); tft.setClock(_clock); - // Could set up an automatic DMA transfer here; cf. + // Set up automatic DMA transfers. cf. // https://forum.pjrc.com/threads/25778-Could-there-be-something-like-an-ISR-template-function/page4 +#if 0 + dmaSetting.TCD->CSR = 0; + dmaSetting.TCD->SADDR = dmaBuffer; + dmaSetting.TCD->SOFF = 2; // 2 bytes per pixel + dmaSetting.TCD->ATTR_SRC = 1; + dmaSetting.TCD->NBYTES = 2; + dmaSetting.TCD->SLAST = -320*240*2; + dmaSetting.TCD->BITER = 320*240; + dmaSetting.TCD->CITER = 320*240; + + dmaSetting.TCD->DADDR = &LPSPI4_TDR; // FIXME is this correct? + dmaSetting.TCD->DOFF = 0; + dmaSetting.TCD->ATTR_DST = 1; + dmaSetting.TCD->DLASTSGA = 0; + + // Make it loop on itself + dmaSetting.replaceSettingsOnCompletion(dmaSetting); + + dmatx.begin(false); + dmatx.triggerAtHardwareEvent(DMAMUX_SOURCE_LPSPI4_TX); // FIXME what's the right source ID + dmatx = &dmaSetting; +#endif // LCD initialization complete - clrScr(); + tft.fillScreen(ILI9341_BLACK); driveIndicator[0] = driveIndicator[1] = false; driveIndicatorDirty = true; @@ -111,8 +150,7 @@ void TeensyDisplay::redraw() void TeensyDisplay::clrScr() { - // FIXME: only fill the area that's got our "terminal" - tft.fillScreen(ILI9341_BLACK); + memset(dmaBuffer, 0x00, sizeof(dmaBuffer)); } void TeensyDisplay::drawUIPixel(uint16_t x, uint16_t y, uint16_t color) @@ -132,48 +170,30 @@ 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(AiieRect r) { - // remember these are "starts at pixel number" values, where 0 is the first. - #define HOFFSET 18 - #define VOFFSET 13 + // The goal here is for blitting to happen automatically in DMA transfers. + + // Since that isn't the case yet, here's a manual blit of the whole + // screen (b/c the rect is kinda meaningless in the final "draw + // everything always" DMA mode) + tft.writeRect(0,0,320,240,(const uint16_t *)dmaBuffer); - uint8_t *vbufPtr; - for (uint8_t y=r.top; y<=r.bottom; y++) { - vbufPtr = &videoBuffer[y * TEENSY_DRUN + r.left]; - for (uint16_t x=r.left; x<=r.right; x++) { - uint8_t colorIdx; - if (!(x & 0x01)) { - colorIdx = *vbufPtr >> 4; - } else { - // alpha the right-ish pixel over the left-ish pixel. - colorIdx = *vbufPtr & 0x0F; - } - colorIdx <<= 1; - - uint16_t c; - if (g_displayType == m_monochrome) { - c = loresPixelColorsGreen[colorIdx]; - } - else if (g_displayType == m_blackAndWhite) { - c = loresPixelColorsWhite[colorIdx]; - } else { - c = loresPixelColors[colorIdx]; - } - - drawPixel(x+HOFFSET,y+VOFFSET,c); - - if (x & 0x01) { - // When we do the odd pixels, then move the pixel pointer to the next pixel - vbufPtr++; + // draw overlay, if any, occasionally + { + static uint32_t nextMessageTime = 0; + if (millis() >= nextMessageTime) { + if (overlayMessage[0]) { + drawString(M_SELECTDISABLED, 1, 240 - 16 - 12, overlayMessage); } + nextMessageTime = millis() + 1000; } } - - // draw overlay, if any - if (overlayMessage[0]) { - drawString(M_SELECTDISABLED, 1, 240 - 16 - 12, overlayMessage); - } } void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c) @@ -212,9 +232,9 @@ void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c) uint8_t ch = pgm_read_byte(&BiosFont[temp]); for (int8_t x_off = 0; x_off <= xsize; x_off++) { if (ch & (1 << (7-x_off))) { - drawPixel(x+x_off, y+y_off, onPixel); + dmaBuffer[y+y_off][x+x_off] = onPixel; } else { - drawPixel(x+x_off, y+y_off, offPixel); + dmaBuffer[y+y_off][x+x_off] = offPixel; } } temp++; @@ -242,27 +262,19 @@ void TeensyDisplay::drawImageOfSizeAt(const uint8_t *img, r = pgm_read_byte(&img[(y*sizex + x)*3 + 0]); g = pgm_read_byte(&img[(y*sizex + x)*3 + 1]); b = pgm_read_byte(&img[(y*sizex + x)*3 + 2]); - drawPixel(wherex+x, wherey+y, (((r&248)|g>>5) << 8) | ((g&28)<<3|b>>3)); + dmaBuffer[y+wherey][x+wherex] = RGBto565(r,g,b); } } } // "DoubleWide" means "please double the X because I'm in low-res // width mode". But we only have half the horizontal width required on -// the Teensy, so it's divided in half. And then we drop to 4-bit -// colors, so it's divided in half again. +// the Teensy, so it's divided in half. void TeensyDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color) { - uint8_t b = videoBuffer[y*TEENSY_DRUN+(x>>1)]; - - if (x & 1) { - // Low nybble - b = (b & 0xF0) | (color & 0x0F); - } else { - // High nybble - b = (color << 4) | (b & 0x0F); - } - videoBuffer[y*TEENSY_DRUN+(x>>1)] = b; + uint16_t color16; + color16 = loresPixelColors[(( color & 0x0F ) )]; + dmaBuffer[y+VOFFSET][x+HOFFSET] = color16; } // This exists for 4bpp optimization. We could totally call @@ -271,32 +283,31 @@ void TeensyDisplay::cacheDoubleWidePixel(uint16_t x, uint16_t y, uint8_t color) void TeensyDisplay::cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorA, uint8_t colorB) { - videoBuffer[y*TEENSY_DRUN+(x>>1)] = (colorB << 4) | colorA; + // FIXME: Convert 4-bit colors to 16-bit colors? + dmaBuffer[y+VOFFSET][x+ HOFFSET] = loresPixelColors[colorB&0xF]; + dmaBuffer[y+VOFFSET][x+1+HOFFSET] = loresPixelColors[colorA&0xF]; } // This is the full 560-pixel-wide version -- and we only have 280 -// pixels wide. So we'll divide x by 2. And then at 4bpp, we divide by -// 2 again. -// On odd-numbered X pixels, we also alpha-blend -- "black" means "clear" +// pixels in our buffer b/c the display is only 320 pixels wide +// itself. So we'll divide x by 2. On odd-numbered X pixels, we also +// alpha-blend -- "black" means "clear" void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color) { - if (x&1) { - x >>= 1; // divide by 2, then this is mostly cacheDoubleWidePixel. Except... - uint8_t b = videoBuffer[y*TEENSY_DRUN+(x>>1)]; - - if (x & 1) { - // Low nybble - if (color == c_black) - color = b & 0x0F; - b = (b & 0xF0) | (color & 0x0F); - } else { - // High nybble - if (color == c_black) - color = (b & 0xF0) >> 4; - b = (color << 4) | (b & 0x0F); - } - videoBuffer[y*TEENSY_DRUN+(x>>1)] = b; + if (/*x&*/1) { + // divide x by 2, then this is mostly cacheDoubleWidePixel. Except + // we also have to do the alpha blend so we can see both pixels. + + uint16_t *p = &dmaBuffer[y+VOFFSET][(x>>1)+HOFFSET]; + uint16_t destColor = loresPixelColors[color]; + + // if (color == 0) + // destColor = *p; // retain the even-numbered pixel's contents ("alpha blend") + // Otherwise the odd-numbered pixel's contents "win" as "last drawn" + // FIXME: do better blending of these two pixels. + + dmaBuffer[y+VOFFSET][(x>>1)+HOFFSET] = destColor; } else { - cacheDoubleWidePixel(x/2, y, color); + cacheDoubleWidePixel(x, y, color); } } diff --git a/teensy/teensy-display.h b/teensy/teensy-display.h index 7eed5d8..0b12fad 100644 --- a/teensy/teensy-display.h +++ b/teensy/teensy-display.h @@ -3,16 +3,9 @@ #include #include + #include "physicaldisplay.h" -#define TEENSY_DHEIGHT 192 -#define TEENSY_DWIDTH 280 -// run length of one row of pixels -#define TEENSY_DRUN (TEENSY_DWIDTH/2) - -#define regtype volatile uint8_t -#define regsize uint8_t - class UTFT; class BIOS; @@ -27,7 +20,7 @@ class TeensyDisplay : public PhysicalDisplay { virtual void redraw(); virtual void clrScr(); - virtual void flush() {}; + virtual void flush(); virtual void drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c); virtual void drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str); @@ -46,8 +39,6 @@ class TeensyDisplay : public PhysicalDisplay { bool needsRedraw; bool driveIndicator[2]; bool driveIndicatorDirty; - - uint8_t videoBuffer[TEENSY_DHEIGHT * TEENSY_DWIDTH / 2]; }; #endif diff --git a/teensy/teensy-filemanager.cpp b/teensy/teensy-filemanager.cpp index cca771d..6c9cae4 100644 --- a/teensy/teensy-filemanager.cpp +++ b/teensy/teensy-filemanager.cpp @@ -118,12 +118,13 @@ int8_t TeensyFileManager::readDir(const char *where, const char *suffix, char *o // multiple suffixes to check - all must be 3 chars long, FIXME bool matchesAny = false; const char *p = suffix; - while (*p && strlen(p)) { + while (p && *p && strlen(p)) { if (!strncasecmp(fsuff, p, 3)) { matchesAny = true; break; } - p = strstr(p, ",")+1; + p = strstr(p, ","); + if (p) p++; } if (!matchesAny) { e.close(); diff --git a/teensy/teensy.ino b/teensy/teensy.ino index 356799e..e25e5c6 100644 --- a/teensy/teensy.ino +++ b/teensy/teensy.ino @@ -38,13 +38,17 @@ static time_t getTeensy3Time() { return Teensy3Clock.get(); } void setup() { Serial.begin(230400); - /* +#if 0 + // Wait for USB serial connection before booting while debugging while (!Serial) { yield(); - }*/ - delay(100); // let the power settle + } +#endif + delay(120); // let the power settle + +// enableFaultHandler(); + SCB_SHCSR |= SCB_SHCSR_BUSFAULTENA | SCB_SHCSR_USGFAULTENA | SCB_SHCSR_MEMFAULTENA; - enableFaultHandler(); // set the Time library to use Teensy 3.0's RTC to keep time setSyncProvider(getTeensy3Time); @@ -69,27 +73,45 @@ void setup() pinMode(BATTERYPIN, INPUT); */ + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + println("creating virtual hardware"); g_speaker = new TeensySpeaker(SPEAKERPIN); + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + println(" fm"); // First create the filemanager - the interface to the host file system. g_filemanager = new TeensyFileManager(); + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + // 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 // yet. println(" display"); g_display = new TeensyDisplay(); + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + println(" UI"); g_ui = new AppleUI(); + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + // Next create the virtual CPU. This needs the VM's MMU in order to // run, but we don't have that yet. println(" cpu"); g_cpu = new Cpu(); + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + // Create the virtual machine. This may read from g_filemanager to // get ROMs if necessary. (The actual Apple VM we've built has them // compiled in, though.) It will create its virutal hardware (MMU, @@ -97,18 +119,31 @@ void setup() println(" vm"); 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 // how to access memory through the MMU. println(" [setMMU]"); g_cpu->SetMMU(g_vm->getMMU()); + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + + // And the physical keyboard needs hooks in to the virtual keyboard... println(" keyboard"); g_keyboard = new TeensyKeyboard(g_vm->getKeyboard()); + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + println(" paddles"); g_paddles = new TeensyPaddles(A3, A4, 1, 1); + Serial.print("Free RAM: "); + println(FreeRamEstimate()); + // Now that all the virtual hardware is glued together, reset the VM println("Resetting VM"); g_vm->Reset(); @@ -122,8 +157,8 @@ void setup() startMicros = nextInstructionMicros = micros(); // Debugging: insert a disk on startup... - // ((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/UTIL/mock2dem.dsk", false); - // ((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/JORJ/disk_s6d1.dsk", false); + //((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/UTIL/mock2dem.dsk", false); + //((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/JORJ/disk_s6d1.dsk", false); // ((AppleVM *)g_vm)->insertDisk(0, "/A2DISKS/GAMES/ALIBABA.DSK", false); // pinMode(56, OUTPUT); @@ -271,10 +306,11 @@ void loop() g_ui->blit(); g_vm->vmdisplay->lockDisplay(); if (g_vm->vmdisplay->needsRedraw()) { - AiieRect what = g_vm->vmdisplay->getDirtyRect(); - g_vm->vmdisplay->didRedraw(); - g_display->blit(what); + // AiieRect what = g_vm->vmdisplay->getDirtyRect(); + // g_vm->vmdisplay->didRedraw(); + // g_display->blit(what); } + g_display->blit({0,0,191,279}); g_vm->vmdisplay->unlockDisplay(); if (g_prioritizeDisplay) Timer1.start(); diff --git a/vmram.cpp b/vmram.cpp index b340352..9cadf56 100644 --- a/vmram.cpp +++ b/vmram.cpp @@ -1,6 +1,7 @@ #ifdef TEENSYDUINO #include #include "teensy-println.h" +EXTMEM uint8_t preallocatedRam[591*256]; #endif #include "vmram.h" diff --git a/vmram.h b/vmram.h index 4b893d5..1d3e158 100644 --- a/vmram.h +++ b/vmram.h @@ -36,7 +36,10 @@ class VMRam { // Pages 0-3 are ZP; we want those in RAM. // Pages 4-7 are 0x200 - 0x3FF. We want those in RAM too (text pages). - uint8_t preallocatedRam[591*256]; + // Has to be static if we're using the EXTMEM sectioning, so it's now in vmram.cpp :/ + //EXTMEM uint8_t preallocatedRam[591*256]; + + };