first pass at Teensy 4.1 support

This commit is contained in:
Jorj Bauer 2020-07-06 07:04:22 -04:00
parent e8b77c8aff
commit c9fe8edc29
9 changed files with 106 additions and 698 deletions

View File

@ -2,7 +2,9 @@
#define __NIBUTIL_H
#include <unistd.h>
#ifndef TEENSYDUINO
#include <fcntl.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>

View File

@ -1,218 +0,0 @@
#include "parallelsram.h"
// Assumes any Output Enable pin is hardwired-enabled;
// any Chip Enable pin is hardwared-enabled.
//
// Uses the low 8 bits of Port D as I/O lines (2, 14, 7, 8, 6, 20, 21, 5).
//
// R/W (aka WriteEnable) is on pin 31.
#define RAM_RW 34
// The Address pins (19 of them). It would be nice to have these
// easily bitwise-manipulable, instead of having to set each bit
// individually.
//
// We can use 12 bits of Port C: 15, 22, 23, 9, 10, 13, 11, 12, 35, 36, 37, 38
// And then 6 bits of Port B: 16 17 19 18 49 50
//
// And hard wire one bit low (we don't need all 19 lines). That gets us
// 256 Kb of RAM which should be sufficient.
static uint8_t addrPins[] = { 15, 22, 23, 9, 10, 13, 11, 12, 35, 36, 37, 38,
16, 17, 19, 18, 49, 50
};
#if 0
#define DELAY { delayMicroseconds(1); /* overkill, but useful for debugging */ }
#else
#define DELAY { __asm__ volatile ("nop"); __asm__ volatile ("nop"); \
__asm__ volatile ("nop"); __asm__ volatile ("nop"); \
__asm__ volatile ("nop"); __asm__ volatile ("nop"); \
__asm__ volatile ("nop"); __asm__ volatile ("nop"); \
}
#endif
#define OE_ON { /*if (noe != 255) {digitalWrite(noe, LOW);}*/ }
#define OE_OFF { /*if (noe != 255) {digitalWrite(noe, HIGH);}*/ }
#define CE_ON { /*if (n_ce != 255) {digitalWrite(n_ce, LOW);} if (p_ce != 255) { digitalWrite(p_ce, HIGH); }*/ }
#define CE_OFF { /*if (n_ce != 255) {digitalWrite(n_ce, HIGH);} if (p_ce != 255) { digitalWrite(p_ce, LOW); }*/ }
#define WE_ON { digitalWriteFast(RAM_RW, LOW); }
#define WE_OFF { digitalWriteFast(RAM_RW, HIGH); }
ParallelSRAM::ParallelSRAM()
{
pinMode(RAM_RW, OUTPUT);
// Port D is our I/O port. Use the AVR emulation layer to set up the
// pins once, and then we'll just fiddle with the DDR, input, and
// output directly.
// Enable it as a digital port...
// SIM_SCGC5 |= SIM_SCGC5_PORTD;
//... what else? How do we set PORTD_PCR[0-7]?
pinMode(2, INPUT);
pinMode(14, INPUT);
pinMode(7, INPUT);
pinMode(8, INPUT);
pinMode(6, INPUT);
pinMode(20, INPUT);
pinMode(21, INPUT);
pinMode(5, INPUT);
isInput = true;
// Set up the address pins
for (int i=0; i<sizeof(addrPins); i++) {
pinMode(addrPins[i], INPUT); // disable pull-ups
pinMode(addrPins[i], OUTPUT);
digitalWrite(addrPins[i], LOW);
}
}
void ParallelSRAM::SetPins()
{
pinMode(RAM_RW, OUTPUT);
// Port D is our I/O port. Use the AVR emulation layer to set up the
// pins once, and then we'll just fiddle with the DDR, input, and
// output directly.
// Enable it as a digital port...
// SIM_SCGC5 |= SIM_SCGC5_PORTD;
//... what else? How do we set PORTD_PCR[0-7]?
pinMode(2, INPUT);
pinMode(14, INPUT);
pinMode(7, INPUT);
pinMode(8, INPUT);
pinMode(6, INPUT);
pinMode(20, INPUT);
pinMode(21, INPUT);
pinMode(5, INPUT);
isInput = true;
// Set up the address pins
for (int i=0; i<sizeof(addrPins); i++) {
pinMode(addrPins[i], INPUT); // disable pull-ups
pinMode(addrPins[i], OUTPUT);
digitalWrite(addrPins[i], LOW);
}
}
ParallelSRAM::~ParallelSRAM()
{
}
uint8_t ParallelSRAM::read(uint32_t addr)
{
cli();
// Read cycle 2
setAddress(addr);
// make sure address is valid before CE is asserted
DELAY;
CE_ON;
OE_ON;
DELAY;
uint8_t ret = getInput();
// Optional; can leave these lines asserted...
OE_OFF;
CE_OFF;
sei();
return ret;
}
void ParallelSRAM::write(uint32_t addr, uint8_t v)
{
cli();
setAddress(addr);
DELAY;
WE_ON;
CE_ON;
setOutput(v);
DELAY;
CE_OFF;
WE_OFF;
sei();
}
uint8_t ParallelSRAM::getInput()
{
if (!isInput) {
#if 1
// Directly set the direction bits. The rest of the port setup
// should be fine from the initial config.
*(volatile uint8_t *)(&GPIOD_PDDR) = 0x00; // inputs
#else
pinMode(2, INPUT);
pinMode(14, INPUT);
pinMode(7, INPUT);
pinMode(8, INPUT);
pinMode(6, INPUT);
pinMode(20, INPUT);
pinMode(21, INPUT);
pinMode(5, INPUT);
#endif
isInput = true;
}
return GPIOD_PDIR & 0xFF;
}
void ParallelSRAM::setOutput(uint8_t v)
{
// FIXME: is there a faster way to do this?
if (isInput) {
#if 1
// FIMXE: would this be correct?
*(volatile uint8_t *)(&GPIOD_PDDR) |= 0xFF; // outputs
#else
pinMode(2, OUTPUT);
pinMode(14, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(6, OUTPUT);
pinMode(20, OUTPUT);
pinMode(21, OUTPUT);
pinMode(5, OUTPUT);
#endif
isInput = false;
}
// Directly set the low 8 bits of D.
*(volatile uint8_t *)(&GPIOD_PDOR) = v;
}
void ParallelSRAM::setAddress(uint32_t addr)
{
// The low 12 bits of the address go right in to Port C. Set these
// by doing a clear of the bitmask, and then set the bits...
GPIOC_PCOR = 0x00000FFF;
GPIOC_PSOR = (addr & 0xFFF);
// The high 6 bits of the address go in to Port B, bits 0..5.
// We do that the same way...
GPIOB_PCOR = 0x0000003F;
GPIOB_PSOR = (addr >> 12);
#if 0
for (uint8_t i=0; i<sizeof(addrPins); i++) {
digitalWrite(addrPins[i],
addr & (1 << i) ? HIGH : LOW);
}
#endif
}

View File

@ -1,25 +0,0 @@
#ifndef __PARALLELSRAM_H
#define __PARALLELSRAM_H
#include <Arduino.h>
class ParallelSRAM {
public:
ParallelSRAM();
~ParallelSRAM();
void SetPins();
uint8_t read(uint32_t addr);
void write(uint32_t addr, uint8_t v);
protected:
uint8_t getInput();
void setOutput(uint8_t v);
void setAddress(uint32_t addr);
private:
bool isInput;
};
#endif

View File

@ -4,208 +4,91 @@
#include "bios-font.h"
#include "appleui.h"
#define RS 16
#define WR 17
#define CS 18
#define RST 19
#define _clock 65000000
// 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 PIN_RST 8
#define PIN_DC 9
#define PIN_CS 10
#define PIN_MOSI 11
#define PIN_MISO 12
#define PIN_SCK 13
#define disp_x_size 239
#define disp_y_size 319
#define setPixel(color) { LCD_Write_DATA(((color)>>8),((color)&0xFF)); } // 565 RGB
#include "globals.h"
#include "applevm.h"
ILI9341_t3 tft = ILI9341_t3(PIN_CS, PIN_DC, PIN_RST, PIN_MOSI, PIN_SCK, PIN_MISO);
// RGB map of each of the lowres colors
const uint8_t loresPixelColors[16*2] = { 0x00,0x00, // 0 black
0xC0,0x06, // 1 magenta
0x00,0x10, // 2 dark blue
0xA1,0xB5, // 3 purple
0x04,0x80, // 4 dark green
0x6B,0x4D, // 5 dark grey
0x1B,0x9F, // 6 med blue
0x0D,0xFD, // 7 light blue
0x92,0xA5, // 8 brown
0xF8,0xC5, // 9 orange
0x95,0x55, // 10 light gray
0xFC,0xF2, // 11 pink
0x07,0xE0, // 12 green
0xFF,0xE0, // 13 yellow
0x87,0xF0, // 14 aqua
0xFF,0xFF // 15 white
const uint16_t loresPixelColors[16] = { 0x0000, // 0 black
0xC006, // 1 magenta
0x0010, // 2 dark blue
0xA1B5, // 3 purple
0x0480, // 4 dark green
0x6B4D, // 5 dark grey
0x1B9F, // 6 med blue
0x0DFD, // 7 light blue
0x92A5, // 8 brown
0xF8C5, // 9 orange
0x9555, // 10 light gray
0xFCF2, // 11 pink
0x07E0, // 12 green
0xFFE0, // 13 yellow
0x87F0, // 14 aqua
0xFFFF // 15 white
};
const uint8_t loresPixelColorsGreen[16*2] = { 0x00, 0x00,
0x01, 0x40,
0x00, 0x40,
0x02, 0x80,
0x03, 0x00,
0x03, 0x40,
0x03, 0x00,
0x04, 0x80,
0x02, 0xC0,
0x02, 0x40,
0x05, 0x00,
0x05, 0x40,
0x05, 0x80,
0x07, 0x00,
0x06, 0x80,
0x07, 0xC0
const uint16_t loresPixelColorsGreen[16] = { 0x0000,
0x0140,
0x0040,
0x0280,
0x0300,
0x0340,
0x0300,
0x0480,
0x02C0,
0x0240,
0x0500,
0x0540,
0x0580,
0x0700,
0x0680,
0x07C0
};
const uint8_t loresPixelColorsWhite[16*2] = { 0x00, 0x00,
0x29, 0x45,
0x08, 0x41,
0x52, 0x8A,
0x63, 0x0C,
0x6B, 0x4D,
0x63, 0x0C,
0x94, 0x92,
0x5A, 0xCB,
0x4A, 0x49,
0xA5, 0x14,
0xAD, 0x55,
0xB5, 0x96,
0xE7, 0x1C,
0xD6, 0x9A,
0xFF, 0xDF
const uint16_t loresPixelColorsWhite[16] = { 0x0000,
0x2945,
0x0841,
0x528A,
0x630C,
0x6B4D,
0x630C,
0x9492,
0x5ACB,
0x4A49,
0xA514,
0xAD55,
0xB596,
0xE71C,
0xD69A,
0xFFDF
};
TeensyDisplay::TeensyDisplay()
{
memset(videoBuffer, 0, sizeof(videoBuffer));
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);
// Setup here is from the document "Driver IC SSD1289.pdf"
// https://forum.allaboutcircuits.com/attachments/driver-ic-ssd1289-pdf.71570/
LCD_Write_COM_DATA(0x00,0x0001); // R00h: enable the oscillator
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
// This sets the direction of the scan. These two are mirror
// opposites. The first is right in my setup.
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,0x5308); // frame cycle control: %0101 0011 0000 1000
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 1
// Set data access speed optimization (?) per pg. 50; doesn't actually seem to change anything though?
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);
tft.begin();
tft.setRotation(3);
tft.setClock(_clock);
// Could set up an automatic DMA transfer here; cf.
// https://forum.pjrc.com/threads/25778-Could-there-be-something-like-an-ISR-template-function/page4
// LCD initialization complete
setColor(255, 255, 255);
clrScr();
driveIndicator[0] = driveIndicator[1] = false;
@ -216,196 +99,37 @@ TeensyDisplay::~TeensyDisplay()
{
}
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);
g_ui->drawStaticUIElement(UIeOverlay);
if (g_vm) {
g_ui->drawOnOffUIElement(UIeDisk1_state, ((AppleVM *)g_vm)->DiskName(0)[0] == '\0');
g_ui->drawOnOffUIElement(UIeDisk2_state, ((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);
}
// 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)
{
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::drawPixel(uint16_t x, uint16_t y)
{
cbi(P_CS, B_CS);
setYX(x, y, x, y);
setPixel((fch<<8)|fcl);
sbi(P_CS, B_CS);
clrXY();
// FIXME: only fill the area that's got our "terminal"
tft.fillScreen(ILI9341_BLACK);
}
void TeensyDisplay::drawUIPixel(uint16_t x, uint16_t y, uint16_t color)
{
drawPixel(x,y,color);
tft.drawPixel(x,y,color);
}
void TeensyDisplay::drawPixel(uint16_t x, uint16_t y, uint16_t color)
{
cbi(P_CS, B_CS);
setYX(x, y, x, y);
setPixel(color);
sbi(P_CS, B_CS);
clrXY();
tft.drawPixel(x,y,color);
}
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);
cbi(P_CS, B_CS);
setYX(x, y, x, y);
setPixel(color16);
sbi(P_CS, B_CS);
clrXY();
}
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);
drawPixel(x,y,color16);
}
void TeensyDisplay::blit(AiieRect r)
@ -413,20 +137,7 @@ void TeensyDisplay::blit(AiieRect r)
// 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
LCD_Write_COM_DATA(0x45, HOFFSET+r.left); // offset by 20 to center it...
LCD_Write_COM_DATA(0x46, HOFFSET+r.right);
// position the "write" address
LCD_Write_COM_DATA(0x4e,VOFFSET+r.top); // row
LCD_Write_COM_DATA(0x4f,HOFFSET+r.left); // col
// prepare the LCD to receive data bytes for its RAM
LCD_Write_COM(0x22);
// send the pixel data
sbi(P_RS, B_RS);
uint8_t *vbufPtr;
for (uint8_t y=r.top; y<=r.bottom; y++) {
vbufPtr = &videoBuffer[y * TEENSY_DRUN + r.left];
@ -440,19 +151,17 @@ void TeensyDisplay::blit(AiieRect r)
}
colorIdx <<= 1;
// The colors are broken up in to two 8-bit values to speed things up.
const uint8_t *p;
uint16_t c;
if (g_displayType == m_monochrome) {
p = &loresPixelColorsGreen[colorIdx];
c = loresPixelColorsGreen[colorIdx];
}
else if (g_displayType == m_blackAndWhite) {
p = &loresPixelColorsWhite[colorIdx];
c = loresPixelColorsWhite[colorIdx];
} else {
p = &loresPixelColors[colorIdx];
c = loresPixelColors[colorIdx];
}
LCD_Writ_Bus(*p, *(p+1));
drawPixel(x+HOFFSET,y+VOFFSET,c);
if (x & 0x01) {
// When we do the odd pixels, then move the pixel pointer to the next pixel
@ -460,14 +169,9 @@ void TeensyDisplay::blit(AiieRect r)
}
}
}
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);
}
}
@ -504,30 +208,17 @@ void TeensyDisplay::drawCharacter(uint8_t mode, uint16_t x, uint8_t y, char c)
temp=(c*ysize);
// FIXME: the embedded moveTo() and setPixel() calls *should* work
// -- and do, for the most part. But in the BIOS they cut off after
// about half the screen. Using drawPixel() is substantially less
// efficient, but works properly.
for (int8_t y_off = 0; y_off <= ysize; y_off++) {
//moveTo(x, y + y_off); // does a cbi(P_CS, B_CS)
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);
//setPixel(onPixel);
} else {
drawPixel(x+x_off, y+y_off, offPixel);
//setPixel(offPixel);
}
}
temp++;
}
// Need to leave cbi set for the next draw operation. Particularly important
// on startup, when transitioning from '@' to 'Apple //e', while also drawing
// overlay text.
cbi(P_CS, B_CS);
}
void TeensyDisplay::drawString(uint8_t mode, uint16_t x, uint8_t y, const char *str)
@ -546,19 +237,12 @@ void TeensyDisplay::drawImageOfSizeAt(const uint8_t *img,
{
uint8_t r, g, b;
if (sizex == DISPLAYWIDTH) {
moveTo(0,0);
}
for (uint8_t y=0; y<sizey; y++) {
if (sizex != DISPLAYWIDTH) {
moveTo(wherex, wherey + y);
}
for (uint16_t x=0; x<sizex; x++) {
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]);
setPixel((((r&248)|g>>5) << 8) | ((g&28)<<3|b>>3));
drawPixel(wherex+x, wherey+y, (((r&248)|g>>5) << 8) | ((g&28)<<3|b>>3));
}
}
}
@ -616,4 +300,3 @@ void TeensyDisplay::cachePixel(uint16_t x, uint16_t y, uint8_t color)
cacheDoubleWidePixel(x/2, y, color);
}
}

