gamebloke/src/teensy-display.cpp

1008 lines
27 KiB
C++
Raw Normal View History

2017-10-29 01:21:14 +00:00
#include <ctype.h> // isgraph
#include "teensy-display.h"
#include "bios-font.h"
#include "display-bg.h"
#define RS 16
#define WR 17
#define CS 18
#define RST 19
// Ports C&D of the Teensy connected to DB of the display
#define DB_0 15
#define DB_1 22
#define DB_2 23
#define DB_3 9
#define DB_4 10
#define DB_5 13
#define DB_6 11
#define DB_7 12
#define DB_8 2
#define DB_9 14
#define DB_10 7
#define DB_11 8
#define DB_12 6
#define DB_13 20
#define DB_14 21
#define DB_15 5
#define disp_x_size 239
#define disp_y_size 319
#define UPSIDEDOWN
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef _swap_int16_t
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
#endif
#define setPixel(color) { LCD_Write_DATA(((color)>>8),((color)&0xFF)); } // 565 RGB
#include "globals.h"
#include "applevm.h"
// RGB map of each of the lowres colors
const uint16_t loresPixelColors[16] = {
BLACK, // 0 black
MAGENTA, // 1 magenta
DARK_BLUE, // 2 dark blue
PURPLE, // 3 purple
DARK_GREEN, // 4 dark green
DARK_GREY, // 5 dark grey
BLUE, // 6 med blue
LIGHT_BLUE, // 7 light blue
BROWN, // 8 brown
ORANGE, // 9 orange
LIGHT_GREY, // 10 light gray
PINK, // 11 pink
GREEN, // 12 green
YELLOW, // 13 yellow
AQUA, // 14 aqua
WHITE // 15 white
};
uint16_t backgroundColor;
TeensyDisplay::TeensyDisplay()
{
pinMode(DB_8, OUTPUT);
pinMode(DB_9, OUTPUT);
pinMode(DB_10, OUTPUT);
pinMode(DB_11, OUTPUT);
pinMode(DB_12, OUTPUT);
pinMode(DB_13, OUTPUT);
pinMode(DB_14, OUTPUT);
pinMode(DB_15, OUTPUT);
pinMode(DB_0, OUTPUT);
pinMode(DB_1, OUTPUT);
pinMode(DB_2, OUTPUT);
pinMode(DB_3, OUTPUT);
pinMode(DB_4, OUTPUT);
pinMode(DB_5, OUTPUT);
pinMode(DB_6, OUTPUT);
pinMode(DB_7, OUTPUT);
P_RS = portOutputRegister(digitalPinToPort(RS));
B_RS = digitalPinToBitMask(RS);
P_WR = portOutputRegister(digitalPinToPort(WR));
B_WR = digitalPinToBitMask(WR);
P_CS = portOutputRegister(digitalPinToPort(CS));
B_CS = digitalPinToBitMask(CS);
P_RST = portOutputRegister(digitalPinToPort(RST));
B_RST = digitalPinToBitMask(RST);
pinMode(RS,OUTPUT);
pinMode(WR,OUTPUT);
pinMode(CS,OUTPUT);
pinMode(RST,OUTPUT);
// begin initialization
sbi(P_RST, B_RST);
delay(5);
cbi(P_RST, B_RST);
delay(15);
sbi(P_RST, B_RST);
delay(15);
cbi(P_CS, B_CS);
LCD_Write_COM_DATA(0x00,0x0001); // oscillator start [enable]
LCD_Write_COM_DATA(0x03,0xA8A4); // power control [%1010 1000 1010 1000] == DCT3, DCT1, BT2, DC3, DC1, AP2
LCD_Write_COM_DATA(0x0C,0x0000); // power control2 [0]
LCD_Write_COM_DATA(0x0D,0x080C); // power control3 [VRH3, VRH2, invalid bits]
LCD_Write_COM_DATA(0x0E,0x2B00); // power control4 VCOMG, VDV3, VDV1, VDV0
LCD_Write_COM_DATA(0x1E,0x00B7); // power control5 nOTP, VCM5, VCM4, VCM2, VCM1, VCM0
// LCD_Write_COM_DATA(0x01,0x2B3F); // driver control output REV, BGR, TB, MUX8, MUX5, MUX4, MUX3, MUX2, MUX1, MUX0
// Change that: TB = 0 please
LCD_Write_COM_DATA(0x01,0x293F); // driver control output REV, BGR, TB, MUX8, MUX5, MUX4, MUX3, MUX2, MUX1, MUX0
// LCD_Write_COM_DATA(0x01,0x693F); // driver control output RL, REV, BGR, TB, MUX8, MUX5, MUX4, MUX3, MUX2, MUX1, MUX0
LCD_Write_COM_DATA(0x02,0x0600); // LCD drive AC control B/C, EOR
LCD_Write_COM_DATA(0x10,0x0000); // sleep mode 0
// Change the (Y) order here to match above (TB=0)
//LCD_Write_COM_DATA(0x11,0x6070); // Entry mode DFM1, DFM0, TY0, ID1, ID0
//LCD_Write_COM_DATA(0x11,0x6050); // Entry mode DFM1, DFM0, TY0, ID0
LCD_Write_COM_DATA(0x11,0x6078); // Entry mode DFM1, DFM0, TY0, ID1, ID0, AM
LCD_Write_COM_DATA(0x05,0x0000); // compare reg1
LCD_Write_COM_DATA(0x06,0x0000); // compare reg2
LCD_Write_COM_DATA(0x16,0xEF1C); // horiz porch (default)
LCD_Write_COM_DATA(0x17,0x0003); // vertical porch
LCD_Write_COM_DATA(0x07,0x0233); // display control VLE1, GON, DTE, D1, D0
LCD_Write_COM_DATA(0x0B,0x0000); // frame cycle control
LCD_Write_COM_DATA(0x0F,0x0000); // gate scan start posn
LCD_Write_COM_DATA(0x41,0x0000); // vertical scroll control1
LCD_Write_COM_DATA(0x42,0x0000); // vertical scroll control2
LCD_Write_COM_DATA(0x48,0x0000); // first window start
LCD_Write_COM_DATA(0x49,0x013F); // first window end (0x13f == 319)
LCD_Write_COM_DATA(0x4A,0x0000); // second window start
LCD_Write_COM_DATA(0x4B,0x0000); // second window end
LCD_Write_COM_DATA(0x44,0xEF00); // horiz ram addr posn
LCD_Write_COM_DATA(0x45,0x0000); // vert ram start posn
LCD_Write_COM_DATA(0x46,0x013F); // vert ram end posn
LCD_Write_COM_DATA(0x30,0x0707); // γ control
LCD_Write_COM_DATA(0x31,0x0204);//
LCD_Write_COM_DATA(0x32,0x0204);//
LCD_Write_COM_DATA(0x33,0x0502);//
LCD_Write_COM_DATA(0x34,0x0507);//
LCD_Write_COM_DATA(0x35,0x0204);//
LCD_Write_COM_DATA(0x36,0x0204);//
LCD_Write_COM_DATA(0x37,0x0502);//
LCD_Write_COM_DATA(0x3A,0x0302);//
LCD_Write_COM_DATA(0x3B,0x0302);//
LCD_Write_COM_DATA(0x23,0x0000);// RAM write data mask1
LCD_Write_COM_DATA(0x24,0x0000); // RAM write data mask2
LCD_Write_COM_DATA(0x25,0x8000); // frame frequency (OSC3)
LCD_Write_COM_DATA(0x4f,0x0000); // Set GDDRAM Y address counter
LCD_Write_COM_DATA(0x4e,0x0000); // Set GDDRAM X address counter
#if 0
// Set data access speed optimization (?)
LCD_Write_COM_DATA(0x28, 0x0006);
LCD_Write_COM_DATA(0x2F, 0x12BE);
LCD_Write_COM_DATA(0x12, 0x6CEB);
#endif
LCD_Write_COM(0x22); // RAM data write
sbi(P_CS, B_CS);
// LCD initialization complete
setColor(255, 255, 255);
clrScr();
driveIndicator[0] = driveIndicator[1] = false;
driveIndicatorDirty = true;
backgroundColor = DARK_BLUE;
LED(0, 0, 64);
}
TeensyDisplay::~TeensyDisplay()
{
}
void TeensyDisplay::LED(uint8_t r, uint8_t g, uint8_t b) {
analogWrite(REDPIN, ledRed = r);
analogWrite(GREENPIN, ledGreen = g);
analogWrite(BLUEPIN, ledBlue = b);
}
void TeensyDisplay::LED() {
analogWrite(REDPIN, ledRed);
analogWrite(GREENPIN, ledGreen);
analogWrite(BLUEPIN, ledBlue);
}
void TeensyDisplay::LEDflash() {
analogWrite(REDPIN, 128);
analogWrite(GREENPIN, 128);
analogWrite(BLUEPIN, 128);
}
void TeensyDisplay::_fast_fill_16(int ch, int cl, long pix)
{
*(volatile uint8_t *)(&GPIOD_PDOR) = ch;
*(volatile uint8_t *)(&GPIOC_PDOR) = cl;
uint16_t blocks = pix / 16;
for (uint16_t i=0; i<blocks; i++) {
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
pulse_low(P_WR, B_WR);
}
if ((pix % 16) != 0) {
for (int i=0; i<(pix % 16); i++)
{
pulse_low(P_WR, B_WR);
}
}
}
void TeensyDisplay::redraw()
{
cbi(P_CS, B_CS);
clrXY();
sbi(P_RS, B_RS);
moveTo(0, 0);
#ifdef UPSIDEDOWN
for (int y=TEENSY_DHEIGHT-1; y>=0; y--) {
for (int x=TEENSY_DWIDTH-1; x>=0; x--) {
#else
for (int y=0; y<TEENSY_DHEIGHT; y++) {
for (int x=0; x<TEENSY_DWIDTH; x++) {
#endif
uint8_t r = pgm_read_byte(&displayBitmap[(y * DBITMAP_WIDTH + x)*3 + 0]);
uint8_t g = pgm_read_byte(&displayBitmap[(y * DBITMAP_WIDTH + x)*3 + 1]);
uint8_t b = pgm_read_byte(&displayBitmap[(y * DBITMAP_WIDTH + x)*3 + 2]);
uint16_t color16 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
setPixel(color16);
}
}
if (g_vm) {
drawDriveDoor(0, ((AppleVM *)g_vm)->DiskName(0)[0] == '\0');
drawDriveDoor(1, ((AppleVM *)g_vm)->DiskName(1)[0] == '\0');
}
cbi(P_CS, B_CS);
clrXY();
sbi(P_RS, B_RS);
}
void TeensyDisplay::clrScr()
{
cbi(P_CS, B_CS);
clrXY();
sbi(P_RS, B_RS);
_fast_fill_16(0, 0, ((disp_x_size+1)*(disp_y_size+1)));
sbi(P_CS, B_CS);
}
void TeensyDisplay::clrScr(uint16_t color)
{
uint8_t ch = (uint8_t)(color >> 8);
uint8_t cl = (uint8_t)(color & 0xFF);
cbi(P_CS, B_CS);
clrXY();
sbi(P_RS, B_RS);
_fast_fill_16(ch, cl, ((disp_x_size+1)*(disp_y_size+1)));
sbi(P_CS, B_CS);
}
void TeensyDisplay::clrScr(uint8_t r, uint8_t g, uint8_t b)
{
uint8_t ch=((r&248)|g>>5);
uint8_t cl=((g&28)<<3|b>>3);
cbi(P_CS, B_CS);
clrXY();
sbi(P_RS, B_RS);
_fast_fill_16(ch, cl, ((disp_x_size+1)*(disp_y_size+1)));
sbi(P_CS, B_CS);
}
// The display flips X and Y, so expect to see "x" as "vertical"
// and "y" as "horizontal" here...
void TeensyDisplay::setYX(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
LCD_Write_COM_DATA(0x44, (y2<<8)+y1); // Horiz start addr, Horiz end addr
LCD_Write_COM_DATA(0x45, x1); // vert start pos
LCD_Write_COM_DATA(0x46, x2); // vert end pos
LCD_Write_COM_DATA(0x4e,y1); // RAM address set (horiz)
LCD_Write_COM_DATA(0x4f,x1); // RAM address set (vert)
LCD_Write_COM(0x22);
}
void TeensyDisplay::clrXY()
{
setYX(0, 0, disp_y_size, disp_x_size);
}
void TeensyDisplay::setColor(byte r, byte g, byte b)
{
fch=((r&248)|g>>5);
fcl=((g&28)<<3|b>>3);
}
void TeensyDisplay::setColor(uint16_t color)
{
fch = (uint8_t)(color >> 8);
fcl = (uint8_t)(color & 0xFF);
}
void TeensyDisplay::fillRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
#ifdef UPSIDEDOWN
x1 = disp_y_size - x1;
x2 = disp_y_size - x2;
y1 = disp_x_size - y1;
y2 = disp_x_size - y2;
#endif
if (x1>x2) {
swap(uint16_t, x1, x2);
}
if (y1 > y2) {
swap(uint16_t, y1, y2);
}
cbi(P_CS, B_CS);
setYX(x1, y1, x2, y2);
sbi(P_RS, B_RS);
_fast_fill_16(fch,fcl,((long(x2-x1)+1)*(long(y2-y1)+1)));
sbi(P_CS, B_CS);
}
void TeensyDisplay::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) {
setColor(color);
fillRect(x, y, x+w, y+h);
}
void TeensyDisplay::drawPixel(uint16_t x, uint16_t y)
{
//#ifdef UPSIDEDOWN
// x = disp_y_size - x;
// y = disp_x_size - y;
//#endif
cbi(P_CS, B_CS);
setYX(x, y, x, y);
setPixel((fch<<8)|fcl);
sbi(P_CS, B_CS);
clrXY();
}
void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
{
//#ifdef UPSIDEDOWN
// x = disp_y_size - x;
// y = disp_x_size - y;
//#endif
cbi(P_CS, B_CS);
setYX(x, y, x, y);
setPixel(color);
sbi(P_CS, B_CS);
clrXY();
}
void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b)
{
uint16_t color16 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
//#ifdef UPSIDEDOWN
// x = disp_y_size - x;
// y = disp_x_size - y;
//#endif
cbi(P_CS, B_CS);
setYX(x, y, x, y);
setPixel(color16);
sbi(P_CS, B_CS);
clrXY();
}
// Draw a circle outline
void TeensyDisplay::drawCircle(int16_t x0, int16_t y0, int16_t r,
uint16_t color) {
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
#ifdef UPSIDEDOWN
x0 = disp_y_size - x0;
y0 = disp_x_size - y0;
#endif
drawPixel(x0 , y0+r, color);
drawPixel(x0 , y0-r, color);
drawPixel(x0+r, y0 , color);
drawPixel(x0-r, y0 , color);
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
drawPixel(x0 + x, y0 + y, color);
drawPixel(x0 - x, y0 + y, color);
drawPixel(x0 + x, y0 - y, color);
drawPixel(x0 - x, y0 - y, color);
drawPixel(x0 + y, y0 + x, color);
drawPixel(x0 - y, y0 + x, color);
drawPixel(x0 + y, y0 - x, color);
drawPixel(x0 - y, y0 - x, color);
}
}
void TeensyDisplay::drawCircleHelper( int16_t x0, int16_t y0,
int16_t r, uint8_t cornername, uint16_t color) {
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
#ifdef UPSIDEDOWN
x0 = disp_y_size - x0;
y0 = disp_x_size - y0;
#endif
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
#ifdef UPSIDEDOWN
if (cornername & 0x1) {
#else
if (cornername & 0x4) {
#endif
drawPixel(x0 + x, y0 + y, color);
drawPixel(x0 + y, y0 + x, color);
}
#ifdef UPSIDEDOWN
if (cornername & 0x8) {
#else
if (cornername & 0x2) {
#endif
drawPixel(x0 + x, y0 - y, color);
drawPixel(x0 + y, y0 - x, color);
}
#ifdef UPSIDEDOWN
if (cornername & 0x2) {
#else
if (cornername & 0x8) {
#endif
drawPixel(x0 - y, y0 + x, color);
drawPixel(x0 - x, y0 + y, color);
}
#ifdef UPSIDEDOWN
if (cornername & 0x4) {
#else
if (cornername & 0x1) {
#endif
drawPixel(x0 - y, y0 - x, color);
drawPixel(x0 - x, y0 - y, color);
}
}
}
void TeensyDisplay::fillCircle(int16_t x0, int16_t y0, int16_t r,
uint16_t color) {
drawFastVLine(x0, y0-r, 2*r+1, color);
fillCircleHelper(x0, y0, r, 3, 0, color);
}
// Used to do circles and roundrects
void TeensyDisplay::fillCircleHelper(int16_t x0, int16_t y0, int16_t r,
uint8_t cornername, int16_t delta, uint16_t color) {
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
if (cornername & 0x1) {
drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
}
if (cornername & 0x2) {
drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
}
}
}
// Bresenham's algorithm - thx wikpedia
void TeensyDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color) {
#ifdef UPSIDEDOWN
x0 = disp_y_size - x0;
y0 = disp_x_size - y0;
x1 = disp_y_size - x1;
y1 = disp_x_size - y1;
#endif
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
_swap_int16_t(x0, y0);
_swap_int16_t(x1, y1);
}
if (x0 > x1) {
_swap_int16_t(x0, x1);
_swap_int16_t(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t err = dx / 2;
int16_t ystep;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
for (; x0<=x1; x0++) {
if (steep) {
drawPixel(y0, x0, color);
} else {
drawPixel(x0, y0, color);
}
err -= dy;
if (err < 0) {
y0 += ystep;
err += dx;
}
}
}
// Draw a rectangle
void TeensyDisplay::drawRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) {
drawFastHLine(x, y, w, color);
drawFastHLine(x, y+h-1, w, color);
drawFastVLine(x, y, h, color);
drawFastVLine(x+w-1, y, h, color);
}
void TeensyDisplay::drawFastVLine(int16_t x, int16_t y,
int16_t h, uint16_t color) {
// Update in subclasses if desired!
setColor(color);
fillRect(x, y, x, y+h-1);
}
void TeensyDisplay::drawFastHLine(int16_t x, int16_t y,
int16_t w, uint16_t color) {
// Update in subclasses if desired!
setColor(color);
fillRect(x, y, x+w-1, y);
}
// Draw a rounded rectangle
void TeensyDisplay::drawRoundRect(int16_t x, int16_t y, int16_t w,
int16_t h, int16_t r, uint16_t color) {
// smarter version
drawFastHLine(x+r , y , w-2*r, color); // Top
drawFastHLine(x+r , y+h-1, w-2*r, color); // Bottom
drawFastVLine(x , y+r , h-2*r, color); // Left
drawFastVLine(x+w-1, y+r , h-2*r, color); // Right
// draw four corners
drawCircleHelper(x+r , y+r , r, 1, color);
drawCircleHelper(x+w-r-1, y+r , r, 2, color);
drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
drawCircleHelper(x+r , y+h-r-1, r, 8, color);
}
// Fill a rounded rectangle
void TeensyDisplay::fillRoundRect(int16_t x, int16_t y, int16_t w,
int16_t h, int16_t r, uint16_t color) {
// smarter version
fillRect(x+r, y, w-2*r, h, color);
// draw four corners
fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color);
}
void TeensyDisplay::LCD_Writ_Bus(uint8_t ch, uint8_t cl)
{
*(volatile uint8_t *)(&GPIOD_PDOR) = ch;
*(volatile uint8_t *)(&GPIOC_PDOR) = cl;
pulse_low(P_WR, B_WR);
}
void TeensyDisplay::LCD_Write_COM(uint8_t VL)
{
cbi(P_RS, B_RS);
LCD_Writ_Bus(0x00, VL);
}
void TeensyDisplay::LCD_Write_DATA(uint8_t VH, uint8_t VL)
{
sbi(P_RS, B_RS);
LCD_Writ_Bus(VH,VL);
}
void TeensyDisplay::LCD_Write_DATA(uint8_t VL)
{
sbi(P_RS, B_RS);
LCD_Writ_Bus(0x00, VL);
}
void TeensyDisplay::LCD_Write_COM_DATA(uint8_t com1, uint16_t dat1)
{
LCD_Write_COM(com1);
LCD_Write_DATA(dat1>>8, dat1);
}
void TeensyDisplay::moveTo(uint16_t col, uint16_t row)
{
cbi(P_CS, B_CS);
// FIXME: eventually set drawing to the whole screen and leave it that way
// set drawing to the whole screen
setYX(0, 0, disp_y_size, disp_x_size);
LCD_Write_COM_DATA(0x4e,row); // RAM address set (horiz)
LCD_Write_COM_DATA(0x4f,col); // RAM address set (vert)
LCD_Write_COM(0x22);
}
void TeensyDisplay::drawNextPixel(uint16_t color)
{
// Anything inside this object should call setPixel directly. This
// is primarily for the BIOS.
setPixel(color);
}
void TeensyDisplay::blit(AiieRect r)
{
uint8_t *videoBuffer = g_vm->videoBuffer; // FIXME: poking deep
// remember these are "starts at pixel number" values, where 0 is the first.
#define HOFFSET 18
#define VOFFSET 13
// Define the horizontal area that we're going to draw in
#ifdef UPSIDEDOWN
LCD_Write_COM_DATA(0x44, ((disp_x_size-VOFFSET-r.top)<<8)+disp_x_size-VOFFSET-r.bottom); // Horiz start addr, Horiz end addr
LCD_Write_COM_DATA(0x45, disp_y_size-HOFFSET-r.right); // offset by 20 to center it...
LCD_Write_COM_DATA(0x46, disp_y_size-HOFFSET-r.left);
#else
LCD_Write_COM_DATA(0x44, ((VOFFSET+r.top)<<8)+VOFFSET+r.bottom);
LCD_Write_COM_DATA(0x45, HOFFSET+r.left); // offset by 20 to center it...
LCD_Write_COM_DATA(0x46, HOFFSET+r.right);
#endif
// position the "write" address
#ifdef UPSIDEDOWN
LCD_Write_COM_DATA(0x4e,disp_x_size-VOFFSET-r.bottom); // row
LCD_Write_COM_DATA(0x4f,disp_y_size-HOFFSET-r.right); // col
#else
LCD_Write_COM_DATA(0x4e,VOFFSET+r.top); // row
LCD_Write_COM_DATA(0x4f,HOFFSET+r.left); // col
#endif
// prepare the LCD to receive data bytes for its RAM
LCD_Write_COM(0x22);
// send the pixel data
sbi(P_RS, B_RS);
uint16_t pixel;
#ifdef UPSIDEDOWN
for (int16_t y=r.bottom; y>=r.top; y--) {
for (int16_t x=r.right; x>=r.left; x--) {
#else
for (uint8_t y=r.top; y<=r.bottom; y++) {
for (uint16_t x=r.left; x<=r.right; x++) {
#endif
pixel = y * (DISPLAYRUN >> 1) + (x >> 1);
uint8_t colorIdx;
if (!(x & 0x01)) {
colorIdx = videoBuffer[pixel] >> 4;
} else {
colorIdx = videoBuffer[pixel] & 0x0F;
}
LCD_Writ_Bus(loresPixelColors[colorIdx]>>8,loresPixelColors[colorIdx]);
}
}
cbi(P_CS, B_CS);
// draw overlay, if any
if (overlayMessage[0]) {
// reset the viewport in order to draw the overlay...
LCD_Write_COM_DATA(0x45, 0);
LCD_Write_COM_DATA(0x46, 319);
drawString(M_SELECTDISABLED, 1, 240 - 16 - 12, overlayMessage);
}
redrawDriveIndicators();
}
void TeensyDisplay::drawThumb(int16_t x0, int16_t y0) { // draw the VM display area 1/2 size
uint8_t *videoBuffer = g_vm->videoBuffer; // FIXME: poking deep
#define HSIZE 139
#define VSIZE 95
int16_t x1 = x0 + HSIZE;
int16_t y1 = y0 + VSIZE;
cbi(P_CS, B_CS);
clrXY();
sbi(P_RS, B_RS);
// Define the horizontal area that we're going to draw in
#ifdef UPSIDEDOWN
LCD_Write_COM_DATA(0x44, ((disp_x_size-y0)<<8)+disp_x_size-y1); // Horiz start addr, Horiz end addr
LCD_Write_COM_DATA(0x45, disp_y_size-x1); // offset by 20 to center it...
LCD_Write_COM_DATA(0x46, disp_y_size-x0);
#else
LCD_Write_COM_DATA(0x44, ((y0)<<8)+y1);
LCD_Write_COM_DATA(0x45, x0); // offset by 20 to center it...
LCD_Write_COM_DATA(0x46, x1);
#endif
// position the "write" address
#ifdef UPSIDEDOWN
LCD_Write_COM_DATA(0x4e,disp_x_size-y1); // row
LCD_Write_COM_DATA(0x4f,disp_y_size-x1); // col
#else
LCD_Write_COM_DATA(0x4e,y0); // row
LCD_Write_COM_DATA(0x4f,x0); // col
#endif
// prepare the LCD to receive data bytes for its RAM
LCD_Write_COM(0x22);
// send the pixel data
sbi(P_RS, B_RS);
uint16_t pixel1, pixel2;
#ifdef UPSIDEDOWN
for (int16_t y=VSIZE; y>=0; y--) {
for (int16_t x=HSIZE; x>=0; x--) {
#else
for (uint8_t y=0; y<=VSIZE; y++) {
for (uint16_t x=0; x<=HSIZE; x++) {
#endif
pixel1 = y * DISPLAYRUN + x;
pixel2 = (y * DISPLAYRUN) + (DISPLAYRUN >> 1) + x;
uint8_t colorIdx = (videoBuffer[pixel1] >> 4) | (videoBuffer[pixel1] & 0x0F);
colorIdx |= (videoBuffer[pixel2] >> 4) | (videoBuffer[pixel2 ] & 0x0F);
LCD_Writ_Bus(loresPixelColors[colorIdx]>>8,loresPixelColors[colorIdx]);
}
}
cbi(P_CS, B_CS);
}
void TeensyDisplay::setBackground(uint16_t color) {
backgroundColor = color;
}
void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c)
{
int8_t xsize = 8,
ysize = 0x0C,
offset = 0x20;
uint16_t temp;
c -= offset;// font starts with a space
uint16_t offPixel, onPixel;
switch (mode) {
case M_NORMAL:
onPixel = WHITE;
offPixel = backgroundColor;
break;
case M_SELECTED:
onPixel = BLACK;
offPixel = WHITE;
break;
case M_DISABLED:
default:
onPixel = DARK_GREY;
offPixel = backgroundColor;
break;
case M_SELECTDISABLED:
onPixel = BLACK;
offPixel = DARK_GREY;
break;
case M_HIGHLIGHT:
onPixel = GREEN;
offPixel = backgroundColor;
break;
case M_SELECTHIGHLIGHT:
onPixel = BLACK;
offPixel = YELLOW;
break;
}
#ifdef UPSIDEDOWN
temp=(c*ysize+ysize);
for (int8_t y_off = ysize; y_off >= 0; y_off--) {
moveTo(disp_y_size-(x+xsize), disp_x_size-(y + y_off));
uint8_t ch = pgm_read_byte(&BiosFont[temp]);
for (int8_t x_off = xsize; x_off >= 0; x_off--) {
if (ch & (1 << (7-x_off))) {
setPixel(onPixel);
} else {
setPixel(offPixel);
}
}
temp--;
}
#else
temp=(c*ysize);
for (int8_t y_off = 0; y_off <= ysize; y_off++) {
moveTo(x, y + y_off);
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))) {
setPixel(onPixel);
} else {
setPixel(offPixel);
}
}
temp++;
}
#endif
}
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
for (uint8_t i=0; i<strlen(str); i++) {
drawCharacter(mode, x, y, str[i]);
x += xsize; // fixme: any inter-char spacing?
}
}
void TeensyDisplay::drawDriveDoor(uint8_t which, bool isOpen)
{
// location of drive door for left drive
uint16_t xoff = 55;
uint16_t yoff = 216;
// location for right drive
if (which == 1) {
xoff += 134;
}
for (int y=0; y<20; y++) {
for (int x=0; x<43; x++) {
uint8_t r, g, b;
if (isOpen) {
r = pgm_read_byte(&driveLatchOpen[(y * 43 + x)*3 + 0]);
g = pgm_read_byte(&driveLatchOpen[(y * 43 + x)*3 + 1]);
b = pgm_read_byte(&driveLatchOpen[(y * 43 + x)*3 + 2]);
} else {
r = pgm_read_byte(&driveLatch[(y * 43 + x)*3 + 0]);
g = pgm_read_byte(&driveLatch[(y * 43 + x)*3 + 1]);
b = pgm_read_byte(&driveLatch[(y * 43 + x)*3 + 2]);
}
#ifdef UPSIDEDOWN
drawPixel(disp_y_size - (x + xoff), disp_x_size - (y + yoff), r, g, b);
#else
drawPixel(x+xoff, y+yoff, r, g, b);
#endif
}
}
}
void TeensyDisplay::setDriveIndicator(uint8_t which, bool isRunning)
{
driveIndicator[which] = isRunning;
driveIndicatorDirty = true;
if (driveIndicator[0] || driveIndicator[1]) LEDflash();
else LED();
}
void TeensyDisplay::redrawDriveIndicators()
{
if (driveIndicatorDirty) {
// location of status indicator for left drive
uint16_t xoff = 125;
uint16_t yoff = 213;
for (int which = 0; which <= 1; which++,xoff += 135) {
for (int y=0; y<2; y++) {
for (int x=0; x<6; x++) {
#ifdef UPSIDEDOWN
drawPixel(disp_y_size - (x + xoff), disp_x_size - (y + yoff), driveIndicator[which] ? 0xF800 : 0x8AA9);
#else
drawPixel(x + xoff, y + yoff, driveIndicator[which] ? 0xF800 : 0x8AA9);
#endif
}
}
}
driveIndicatorDirty = false;
}
}
void TeensyDisplay::drawBatteryStatus(uint8_t percent)
{
uint16_t xoff = 300;
uint16_t yoff = 222;
// the area around the apple is 12 wide
// it's exactly 11 high
// the color is 210/202/159
float watermark = ((float)percent / 100.0) * 11;
for (int y=0; y<11; y++) {
uint8_t bgr = 210;
uint8_t bgg = 202;
uint8_t bgb = 159;
if (11-y > watermark) {
// black...
bgr = bgg = bgb = 0;
}
for (int x=0; x<11; x++) {
//uint8_t *p = &appleBitmap[(y * 10 + (x-1))*4];
// It's RGBA; blend w/ background color
uint8_t r,g,b;
float alpha = (float)pgm_read_byte(&appleBitmap[(y * 10 + (x-1))*4 + 3]) / 255.0;
r = (float)pgm_read_byte(&appleBitmap[(y * 10 + (x-1))*4 + 0])
* alpha + (bgr * (1.0 - alpha));
g = (float)pgm_read_byte(&appleBitmap[(y * 10 + (x-1))*4 + 1])
* alpha + (bgr * (1.0 - alpha));
b = (float)pgm_read_byte(&appleBitmap[(y * 10 + (x-1))*4 + 2])
* alpha + (bgr * (1.0 - alpha));
#ifdef UPSIDEDOWN
drawPixel(disp_y_size - (x + xoff), disp_x_size - (y + yoff), r, g, b);
#else
drawPixel(x+xoff, y+yoff, r, g, b);
#endif
}
}
}