/*
 *  TESTLCD.C
 *
 *  Test a Hitachi HD44780 based LCD module by using the PC parallel
 *  port to write a string to the display.  Compile with a Borland C
 *  or C++ compiler (DOS target).  To simplify things long delays are
 *  used rather than checking the BUSY flag.  (Note: early versions of
 *  Turbo C had a bug in the delay() routine.) 
 *
 *
 *  Usage:
 *          testlcd               print the default string
 *          testlcd string        print the given string (but not "-")
 *          testlcd -             print stdin (and scroll if necessary)
 *
 *
 *  The LCD is connected with a 4-bit data interface:
 *
 *        LPT Signal     LPT pin      LCD Signal    LCD pin
 *        ==========     =======      ==========    =======
 *       SELECT PRINTER    17            DB7           14
 *       INIT PRINTER      16            DB6           13
 *       AUTO LINEFEED     14            DB5           12
 *       STROBE             1            DB4           11
 *       D0                 2            EN             6
 *       D1                 3            R/W            5
 *       D2                 4            RS             4
 *                                       VO  (*)        3
 *                                       VDD (+5V)      2
 *       GND               25            VSS            1
 *
 *
 *       (*) VO is connected to the slider of a 10k pot between VDD
 *           and VSS
 *
 *
 *  Copyright (C) 1996 David Tait
 *  Free for non-profit use and like anything that's free this software
 *  comes with absolutely no warranty
 *
 *  Version 0.0  3rd March 1996
 *
 */

#include <stdio.h>
#include <stddef.h>
#include <dos.h>

#define CHARS               16                      /* LCD width in chars */
#define out_data(w)         outportb(c_reg,(w)^0xB) /* bits 3,1,0 inverted */
#define out_cntl(en,rw,rs)  outportb(d_reg,(en)|((rw)<<1)|((rs)<<2))
#define move_to(a)          write8(0x80+a,0,1)
#define home()              write8(2,0,5)
#define cur_off()           write8(0xC,0,1)


int d_reg;
int c_reg;


void idle(void)
{
    out_cntl(0,1,1);
    out_data(0xF);     /* all O/C ouputs high and can be used as inputs */
}


void setup(void)
{
    d_reg = peek(0, 0x408);      /* port address of LPT1 data register */
    c_reg = d_reg+2;             /* port address of LPT1 control register */
}


void write4(int w, int rs, int ms)
{
    out_data(w);
    out_cntl(0,0,rs);            /* cycle EN */
    out_cntl(1,0,rs);
    out_cntl(0,0,rs);
    delay(ms);
}


void write8(int w, int rs, int ms)
{
    write4((w&0xF0)>>4,rs,0);         /* write high nibble */
    write4(w&0xF,rs,ms);              /* then low nibble */
}


void init_lcd(int n)
{
    idle();
    delay(15);                /* not necessary here of course */
    write4(3,0,5);            /* set 8-bit mode */
    write4(3,0,5);            /* and again */
    write4(3,0,5);            /* and again */
    write4(2,0,5);            /* set 4-bit mode */
    write8(0x20+8*n,0,5);     /* set 4-bit mode, n+1 lines, 5x7 dots */
    write8(6,0,5);            /* set cursor to move forward */
    write8(1,0,5);            /* clear display */
    write8(0xF,0,5);          /* display on, cursor on, blink */
    idle();
}


void print(char *s)
{
    while ( *s )
	write8(*s++,1,1);
    idle();
}


void scroll(void)
{
    int c, n=0;

    init_lcd(0);                       /* select one line mode */
    cur_off();
    while ( (c=getchar()) != EOF ) {
	if ( n == CHARS )
	    write8(7,0,1);             /* need to enable scrolling now */
	write8(c,1,1);
	delay(150);
	++n;
    }
    idle();
}


void main(int argc, char *argv[])
{
    setup();
    init_lcd(1);                        /* two line mode */

    if ( argc < 2 ) {                   /* print default string */
	print("Look:");
	move_to(40);
	print("4bit mode works!");
	home();
	cur_off();
	idle();
    } else if ( *argv[1]=='-' )
	scroll();                       /* or print stdin */
    else
	print(argv[1]);                 /* or print argument */
}
