/* 
    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 <avr/io.h>
#include <avr/pgmspace.h>
#include <string.h>

void init_spi (void);		/* Initialize SPI port (asmfunc.S) */
void deselect (void);		/* Select MMC (asmfunc.S) */
void select (void);			/* Deselect MMC (asmfunc.S) */
void xmit_spi (uint8_t d);		/* Send a byte to the MMC (asmfunc.S) */
void dly_100us (void);		/* Delay 100 microseconds (asmfunc.S) */

extern const uint8_t tiny_font[][3] PROGMEM;
const uint8_t tiny_font[][3] = {
	{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				
	{0x00,0x10,0x00}, // 51 . (not 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
	{0x00,0x00,0x00}, // 58 space (not X)
	{0x03,0x1c,0x03}, // 59 Y
	{0x00,0x0a,0x00}  // 5a : (not Z)
};
		
#define LCD_DDR DDRB
#define LCD_PORT PORTB
#define LCD_RESET_PIN_NUMBER 0
#define LCD_CS_PIN_NUMBER 2
#define LCD_DC_DDR DDRD
#define LCD_DC_PORT PORTD
#define LCD_DC_PIN_NUMBER 6

void LcdCommand()
{
	LCD_DC_PORT &= ~(1<<LCD_DC_PIN_NUMBER);
}

void LcdData()
{
	LCD_DC_PORT |= (1<<LCD_DC_PIN_NUMBER);	
}

void LcdWrite(uint8_t data)
{
	deselect(); // make sure the SD card is not selected
	
	LCD_PORT &= ~(1<<LCD_CS_PIN_NUMBER);

	xmit_spi(data);

	LCD_PORT |= (1<<LCD_CS_PIN_NUMBER);
}

void LcdGoto(uint8_t x, uint8_t y)
{
	LcdCommand();
	LcdWrite(0x80 | x);  
	LcdWrite(0x40 | y);
}

void LcdClear(void)
{
	LcdData();
	for (uint16_t index = 0; index < 84 * 6; index++)
	{
		LcdWrite(0x00);
	}
}

void LcdString(const char *s)
{
	LcdData();

	uint8_t c;	
	while ((c = pgm_read_byte_far(s)))
	{
		unsigned short charbase = (c - 0x41) * 3;
		s++;
		
		for (uint8_t index = 0; index < 3; index++)
		{
			// need to use pgm_read_byte_far on Atmegas with more than 64K Flash
			uint8_t pixels = pgm_read_byte_far((unsigned char*)tiny_font + charbase + index);
			pixels = pixels << 1;
			LcdWrite(pixels);
		}
		
		LcdWrite(0x00);		
	}	
}

void LcdInit()
{
	// set LCD pin directions
	LCD_DDR |= (1<<LCD_RESET_PIN_NUMBER);
	LCD_DDR |= (1<<LCD_CS_PIN_NUMBER);
	LCD_DC_DDR |= (1<<LCD_DC_PIN_NUMBER);
  	
	init_spi();
	
	// toggle RST low to reset; CS low so it'll listen to us
	LCD_PORT &= ~(1<<LCD_CS_PIN_NUMBER);
	
	LCD_PORT |= (1<<LCD_RESET_PIN_NUMBER);
    for (uint8_t n = 200; n; n--) dly_100us();
	LCD_PORT &= ~(1<<LCD_RESET_PIN_NUMBER);
    for (uint8_t n = 200; n; n--) dly_100us();
    LCD_PORT |= (1<<LCD_RESET_PIN_NUMBER);
    for (uint8_t n = 200; n; n--) dly_100us();

	LcdCommand();
	LcdWrite(0x21); // LCD Extended Commands.
	LcdWrite(0xBF); // Set LCD Vop (Contrast). 
	LcdWrite(0x14); // LCD bias mode 
	LcdWrite(0x20);
	LcdWrite(0x0C); // LCD in normal mode. 0x0d for inverse	
	
	LcdGoto(0,0);
	LcdClear();
}