mac-floppy-emu/floppy_emu_arduino/noklcd.cpp

351 lines
8.6 KiB
C++
Executable File

/*
Floppy Emu, copyright 2013 Steve Chamberlin, "Big Mess o' Wires". All rights reserved.
Floppy Emu is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported
license. (CC BY-NC 3.0) The terms of the license may be viewed at
http://creativecommons.org/licenses/by-nc/3.0/
Based on a work at http://www.bigmessowires.com/macintosh-floppy-emu/
Permissions beyond the scope of this license may be available at www.bigmessowires.com
or from mailto:steve@bigmessowires.com.
*/
#include <math.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <util/atomic.h>
#include <util/delay.h>
#include <string.h>
#include "noklcd.h"
#include "portmacros.h"
#define LCD_RESET_PORT B
#define LCD_RESET_PIN 0
#define LCD_CS_PORT B
#define LCD_CS_PIN 2
#define SPI_CLK_PORT B
#define SPI_CLK_PIN 7
#define SPI_DATA_PORT B
#define SPI_DATA_PIN 5
#define SPI_DC_PORT D
#define SPI_DC_PIN 6
volatile uint8_t lcd_vop;
volatile uint8_t lcd_bias;
volatile uint8_t lcd_tempCoef;
extern const uint8_t tiny_font[][3] PROGMEM;
const uint8_t tiny_font[][3] = {
{0x00,0x00,0x00}, // 20
{0x00,0x17,0x00}, // 21 !
{0x03,0x00,0x03}, // 22 "
{0x1f,0x0a,0x1f}, // 23 #
{0x0a,0x1f,0x05}, // 24 $
{0x09,0x04,0x12}, // 25 %
{0x0f,0x17,0x1c}, // 26 &
{0x00,0x03,0x03}, // 27 ' -> degree
{0x00,0x0e,0x11}, // 28 (
{0x11,0x0e,0x00}, // 29 )
{0x05,0x02,0x05}, // 2a *
{0x04,0x0e,0x04}, // 2b +
{0x10,0x08,0x00}, // 2c ,
{0x04,0x04,0x04}, // 2d -
{0x00,0x10,0x00}, // 2e .
{0x18,0x04,0x03}, // 2f /
{0x1e,0x11,0x0f}, // 30 0
{0x02,0x1f,0x00}, // 31 1
{0x19,0x15,0x12}, // 32 2
{0x15,0x15,0x0a}, // 33 3
{0x07,0x04,0x1f}, // 34 4
{0x17,0x15,0x09}, // 35 5
{0x1e,0x15,0x1d}, // 36 6
{0x19,0x05,0x03}, // 37 7
{0x1a,0x15,0x0b}, // 38 8
{0x17,0x15,0x0f}, // 39 9
{0x00,0x0a,0x00}, // 3a :
{0x04,0x0c,0x04}, // 3b ; -> down arrow
{0x04,0x0e,0x1f}, // 3c <
{0x0a,0x0a,0x0a}, // 3d =
{0x1f,0x0e,0x04}, // 3e >
{0x01,0x15,0x03}, // 3f ?
{0x0e,0x15,0x16}, // 40 @
{0x1e,0x05,0x1e}, // 41 A
{0x1f,0x15,0x0a}, // 42 B
{0x0e,0x11,0x11}, // 43 C
{0x1f,0x11,0x0e}, // 44 D
{0x1f,0x15,0x15}, // 45 E
{0x1f,0x05,0x05}, // 46 F
{0x0e,0x15,0x1d}, // 47 G
{0x1f,0x04,0x1f}, // 48 H
{0x11,0x1f,0x11}, // 49 I
{0x08,0x10,0x0f}, // 4a J
{0x1f,0x04,0x1b}, // 4b K
{0x1f,0x10,0x10}, // 4c L
{0x1f,0x06,0x1f}, // 4d M
{0x1f,0x0e,0x1f}, // 4e N
{0x0e,0x11,0x0e}, // 4f O
{0x1f,0x05,0x02}, // 50 P
{0x0e,0x19,0x1e}, // 51 Q
{0x1f,0x0d,0x16}, // 52 R
{0x12,0x15,0x09}, // 53 S
{0x01,0x1f,0x01}, // 54 T
{0x0f,0x10,0x1f}, // 55 U
{0x07,0x18,0x07}, // 56 V
{0x1f,0x0c,0x1f}, // 57 W
{0x1b,0x04,0x1b}, // 58 X
{0x03,0x1c,0x03}, // 59 Y
{0x19,0x15,0x13}, // 5a Z
{0x1f,0x10,0x10}, // 5b [
{0x02,0x04,0x08}, // 5c backslash
{0x10,0x10,0x1f}, // 5d ]
{0x04,0x06,0x04}, // 5e ^
{0x10,0x10,0x10}, // 5f _
{0x00,0x03,0x03}, // 60 ` -> degree
{0x1a,0x16,0x1c}, // 61 a
{0x1f,0x12,0x0c}, // 62 b
{0x0c,0x12,0x12}, // 63 c
{0x0c,0x12,0x1f}, // 64 d
{0x0c,0x1a,0x16}, // 65 e
{0x04,0x1e,0x05}, // 66 f
{0x0c,0x2a,0x1e}, // 67 g
{0x1f,0x02,0x1c}, // 68 h
{0x00,0x1d,0x00}, // 69 i
{0x10,0x20,0x1d}, // 6a j
{0x1f,0x0c,0x12}, // 6b k
{0x11,0x1f,0x10}, // 6c l
{0x1e,0x0e,0x1e}, // 6d m
{0x1e,0x02,0x1c}, // 6e n
{0x0c,0x12,0x0c}, // 6f o
{0x3e,0x12,0x0c}, // 70 p
{0x0c,0x12,0x3e}, // 71 q
{0x1c,0x02,0x02}, // 72 r
{0x14,0x1e,0x0a}, // 73 s
{0x02,0x1f,0x12}, // 74 t
{0x0e,0x10,0x1e}, // 75 u
{0x0e,0x18,0x0e}, // 76 v
{0x1e,0x1c,0x1e}, // 77 w
{0x12,0x0c,0x12}, // 78 x
{0x06,0x28,0x1e}, // 79 y
{0x1a,0x1f,0x16}, // 7a z
{0x04,0x1b,0x11}, // 7b {
{0x00,0x1f,0x00}, // 7c |
{0x11,0x1b,0x04}, // 7d }
{0x08,0x0c,0x04}, // 7e ~
};
void LcdWrite(uint8_t dc, uint8_t data)
{
// set SPI speed to "half speed": clock speed / 4
SPCR = (1 << SPE) | (1 << MSTR);
SPSR = 0;
if (dc)
{
PORT(SPI_DC_PORT) |= (1<<SPI_DC_PIN);
}
else
{
PORT(SPI_DC_PORT) &= ~(1<<SPI_DC_PIN);
}
PORT(LCD_CS_PORT) &= ~(1<<LCD_CS_PIN);
SPDR = data;
while(!(SPSR & (1<<SPIF)))
{}
PORT(LCD_CS_PORT) |= (1<<LCD_CS_PIN);
}
void LcdGoto(uint8_t x, uint8_t y)
{
LcdWrite(LCD_CMD, 0x80 | x);
LcdWrite(LCD_CMD, 0x40 | y);
}
void LcdTinyString(const char *characters, uint8_t inverse, uint8_t maxWidth)
{
uint8_t width = 0;
while (*characters && width < maxWidth)
{
if (*characters == 'm')
{
// special case 'm'
characters++;
if (width < maxWidth)
{
LcdWrite(LCD_DATA, inverse ? 0x3c ^ 0x7F : 0x3c);
width++;
}
if (width < maxWidth)
{
LcdWrite(LCD_DATA, inverse ? 0x04 ^ 0x7F : 0x04);
width++;
}
if (width < maxWidth)
{
LcdWrite(LCD_DATA, inverse ? 0x18 ^ 0x7F : 0x18);
width++;
}
if (width < maxWidth)
{
LcdWrite(LCD_DATA, inverse ? 0x04 ^ 0x7F : 0x04);
width++;
}
if (width < maxWidth)
{
LcdWrite(LCD_DATA, inverse ? 0x38 ^ 0x7F : 0x38);
width++;
}
if (width < maxWidth)
{
LcdWrite(LCD_DATA, inverse ? 0x7F : 0x00);
width++;
}
}
else
{
unsigned short charbase = (*characters++ - 0x20) * 3;
for (uint8_t index = 0; index < 3 && width < maxWidth; index++)
{
uint8_t pixels = pgm_read_byte((unsigned char*)tiny_font + charbase + index);
pixels = pixels << 1;
LcdWrite(LCD_DATA, inverse ? pixels ^ 0x7F : pixels);
width++;
}
if (width < maxWidth)
{
LcdWrite(LCD_DATA, inverse ? 0x7F : 0x00);
width++;
}
}
}
}
void LcdTinyStringP(PGM_P characters, uint8_t inverse)
{
while (pgm_read_byte(characters))
{
if (pgm_read_byte(characters) == 'm')
{
// special case 'm'
characters++;
LcdWrite(LCD_DATA, inverse ? 0x3c ^ 0x7F : 0x3c);
LcdWrite(LCD_DATA, inverse ? 0x04 ^ 0x7F : 0x04);
LcdWrite(LCD_DATA, inverse ? 0x18 ^ 0x7F : 0x18);
LcdWrite(LCD_DATA, inverse ? 0x04 ^ 0x7F : 0x04);
LcdWrite(LCD_DATA, inverse ? 0x38 ^ 0x7F : 0x38);
LcdWrite(LCD_DATA, inverse ? 0x7F : 0x00);
}
else
{
unsigned short charbase = (pgm_read_byte(characters) - 0x20) * 3;
characters++;
for (uint8_t index = 0; index < 3; index++)
{
uint8_t pixels = pgm_read_byte((unsigned char*)tiny_font + charbase + index);
pixels = pixels << 1;
LcdWrite(LCD_DATA, inverse ? pixels ^ 0x7F : pixels);
}
LcdWrite(LCD_DATA, inverse ? 0x7F : 0x00);
}
}
}
void LcdTinyStringFramed(const char *characters)
{
while (*characters)
{
if (*characters == 'm')
{
// special case 'm'
characters++;
LcdWrite(LCD_DATA, (0x3c >> 1) | 0x40);
LcdWrite(LCD_DATA, (0x04 >> 1) | 0x40);
LcdWrite(LCD_DATA, (0x18 >> 1) | 0x40);
LcdWrite(LCD_DATA, (0x04 >> 1) | 0x40);
LcdWrite(LCD_DATA, (0x38 >> 1) | 0x40);
LcdWrite(LCD_DATA, (0x00 >> 1) | 0x40);
}
else
{
unsigned short charbase = (*characters++ - 0x20) * 3;
for (uint8_t index = 0; index < 3; index++)
{
uint8_t pixels = pgm_read_byte((unsigned char*)tiny_font + charbase + index);
//pixels = pixels << 2;
pixels |= 0x40;
LcdWrite(LCD_DATA, pixels);
}
LcdWrite(LCD_DATA, 0x40);
}
}
}
void LcdClear(void)
{
for (int index = 0; index < LCD_WIDTH * LCD_HEIGHT / 8; index++)
{
LcdWrite(LCD_DATA, 0x00);
}
}
void LcdReset(void)
{
lcd_vop = 0xa4; //0xBF;
lcd_bias = 0x14;
lcd_tempCoef = 0x04;
// set pin directions
DDR(SPI_DC_PORT) |= (1<<SPI_DC_PIN);
DDR(SPI_CLK_PORT) |= (1<<SPI_CLK_PIN);
DDR(SPI_DATA_PORT) |= (1<<SPI_DATA_PIN);
DDR(LCD_CS_PORT) |= (1<<LCD_CS_PIN);
DDR(LCD_RESET_PORT) |= (1<<LCD_RESET_PIN);
// toggle RST low to reset; CS low so it'll listen to us
PORT(LCD_CS_PORT) &= ~(1<<LCD_CS_PIN);
PORT(LCD_RESET_PORT) |= (1<<LCD_RESET_PIN);
_delay_ms(20);
PORT(LCD_RESET_PORT) &= ~(1<<LCD_RESET_PIN);
_delay_ms(20);
PORT(LCD_RESET_PORT) |= (1<<LCD_RESET_PIN);
_delay_ms(20);
// check for the signature byte in EEPROM
uint8_t signature = eeprom_read_byte((uint8_t*)0);
// get the stored contrast setting
if (signature == 0xF1)
{
lcd_vop = eeprom_read_byte((uint8_t*)1);
}
else
{
eeprom_update_byte((uint8_t*)0, 0xF1);
eeprom_update_byte((uint8_t*)1, lcd_vop);
}
LcdWrite(LCD_CMD, 0x21); // LCD Extended Commands.
LcdWrite(LCD_CMD, lcd_vop); // Set LCD Vop (Contrast).
//LcdWrite(LCD_CMD, lcd_tempCoef); // Set Temp coefficent.
LcdWrite(LCD_CMD, lcd_bias); // LCD bias mode
LcdWrite(LCD_CMD, 0x20);
LcdWrite(LCD_CMD, 0x0C); // LCD in normal mode. 0x0d for inverse
}