View File

@ -2,6 +2,7 @@
#define __TEENSY_DISPLAY_H
#include <Arduino.h>
#include <ILI9341_t3.h>
#include "physicaldisplay.h"
#define TEENSY_DHEIGHT 192
@ -12,16 +13,6 @@
#define regtype volatile uint8_t
#define regsize uint8_t
#define cbi(reg, bitmask) *reg &= ~bitmask
#define sbi(reg, bitmask) *reg |= bitmask
#define pulse_high(reg, bitmask) { sbi(reg, bitmask); cbi(reg, bitmask); }
#define pulse_low(reg, bitmask) { cbi(reg, bitmask); sbi(reg, bitmask); }
#define cport(port, data) port &= data
#define sport(port, data) port |= data
#define swap(type, i, j) {type t = i; i = j; j = t;}
class UTFT;
class BIOS;
@ -47,40 +38,15 @@ class TeensyDisplay : public PhysicalDisplay {
virtual void cache2DoubleWidePixels(uint16_t x, uint16_t y, uint8_t colorA, uint8_t colorB);
virtual void cachePixel(uint16_t x, uint16_t y, uint8_t color);
protected:
void moveTo(uint16_t col, uint16_t row);
void drawNextPixel(uint16_t color);
private:
regtype *P_RS, *P_WR, *P_CS, *P_RST, *P_SDA, *P_SCL, *P_ALE;
regsize B_RS, B_WR, B_CS, B_RST, B_SDA, B_SCL, B_ALE;
uint8_t fch, fcl; // high and low foreground colors
void _fast_fill_16(int ch, int cl, long pix);
void setYX(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void clrXY();
void setColor(byte r, byte g, byte b);
void setColor(uint16_t color);
void fillRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
virtual void drawPixel(uint16_t x, uint16_t y);
virtual void drawPixel(uint16_t x, uint16_t y, uint16_t color);
virtual void drawPixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b);
virtual void drawUIPixel(uint16_t x, uint16_t y, uint16_t color);
inline void LCD_Writ_Bus(uint8_t VH,uint8_t VL) __attribute__((always_inline));
inline void LCD_Write_COM(uint8_t VL) __attribute__((always_inline));
inline void LCD_Write_DATA(uint8_t VH,uint8_t VL) __attribute__((always_inline));
inline void LCD_Write_DATA(uint8_t VL) __attribute__((always_inline));
inline void LCD_Write_COM_DATA(uint8_t com1,uint16_t dat1) __attribute__((always_inline));
bool needsRedraw;
bool driveIndicator[2];
bool driveIndicatorDirty;
// video buffer is 4bpp
uint8_t videoBuffer[TEENSY_DHEIGHT * TEENSY_DWIDTH / 2];
};

