mirror of
https://github.com/JorjBauer/aiie.git
synced 2024-12-27 14:30:22 +00:00
conversion to 16-bit blit
This commit is contained in:
parent
c9fe8edc29
commit
7f7d1cc5ce
@ -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;
|
||||
|
@ -1,8 +1,11 @@
|
||||
#include <ctype.h> // isgraph
|
||||
#include <DMAChannel.h>
|
||||
|
||||
#include "teensy-display.h"
|
||||
|
||||
#include "bios-font.h"
|
||||
#include "appleui.h"
|
||||
#include <SPI.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
@ -3,16 +3,9 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ILI9341_t3.h>
|
||||
|
||||
#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
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifdef TEENSYDUINO
|
||||
#include <Arduino.h>
|
||||
#include "teensy-println.h"
|
||||
EXTMEM uint8_t preallocatedRam[591*256];
|
||||
#endif
|
||||
|
||||
#include "vmram.h"
|
||||
|
5
vmram.h
5
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];
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user