351 lines
8.6 KiB
C++
Executable File
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
|
|
}
|