View File

@ -1,6 +1,5 @@
#include <Arduino.h>
#include <wchar.h>
#include <SdFat.h>
#include "teensy-filemanager.h"
#include <string.h> // strcpy
#include <TeensyThreads.h>
@ -15,7 +14,7 @@ TeensyFileManager::TeensyFileManager()
// FIXME: used to have 'enabled = sd.begin()' here, but we weren't
// using the enabled flag, so I've removed it to save the RAM for
// now; but eventually we need better error handling here
sd.begin();
SD.begin(BUILTIN_SDCARD);
}
TeensyFileManager::~TeensyFileManager()
@ -94,7 +93,7 @@ int8_t TeensyFileManager::readDir(const char *where, const char *suffix, char *o
}
int8_t idxCount = 1;
File f = sd.open(where);
File f = SD.open(where, FILE_READ);
while (1) {
File e = f.openNextFile();
@ -105,7 +104,8 @@ int8_t TeensyFileManager::readDir(const char *where, const char *suffix, char *o
}
// Skip MAC fork files
e.getName(outputFN, maxlen-1); // -1 for trailing '/' on directories
// FIXME: strncpy
strcpy(outputFN, e.name()); // and we need maxlen-1 for trailing '/' on directories
if (outputFN[0] == '.') {
e.close();
continue;
@ -166,8 +166,8 @@ bool TeensyFileManager::_prepCache(int8_t fd)
}
// Open the new one
cacheFile = sd.open(cachedNames[fd], O_RDWR | O_CREAT);
if (!cacheFile.isOpen()) {
cacheFile = SD.open(cachedNames[fd], FILE_WRITE);
if (!cacheFile) {
return false;
}
cacheFd = fd; // cache is live
@ -198,12 +198,12 @@ bool TeensyFileManager::setSeekPosition(int8_t fd, uint32_t pos)
// FIXME: this should be private
void TeensyFileManager::seekToEnd(int8_t fd)
{
FatFile f = sd.open(cachedNames[fd], FILE_READ);
if (!f.isOpen()) {
File f = SD.open(cachedNames[fd], FILE_READ);
if (!f) {
return;
}
fileSeekPositions[fd] = f.fileSize();
fileSeekPositions[fd] = f.size();
f.close();
}
@ -223,11 +223,11 @@ int TeensyFileManager::write(int8_t fd, const void *buf, int nbyte)
uint32_t pos = fileSeekPositions[fd];
if (!cacheFile.seekSet(pos)) {
if (!cacheFile.seek(pos)) {
return -1;
}
if (cacheFile.write(buf, nbyte) != nbyte) {
if (cacheFile.write((const uint8_t *)buf, (size_t)nbyte) != (size_t)nbyte) {
return -1;
}
@ -251,7 +251,7 @@ int TeensyFileManager::read(int8_t fd, void *buf, int nbyte)
_prepCache(fd);
uint32_t pos = fileSeekPositions[fd];
if (!cacheFile.seekSet(pos)) {
if (!cacheFile.seek(pos)) {
return -1;
}
fileSeekPositions[fd] += nbyte;

View File

@ -3,7 +3,8 @@
#include "filemanager.h"
#include <stdint.h>
#include <SdFat.h>
#include <SD.h>
#include <SPI.h>
class TeensyFileManager : public FileManager {
public:
@ -30,11 +31,10 @@ class TeensyFileManager : public FileManager {
bool _prepCache(int8_t fd);
private:
volatile int8_t numCached;
int8_t numCached;
volatile SdFatSdio sd;
volatile int8_t cacheFd;
volatile FatFile cacheFile;
int8_t cacheFd;
File cacheFile;
};
#endif

View File

@ -23,7 +23,8 @@ void TeensySpeaker::toggle(uint32_t c)
mixerValue >>= (16-g_volume);
// FIXME: glad it's DAC0 and all, but... how does that relate to the pin passed in the constructor?
analogWriteDAC0(mixerValue);
// FIXME: really doesn't work for the Teensy 4 at all
// analogWriteDAC0(mixerValue);
}
void TeensySpeaker::maintainSpeaker(uint32_t c, uint64_t runtimeInMicros)

View File

@ -20,7 +20,7 @@
#define RESETPIN 39
#define BATTERYPIN 32
#define SPEAKERPIN A21
#define SPEAKERPIN 19 // FIXME this isn't right
#include "globals.h"
#include "teensy-crash.h"
@ -35,13 +35,6 @@ BIOS bios;
static time_t getTeensy3Time() { return Teensy3Clock.get(); }
#define ESP_TXD 51
#define ESP_CHPD 52
#define ESP_RST 53
#define ESP_RXD 40
#define ESP_GPIO0 41
#define ESP_GPIO2 42
void setup()
{
Serial.begin(230400);
@ -65,6 +58,8 @@ void setup()
pinMode(RESETPIN, INPUT);
digitalWrite(RESETPIN, HIGH);
println("FIXME: skipping analogReference, speaker, battery for Teensy 4");
/*
analogReference(EXTERNAL); // 3.3v external, or 1.7v internal. We need 1.7 internal for the battery level, which means we're gonna have to do something about the paddles :/
analogReadRes(8); // We only need 8 bits of resolution (0-255) for battery & paddles
analogReadAveraging(4); // ?? dunno if we need this or not.
@ -72,6 +67,7 @@ void setup()
pinMode(SPEAKERPIN, OUTPUT); // analog speaker output, used as digital volume control
pinMode(BATTERYPIN, INPUT);
*/
println("creating virtual hardware");
g_speaker = new TeensySpeaker(SPEAKERPIN);
@ -111,7 +107,7 @@ void setup()
g_keyboard = new TeensyKeyboard(g_vm->getKeyboard());
println(" paddles");
g_paddles = new TeensyPaddles(A23, A24, 1, 1);
g_paddles = new TeensyPaddles(A3, A4, 1, 1);
// Now that all the virtual hardware is glued together, reset the VM
println("Resetting VM");
@ -293,9 +289,12 @@ void loop()
// This is a bit disruptive - but the external 3.3v will drop along with the battery level, so we should use the more stable (I hope) internal 1.7v.
// The alternative is to build a more stable buck/boost regulator for reference...
println("FIXME: analogReference for Teensy 4.0 => batteryLevel");
/*
analogReference(INTERNAL);
batteryLevel = analogRead(BATTERYPIN);
analogReference(EXTERNAL);
analogReference(EXTERNAL);*/
/* LiIon charge to a max of 4.2v; and we should not let them discharge below about 3.5v.
* With a resistor voltage divider of Z1=39k, Z2=10k we're looking at roughly 20.4% of