LCD (programming & pinouts)

Contents:

[Document Version: 1.10] [Last Updated: 9/12/96]


1. LCD pintout & setup

(From Steve Hill)

An LCD backlight would be run off of 5vDC, while an EL backlight uses the inverter that you described. The "two extra" pins are for the backlight and are not polarized - hook the outputs of the inverter to these pins.

Vss should be hooked to ground - I believe your origional post may have been in error.

Vdd should be hooked to +5v.

Vo (pin 3) should be connected to the wiper on a 10K pot which is accross pins 1 and 2.

Pin 4 is register select. When high you are writing data (characters to the display) and when low you are writing to control registers.

Pin 5 is Read/Write. Can be tied low since a basic implementation has no need to read from the display registers - but is useful in more advanced applications.

Pin 6 is Enable. The data on pins 7-14 is latched on the falling edge of this signal. Just strobe it low after you have set up data on the data pins.

Pins 7-14 are data I/O. Using all these pins for an 8 bit interface. Use only DB4-7 (pins 11-14) for a 4 bit interface.

Prior to writing characters to the display, it must be initialized. A typical initialization sequence is as follows: (assumes R/W tied low)

  1. Power ON
  2. Function SET (8 data bits, 2 lines, 5x7 dot format) RS=0 Data=0x38
  3. Function SET (repeat above 4 times) RS=0 Data=0x38
  4. Function SET RS=0 Data=0x38
  5. Function SET RS=0 Data=0x38
  6. Entry Mode (Increment one, no shift) RS=0 Data=0x06
  7. Display ON/OFF (display ON, Cursor ON, Blink OFF) RS=0 Data=0x0E
  8. Display Clear RS=0 Data=0x01
  9. DD RAM Address Set RS=0 Data=0x80
(note the address could be different for your display - try it - consult data sheet) Also note that the second display line probably isn't at an address that is consecutive with the first (0x80-0x80 typical first line; 0xC0-0xCF typeical second line) Now you're ready to write data. Just set RS high and write the character codes you want. DDRAM address is automatically incremented. To get to the second line you need to set the DDRAM address as shown above.


2. LCD programming

(From Bill Mayhew)

[Most LCDs have same/similar programming]


RS R/W B7 B6 B5 B4 B3 B2 B1 B0

0  0   0  0  0  0  0  0  0  1        Clear Display

0  0   0  0  0  0  0  0  1  X        Return Home

0  0   0  0  0  0  0  1  D  S        Entry Mode Set
                                     D: 0=direction -, 1=direction +
                                     S: 0=no shift, 1=shift

0  0   0  0  0  0  1  D  C  B        Display on/off
                                     D: 0=disply off, 1=on
                                     C: 0=cursor off, 1=on
                                     B: 0=blink off, 1=on

0  0   0  0  0  1  S  R  X  X        Display or Cursor Shift
                                     S: 0=cursor only, 1=both
                                     R: 0=right; 1=left

0  0   0  0  1  D  N  F  X  X        Set data interface
                                     D: 0=8 bit, 1=4 bit
                                     N: 0=F works, 1=F is ignored
                                     F: 0=5x7 dots, 1=5x10 dots

0  0   0  1  A  A  A  A  A  A        Set char gen RAM address
                                     Lets you select one of 64
                                     chars for loading a bit map

0  0   1  A  A  A  A  A  A  A        Set display RAM address
                                     Lets you select an address
                                     in the display buffer.  With
                                     this you can write in a pos-
                                     ition that is off the viewable
                                     display.

0  1   B  A  A  A  A  A  A  A        Read busy flag, address pointer
                                     If B=1, display is updating.
                                     Also returns address of last
                                     data transfer.

1  0   D  D  D  D  D  D  D  D        Write Data
                                     Writes to Display if previous
                                     instruction was SET DISPLAY
                                     RAM ADDR.  Writes to char gen if
                                     previous instruction was SET
                                     CHAR GEN RAM ADDR.

1  1   D  D  D  D  D  D  D  D        Read Data
                                     Reads from Display if previous
                                     instruction was SET DISPLAY
                                     RAM ADDR.  Reads from char gen if
                                     previous instruction was SET
                                     CHAR GEN RAM ADDR.
It is best to read the busy flag to see if the previous operation you requested is complete. You can also go by timing if you are trying to minimize the number of input operations. Timing:


Clear               82uS to 1.64 mS  (assuming 250 KHz clock)
Return home         40uS to 1.6 mS
Entry mode set      40uS 
Display on/off      40uS
Display/cursor shf  40uS
Function set        40uS
Set CG RAM addr     40uS
Set disp RAM addr   40uS
Read busy flag       1uS
Write Data          40uS
Read Data           40uS

3. LCD programming sample (6502)

(From Bill Mayhew)


	.nolist
	.page
	.title	"Watch dog program"
	.list
	.eject
;
;watch dog single board computer system
;Bill Mayhew,  Neoucom, January 1989
;traget cpu Rockwell 6502 with 1 MHz clock
;
;revised March 6, 1989 to support warm boot into
;bypass mode.
;
;6522 chip registers
outb	.equ	$c000
outa	.equ	$c001
ddrb	.equ	$c002
ddra	.equ	$c003
t1l	.equ	$c004
t1h	.equ	$c005
t2l	.equ	$c008
t2h	.equ	$c009
acr	.equ	$c00b
pcr	.equ	$c00c
flags	.equ	$c00d
icr	.equ	$c00e
;
;assorted page zero locations
asave	.equ	$00		;used by intrpt
ysave	.equ	$01		;used by intrpt
casave	.equ	$02		;used by chrout
cysave	.equ	$03		;used by chrout
caddr	.equ	$04		;address of cursor on display unit
cmode	.equ	$05		;current mode of cursor
pasave	.equ	$06		;used by print
pysave	.equ	$07		;used by print
msgbase .equ	$08		;word used to hold beginning of message
slcount .equ	$0a		;used by sleep routine
sysave	.equ	$0b		;used by sleep
tmin	.equ	$0c		;used in count-down timer
min	.equ	$0d
tsec	.equ	$0e
sec	.equ	$0f
tenth	.equ	$10
twait	.equ	$11
owait	.equ	$12
getsw	.equ	$13		;vector switch for char input
getvec	.equ	$14		;word vector for char input routine
cookie	.equ	$16		;word 'magic cookie' to determine reset mode
;
;
	.org	$f000		;assemble into system ROM
entry	.equ	*
	sei			;kill interrupts whilst setting up
	cld			;operate in binary mode
	lda	#$07		;only bits 0, 1 and 2 are outputs
	sta	ddra		;enable ddra
	lda	#$ff		;ddr output mask -- all bits on
	sta	ddrb		;enable ddrb
	lda	#$c2		;mask to enable timer #1 & ca1 trigger
	sta	icr		;set interrupt control register
	lda	#$40		;mask for t1 continuous mode
	sta	acr		;set auxiliary control register
	lda	#$fb		;ss relay on, lcd in text mode, beeper off
	sta	outa		;place bits in output register
	lda	#(50000 % $100)	;loop time of 50.000 mS
	sta	t1l		;least significant byte of timer
	lda	#(50000 >> 8)
	sta	t1h		;most significant byte of timer
	lda	#$ae		;mask to generate PB2 pulse on outb write
	sta	pcr		;set peripheral control register
	jsr	newline		;initially clear the display
	lda	cookie		;check magic cookoie; $1234 means warm boot.
	cmp	#$34		;cookie lsb
	bne	e1		;continue if not magic value
	lda	cookie+1
	cmp	#$12		;cookie msb
	bne	e1
	jmp	bypass		;go to bypass mode if magic value found
e1	lda	#$34		;set up magic cookie
	sta	cookie		;lsb
	lda	#$12
	sta	cookie+1	;msb
	lda	#(init1 % $100)	;get first initial message
	sta	msgbase
	lda	#(init1 >> 8)
	sta	msgbase+1
	jsr	print		;send line to display
	lda	#3		;sleep 3 seconds
	jsr	sleep
	jsr	newline
	lda	#(init2 % $100)	;get second initial message
	sta	msgbase
	lda	#(init2 >> 8)
	sta	msgbase+1
	jsr	print
	lda	#3
	jsr	sleep
	jsr	newline
	lda	#(init3 % $100)	;get third initial message
	sta	msgbase
	lda	#(init3 >> 8)
	sta	msgbase+1
	jsr	print
	lda	#3
	jsr	sleep
	lda	#(i1 % $100)	;initialize char interrupt vector
	sta	getvec
	lda	#(i1 >> 8)
	sta	getvec+1
	lda	#0
	sta	cookie		;clear cookie
	sta	cookie+1
	lda	t1h		;reset timer
	cli			;interrupts are ok now
;
;
mlp	.equ	*		;kernel begins here
	lda	#$7		;reset count-down clock
	sta	tmin
	lda	#0
	sta	min
	sta	tsec
	sta	sec
	sta	tenth
	lda	twait		;set oldwait-time = this-wait-time
	sta	owait
	sei			;can't interrupt print operations
	jsr	newline		;clear display
	lda	#(resetm % $100)
	sta	msgbase
	lda	#(resetm >> 8)
	sta	msgbase+1
	jsr	print		;print static element of time display
ml1	ldy	tmin		;get tens of minutes left
	lda	#9		;cursor in column 9
	sei			;can't interrupt print operations
	jsr	tput		;put ascii char on display
	ldy	min
	lda	#10
	jsr	tput
	ldy	tsec
	lda	#12
	jsr	tput
	ldy	sec
	lda	#13
	jsr	tput
	ldy	tenth
	lda	#15
	jsr	tput
	cli			;o. k. to interrupt now
				;
	lda	#0		;set accum=0
	ldy	#4		;see if all 5 time bytes are zero
ml2	ora	tmin,y		;tmin+4, +3, +2, +1, +0
	dey
	bpl	ml2		;loop if y >= 0
	ora	#0		;set flags
	beq	mlre		;do a reset when 00:00.0
				;
	dec	tenth
	bpl	ml3		;skip other digits if >= 0
	lda	#9
	sta	tenth
	dec	sec
	bpl	ml3
	lda	#9
	sta	sec
	dec	tsec
	bpl	ml3
	lda	#5
	sta	tsec
	dec	min
	bpl	ml3
	lda	#9
	sta	min
	dec	tmin
ml3	lda	twait		;what is our hash count?
	cmp	owait		;same as before?
	beq	ml3		;wait for interrupt to change hash count
	sta	owait		;twait --> owait
ml4	lda	twait
	cmp	owait
	beq	ml4
	sta	owait
	jmp	ml1		;repeat process for next 1/10 second
				;
mlre	sei			;can't interrupt power-cycle
	jsr	newline		;have to reboot because 70 minutes expired
	lda	#(bootm % $100)
	sta	msgbase
	lda	#(bootm >> 8)
	sta	msgbase+1
	jsr	print
	lda	outa
	and	#$fe		;set bit 0=0
	sta	outa		;shut off solid state relay
	lda	#30		;sleep 30 seconds to allow power-clycle
	jsr	sleep
	lda	outa
	ora	#$01		;set bit 0=1
	sta	outa
	cli			;o. k. to interrupt now
	jmp	mlp		;re-do this routine forever!
;
;
tput	.equ	*		;ascii print a decimal number in y, cursor in a
	pha
	lda	outa		;enter command mode
	and	#$fd		;bit 0=0
	sta	outa
	pla
	ora	#$80		;set msb
	jsr	chrout
	lda	outa
	ora	#$02		;enter text mode
	sta	outa
	tya			;get decimal #
	clc
	adc	#48		;adjust for printable ascii
	sta	outb
	rts
;
;
intrpt	.equ	*
	pha
	lda	flags		;get interrupt flags from 6522 via chip
	and	#$40		;see if t1 interrupt has happened
	beq	ijmp		;else it must have been from the CA-1 pin
	inc	twait		;change hash variable
	lda	t1l		;read register to clear the flag bit
	lda	flags		;see if other interrupt is pending
	and	#$02
	bne	ijmp
	pla
	rti			;resume processing
				;
ijmp	jmp	(getvec)
				;
i1	pla			;get rid of pushed accumulator
	lda	outa		;read outa to clear the CA-1 flag bit
	jsr	newline		;clear display
	lda	#(portm % $100)
	sta	msgbase
	lda	#(portm >> 8)
	sta	msgbase+1
	jsr	print		;display busy activity message
	lda	#0
	sta	getsw		;initially exit at end of this sleep
	lda	#(i2 % $100)	;re-aim vector
	sta	getvec
	lda	#(i2 >> 8)
	sta	getvec+1
	pla			;dispose of interrupt vector and flags
	pla
	pla
i1x	cli			;"manually" re-enable interrupting
	lda	#1		;sleep for one second
	jsr	sleep
	sei			;disable interrupts for checking vector
	lda	getsw		;any more sleep?
	bne	i1y
	dec	getsw		;less sleep now
	jmp	i1x		;go sleep
i1y	lda	#(i1 % $100)	;re-aim vector
	sta	getvec
	lda	#(i1 >> 8)
	sta	getvec+1
	cli			;o. k. for interrupts now
	jmp	mlp		;jump back into main loop
				;
i2	lda	#1		;force one more second of sleep
	sta	getsw
	lda	outa		;read port to reset interrupt
	pla
	rti
;
;
delay	.equ	*		;wait a short time
	pha
	lda	acr		;get timer control
	and	#$df		;mask t2 control bit
	sta	acr		;update status
	pla
	pha
	sta	t2l		;load timer latch
	sty	t2h		;writing MSB triggers timer
wait	lda	flags		;get flags
	and	#$20		;mask out the t2 status bit
	beq	wait		;loop until it times out
	pla
	rts
;
;
sleep	.equ	*		;sleep for n seconds, n passed in accum.
	sty	sysave
	ora	#0		;set flags
sl1	beq	slend
	sec
	sbc	#1		;decrement sleep time
	pha
	lda	#20		;sleep in 20 50-mS increments
	sta	slcount
sl2	dec	slcount
	bne	sl3
	pla
	jmp	sl1
sl3	lda	#(50000 % $100)	;50 mS time constant
	ldy	#(50000 >> 8)
	jsr	delay
	jmp	sl2
	ldy	sysave
slend	rts
;
;
chrout	.equ	*		;send char in accum to display
	sta	outb		;put in PIA output
	sta	casave		;in case caller wants it back
	sty	cysave
	lda	#120		;wait 120 uS
	ldy	#0
	jsr	delay		;burn up some time
	ldy	cysave
	lda	casave
	rts
;
;
putc	.equ	*		;put char into display, maybe with scroll
	pha
	lda	caddr		;where is cursor now?
	cmp	#16		;past last column?
	bne	pc1		;if not, don't worry, be happy
	lda	cmode		;check if scrolling is on
	bne	pc2		;skip this if it is already on
	lda	outa
	and	#$fd		;bit 1=0, puts lcd in command mode
	sta	outa
	lda	#$07		;display board code that enables scrolling
	jsr	chrout		;send
	lda	outa
	ora	#$02		;bit 1=1, puts lcd in character mode
	sta	outa
	inc	cmode		;flag the status byte
pc1	inc	caddr		;show that cursor moved
pc2	pla
	jsr	chrout		;send user's character
	rts			;that's it!
;
;
newline	.equ	*		;clear display & home cursor with full reset
	pha
	lda	outa		;get status
	and	#$fd		;bit 1=0, enter command mode
	sta	outa
	lda	#$01		;display board reset code
	jsr	chrout		;send to display
	lda	#(5000 % $100)	;time constant for 5 mS delay
	ldy	#(5000 >> 8)
	jsr	delay		;burn up some time
	lda	#$34		;select 5 * 10 bit character set
	jsr	chrout
	lda	#$0c		;unblank display
	jsr	chrout
	lda	#$06		;disable display scrolling
	jsr	chrout
	lda	#0
	sta	cmode		;indicate scrolling is disabled
	sta	caddr		;cursor is in column zero
	lda	outa
	ora	#$02		;bit 1=1, enter lcd text mode
	sta	outa
	pla
	rts
;
;
print	.equ	*		;print a message on entry, msgbase points to msg
	sta	pasave
	sty	pysave
	ldy	#0
pl	lda	(msgbase),y	;get character
	beq	pend		;if a \0, that's end of msg.
	jsr	putc
	iny
	bne	pl		;branch always
pend	ldy	pysave
	lda	pasave
	rts
;
;
bypass	.equ	*		;bypass mode
	lda	#0		;clear magic cookie
	sta	cookie
	sta	cookie+1
	lda	#(bpassm % $100)
	sta	msgbase
	lda	#(bpassm >> 8)
	sta	msgbase+1
	jsr	print
b1	lda	outa		;get output port
	ora	#$04		;turn on beeper
	sta	outa
	lda	#(50000 % $100 )
	ldy	#(50000 >> 8)	;50 mS time constant
	jsr	delay
	jsr	delay
	jsr	delay
	jsr	delay		;wait total 200 mS
	lda	outa
	and	#$fb		;turn off beeper
	sta	outa
	lda	#120		;sleep for two minutes
	jsr	sleep
	jmp	b1		;do forever until reset
;
;
init1	.text	"  Watch Cat 1.0"
	.byte	0
init2	.text	"   Bill Ma"
	.byte	%11111001	;y with lower case descender
	.text	"hew"
	.byte	0
init3	.text	"  NEOUCOM  1989"
	.byte	0
resetm	.text	"Reset in   :  ."
	.byte	0
portm	.text	"   Port Active"
	.byte	0
bootm	.text	"RESETTING SYSTEM"
	.byte	0
bpassm	.text	"  BYPASS MODE"
	.byte	0
;
;
	.org	$f7fa		;hardwire vectors
	.word	entry		;NMI points to system entry point
	.word	entry		;address of system entry point
	.word	intrpt		;address of IRQ service routine
	.end

4. Where to buy LCD displays

(From Marc Christensen)

If you can use 1x16 they are easy to find on the surplus market. Timeline, Inc:

TimeLine Inc
23605 Telo Ave
Torrance, CA 90505
USA (800) 872-8878
CA (800) 223-9977

has lots of LCD displays. For example, 1x16's got for $6.00 - $8.00 with the controllers built into them and backlight. They also have a 4 Col x 2 Row for $5.00. Many of the displays can use larger characters and fewer lines if you want to try that.

Marlin P Jones has a 1x24 without the controller for $1.00


5. Controlling LCD with a Motorola 68hc11

(From Alan Kilian)


*
*       Author: Alan Kilian   kilian(at)cray.com
*
*       Title : HC11 LCD driver routines
*
*       File Name : lcd.asm
*
*       Description : This program demonstrates the use of a HC11 processor
*                     to control a LCD panel
*
*       History : 02/10/93 Created.
*                 02/11/93 Added power-up delay
*		  03/17/94 Tried to get reliable operation with 256 X 64 bit
*			   display. Epson LM213B
*		  03/18/94 Added the LCD_RST line to control the *RESET line
*		  03/19/94 Added proper *RESET timeing. Combined SELECT_REG
*			   and STORE_REG into one routine.
*		  03/30/94 Changed to using BSET/BCLR
*		  04/04/94 Finally got BSET/BCLR to work!!!
*
*       Port usage:
*       PORTC Bit0 = DB0 through BIT7 = DB7
*       PORTA Bit7 = E
*	      Bit6 = RS
*	      Bit5 = RW
*	      Bit4 = Reset
*
*       Note : This program is written for the MC68HC811E2 processor running
*              in single-chip mode. It is designed for use with a processor
*              running with an 8 mHz crystal. If you are using a different
*              crystal frequency you will need to re-compute all of the
*              timing values in this code.
*
*              The structure, serial I/O and command processor portions of
*              this program evolved from the program HEXLOB40 written by
*              Fred Martin and Randy Sargent and we thank them greatly.
****************************************************************************
 
* One-Byte Registers
PORTA   EQU     $1000
PACTL   EQU     $1026
PORTB   EQU     $1004   ; PORT B data register
PORTC   EQU     $1003
DDRC    EQU     $1007
SPCR	EQU	$1028
BAUD	EQU	$102B	; SCI Baud Rate Control Register
SCCR1	EQU	$102C	; SCI Control Register 1
SCCR2	EQU	$102D	; SCI Control Register 2
SCSR	EQU     $102E	; SCI Status Register
SCDR	EQU     $102F	; SCI Data Register
* Masks for serial port
PORTD_WOM	EQU	$20
BAUD1200	EQU	$B3
BAUD9600	EQU	$B0
TRENA		EQU	$0C	; Transmit, Receive ENAble
RDRF		EQU	$20	; Receive Data Register Full
TDRE		EQU	$80	; Transmit Data Register Empty
 
* Masks for the PORTA LCD control signals
LCD_E           EQU     $80     ; Bit 7 of PORTA is an active HIGH "E"
LCD_RS          EQU     $40     ; Bit 6 of PORTA is an active HIGH reg#1
LCD_RW          EQU     $20     ; Bit 5 of PORTA is an active LOW write
LCD_RST		EQU	$10	; Bit 4 of PORTA is an active LOW reset
 
* Masks for PORTC direction
ALL_OUTPUT      EQU     $FF     ; 1 = output, 0 = input
 
* The BUSY bit on PORTC
BUSY            EQU     $80     ; Bit 7 of PORTC
 
* The names of the LCD registers
MODE            EQU     $00
PITCH           EQU     $01
NCHARS          EQU     $02
DUTY            EQU     $03
CURPOS          EQU     $04
DUMMY1          EQU     $05     ; There is no command 5
DUMMY2          EQU     $06     ; There is no command 6
DUMMY3          EQU     $07     ; There is no command 7
DSLOW           EQU     $08
DSHIGH          EQU     $09
CURLOW          EQU     $0A
CURHIGH         EQU     $0B
WRDAT           EQU     $0C
RDDAT           EQU     $0D
CLRBIT          EQU     $0E
SETBIT          EQU     $0F
 
* Values for various LCD things
DISPLAY_WIDTH   EQU     256     ; Pixels wide
DISPLAY_HEIGHT  EQU     64      ; Pixels tall
DUTY_CYCLE      EQU     64      ; 1/64 DECIMAL duty cycle
CHAR_WIDTH      EQU     8       ; How wide is the char cell?
CHAR_HEIGHT     EQU     8       ; How tall is the char cell?
CURSOR_LINE     EQU     8       ; Cursor is on line 8 of the char cell
 
* Bits for the mode command
DISPLAY_ON      EQU     $20     ; Is the display on?
MASTER          EQU     $10     ; Master/Slave mode
BLINK           EQU     $08     ; Does the cursor blink?
CURSOR_ON       EQU     $04     ; Is the cursor displayed?
GRAPHICS_MODE   EQU     $02     ; Graphics or character mode?
EXTERNAL_CG     EQU     $01     ; Is the character generator internal?
 
********************************************************************************
* zero page RAM definitions. Do not use FCB here. It will stomp EEBOOT20.
********************************************************************************
 
        ORG     $00             ; The beginning of RAM
 
**********************************************************************
*                              MAIN CODE                             *
**********************************************************************
 
        ORG     $F800           ; $F800 is the beginning of EEPROM
*                               ; on a MC68HC811E2 processor
 
Start:
        LDS     #$00FF          ; Set stack at the top of ram
 
	LDAA	#ALL_OUTPUT
	STAA	DDRC		; Set PORTD for all outputs.
 
	JSR	SERIAL_SETUP
 
	JSR	LCD_RESET
        JSR     LCD_SET
	JSR	LCD_HOME
	JSR	LCD_HOME
 
        JSR     PRT_STR
 
mainloop
        BRA     mainloop
 
SERIAL_SETUP
	LDX	#$1000
	BCLR	SPCR,X PORTD_WOM 	; turn off wired-or mode
	LDAA	#BAUD9600
	STAA	BAUD
	LDAA	#TRENA
	STAA	SCCR2
	RTS
 
LCD_RESET
	LDAA	#00
	STAA	PORTA			; Reset the LCD panel
        JSR     BIGDELAY
	LDAA	#LCD_RST
	STAA	PORTA			; Release the reset line
        JSR     BIGDELAY
	RTS
 
BIGDELAY
        LDAA    #$FF            ; Delay to allow the LCD panel to power-up
BIG1	LDAB    #$FF
BIG2	DECB
        BNE     BIG2
        DECA
        BNE     BIG1
        RTS
 
LCD_HOME
        LDAA    #DSLOW          ; Set display address low
        LDAB    #$00            ; Start displaying at address zero
        JSR     SELECT_REG
 
        LDAA    #DSHIGH         ; Set display address high
        LDAB    #$00            ; Start displaying at address zero
        JSR     SELECT_REG
 
        LDAA    #CURLOW         ; Set cursor address low
        LDAB    #$00            ; Put the cursor at address zero
        JSR     SELECT_REG
 
        LDAA    #CURHIGH        ; Set cursor address high
        LDAB    #$00            ; Put the cursor at address zero
        JSR     SELECT_REG
        RTS
 
PRT_STR LDX     #STR
        LDAA    #WRDAT
PRT0	LDAB    0,X
        CMPB    #'='            ;String terminates with a '=' character
        BEQ     PRT1
        JSR     SELECT_REG
        INX
        BRA     PRT0
PRT1	RTS
 
PRT_CHR PSHA
	PSHB
	TAB
	LDAA    #WRDAT
        JSR     SELECT_REG
	PULB
	PULA
	RTS
 
LCD_SET
        LDAA    #$80
        STAA    PACTL
 
        LDAA    #MODE           ; Command 0 Mode control
	LDAB	#DISPLAY_ON|MASTER|BLINK|CURSOR_ON
        JSR     SELECT_REG
 
        LDAA    #PITCH          ; Command 1 Character pitch
        LDAB    #CHAR_HEIGHT-1
	ASLB
	ASLB
	ASLB
	ASLB
	ORB	#CHAR_WIDTH-1
        JSR     SELECT_REG
 
        LDAA    #NCHARS         ; Command 2 Number of characters
        LDAB    #DISPLAY_WIDTH/CHAR_WIDTH
	SUBB	#1
        JSR     SELECT_REG
 
        LDAA    #DUTY           ; Command 3 Duty cycle
        LDAB    #DUTY_CYCLE-1
        JSR     SELECT_REG
 
        LDAA    #CURPOS         ; Command 4 Cursor position
        LDAB    #CURSOR_LINE-1
        JSR     SELECT_REG
        RTS
 
 
SELECT_REG
	PSHX
	PSHB
	LDX	#$1000
	JSR	WAIT_BUSY
        STAA    PORTC           ; Set the data lines
        BSET    PORTA,X #LCD_RS         ; Set RS high and RW low
        BSET    PORTA,X #LCD_E   ; Set RS and E high
        BCLR    PORTA,X #LCD_E            ; Set RS low also.
        BCLR    PORTA,X #LCD_RS         ; Set E low
 
	PULB
        STAB    PORTC           	; Set the data lines
        BSET    PORTA,X #LCD_E          ; Set E High
	BCLR	PORTA,X #LCD_E
	PULX
        RTS
 
WAIT_BUSY
	PSHB
	BCLR	DDRC,X #BUSY
        
        BSET    PORTA,X #LCD_RS|LCD_RW
        BSET    PORTA,X #LCD_RS|LCD_RW|LCD_E
 
WAIT	BRSET	PORTC,X #BUSY * 	; Wait until the BUSY bit goes zero
 
	BCLR	PORTA,X #LCD_E
	BCLR	PORTA,X #LCD_RW
	BCLR	PORTA,X #LCD_RS
 
	BSET	DDRC,X #BUSY
	PULB
	RTS
 
STR     FCC     'Shutter Speed.1 = '
 
BadInt  RTI                     ; Set all unused vectors here
        Org     $FFC0           ; Where the interrupt vectors are
 
        FDB     BadInt  * $FFC0 ; Reserved
        FDB     BadInt  * $FFC2 ; Reserved
        FDB     BadInt  * $FFC4 ; Reserved
        FDB     BadInt  * $FFC6 ; Reserved
        FDB     BadInt  * $FFC8 ; Reserved
        FDB     BadInt  * $FFCA ; Reserved
        FDB     BadInt  * $FFCC ; Reserved
        FDB     BadInt  * $FFCE ; Reserved
        FDB     BadInt  * $FFD0 ; Reserved
        FDB     BadInt  * $FFD2 ; Reserved
        FDB     BadInt  * $FFD4 ; Reserved
 
        FDB     BadInt  * $FFD6 ; SCI Serial System
        FDB     BadInt  * $FFD8 ; SPI Serial Transfer Complete
        FDB     BadInt  * $FFDA ; Pulse Accumulator Input Edge
        FDB     BadInt  * $FFDC ; Pulse Accumulator Overflow
        FDB     BadInt  * $FFDE ; Timer Overflow
        FDB     BadInt  * $FFE0 ; In Capture 4/Output Compare 5 (TI4O5)
        FDB     BadInt  * $FFE2 ; Timer Output Compare 4 (TOC4)
        FDB     BadInt  * $FFE4 ; Timer Output Compare 3 (TOC3) 
        FDB     BadInt  * $FFE6 ; Timer Output Compare 2 (TOC2)
        FDB     BadInt  * $FFE8 ; Timer Output Compare 1 (TOC1)
        FDB     BadInt  * $FFEA ; Timer Input Capture 3 (TIC3)
        FDB     BadInt  * $FFEC ; Timer Input Capture 2 (TIC2)
        FDB     BadInt  * $FFEE ; Timer Input Capture 1 (TIC1)
        FDB     BadInt  * $FFF0 ; Real Time Interrupt (RTI)
        FDB     BadInt  * $FFF2 ; External Pin or Parallel I/O (IRQ)
        FDB     BadInt  * $FFF4 ; Pseudo Non-Maskable Interrupt (XIRQ)
        FDB     BadInt  * $FFF6 ; Software Interrupt (SWI)
        FDB     BadInt  * $FFF8 ; Illegal Opcode Trap ()
        FDB     BadInt  * $FFFA ; COP Failure (Reset) ()
        FDB     BadInt  * $FFFC ; COP Clock Monitor Fail (Reset) ()
        FDB     Start   * $FFFE ; /RESET
        END

6. LCD software posting for 68HC11

(From Christopher E. Piggott, WZ2B)


*
* Routines for the Hitachi HD44780 LCD controller, as used in many
* Optrex and Densitron text-only displays
*
* Written for the 68HC11 by:
*
* Christopher Piggott, WZ2B
* Rochester, New York
*
*
* This stuff is open for anybody to use as they wish.  I only ask
* that you give me a credit for anything you use or distribute,
* and I warn that there is no warranty whatsoever.
*
***
*
* This should be fairly useful even if you don't know the 68HC11
* language or architecture.  Let me explain that I have the LCD
* display hooked up to general-purpose I/O port PRTC, which is an
* 8-bit bi-directional port.  The DDR (data direction register for
* each port) selects whether the pins of PORT C are inputs or outputs.
*
* In addition to PORT C, you need to have a READ/WRITE line, an ENABLE
* line, and a REGISTER SELECT line.  The RS sets whether you are writing
* data to the display, or are writing to one of the internal
* configuration registers.
*
* I'm not sure how much info you guys need on configuring the displays,
* but there is a lot of stuff to configure.  This is definitely enough
* to get started.
*
* One other thing about the 68HC11 - there are two index registers, X
* and Y.  In this program, I'm assuming that all of the 'HC11's I/O
* port registers (memory mapped) are in the $1000-103F block.  Therefore,
* I'm loading Y with $1000 and accessing the ports indexed to Y.
*
*
* BTW, this assembles with the 2500AD cross-assembler for the IBM PC.
* If you're unfortunate enough to be using the PASM stuff that comes
* from Motorola, you may have to massage it a little, but the code
* stays the same.
*
*
* Good luck
*
* Chris, WZ2B, cep4478@ultb.isc.rit.edu
*

BASE:           EQU     $1000                   ; base address of ports
DDIR:           EQU     07h                     ;       offset to DDRC
DPORT:          EQU     03h                     ;       offset to PRTC
CTRL:           EQU     04h                     ;       offset to PRTB

RW:             EQU     01h                     ; READ/WRITE bit
RS:             EQU     02h                     ; Register Select bit
ENABLE:         EQU     04h                     ; ENABLE bit
BUSYBIT:        EQU     80h                     ; BUSY flag

**********************************************************************

* This can start anywhere you want.  I wrote this for the 'HC11EVB
* so I stuck it at $C000h.

        ORG     $C000

        JSR     initlcd

        LDX     #string1
loop1:
        LDAA    0,X
        BEQ     done1
        JSR     print
        INX
        BRA     loop1

done1:
        LDX     #string2
loop2:
        LDAA    0,X
        BEQ     done2
        JSR     print
        INX
        BRA     loop2

done2:
        SWI
        STOP

**********************************************************************

string1:        FCC     "The Earth is flat"
                FCB     00h
string2:        FCC     "-- Thomas Dolby"
                FCB     00h

**********************************************************************

initlcd:
        LDY     #BASE

        LDAA    #0FFh                   ; set PORT D to be OUTPUTS
        STAA    DDIR,Y

        LDAA    #38h                    ; configure display
        JSR     command

        LDAA    #0Dh                    ; turn on display, block cursor
        JSR     command

        LDAA    #01h                    ; clear screen
        JSR     command

        RTS


**********************************************************************
* wait:
*       spinlock until LCD is ready to accept another command
*       (the LCD controller is a little slow and can only process
*       one command or data word at a time)
**********************************************************************

wait:
        BCLR    DDIR,Y,BUSYBIT                  ; SET BASE+DDIR TO OUTPUT
        BCLR    CTRL,Y,RS                       ; RS=0 (register)
        BSET    CTRL,Y,RW                       ; RW=1 (write)

wloop:
        BSET    CTRL,Y,ENABLE                   ; strobe enable
        BRCLR   DPORT,Y,BUSYBIT,ready           ; if DATA & 7 jmp to ready
        
notready:
        BCLR    CTRL,Y,ENABLE                   ; turn off enable bit
        BRA     wloop

ready:
        BCLR    CTRL,Y,ENABLE                   ; turn off enable bit
        BSET    DDIR,Y,BUSYBIT                  ; set DDRA for output
        RTS
        
**********************************************************************
* command:
*       transmit a command from the accumulator to the LCD display
**********************************************************************

command:
        JSR     wait                            ; make sure dispay is not busy
        STAA    DPORT,Y                         ; put command on data bus
        BCLR    CTRL,Y,RW                       ; RW = 0 (write)
        BCLR    CTRL,Y,RS                       ; RS = 0 (command)
        BSET    CTRL,Y,ENABLE                   ; strobe ENABLE
        BCLR    CTRL,Y,ENABLE                   ; clear ENABLE

        RTS

**********************************************************************
* print:
*       output a character from the accumulator to the LCD display
**********************************************************************

print:
        JSR     wait                            ; make sure display is not busy
        STAA    DPORT,Y                         ; put A on data bus
        BCLR    CTRL,Y,RW                       ; RW = 0 (write)
        BSET    CTRL,Y,RS                       ; RS = 1 (data)
        BSET    CTRL,Y,ENABLE                   ; strobe enable
        BCLR    CTRL,Y,ENABLE                   ; clear enable
        BCLR    CTRL,Y,RS                       ; RS = 0 (command)

        RTS

**********************************************************************
        
        END

7. LCD software for 68HCx05

(From Christopher E. Piggott, WZ2B)



*
* Routines for the Hitachi HD44780 LCD controller, as used in many
* Optrex and Densitron text-only displays
*
* Written for the 68HC05 by:
*
* Christopher Piggott, WZ2B
* Rochester, New York
*
*
* This stuff is open for anybody to use as they wish.  I only ask
* that you give me a credit for anything you use or distribute,
* and I warn that there is no warranty whatsoever.
*
***
*
* These routines do the same thing as the 68HC11 ones I just posted.
* They are a little more complete.  However, I think that they are
* also a little more confusing.
*
* This is a code fragment.  I'm not really worried about it, though,
* because there aren't nearly as many guys fooling with the 'HC05 as
* there are with the 'HC11.  Therefore, while this code is certainly
* executable "as-is", I'm mainly just providing it as a reference
* for people to learn how to write their own stuff.
*
* One other thing...in case anybody didn't catch this already, these
* displays are available on the surplus market fairly CHEAP.  Also,
* DigiKey sells them.  There are lots of configurations - I have mostly
* 20x4 and 16x2 versions.
*
*
*
* Good luck
*
* Chris, WZ2B, cep4478@ultb.isc.rit.edu
*

*
* This contains three separate files.  I'll let you split them up
* yourself.  The first two are short.
*
*

********************** THIS IS FILE LCD.H **********************

BLINK_ON:       EQU     00001001b
BLINK_OFF:      EQU     00001000b
DISPLAY_ON:     EQU     00001100b
DISPLAY_OFF:    EQU     00001000b
CURSOR_ON:      EQU     00001010b
CURSOR_OFF:     EQU     00001000b

PUTS:   .MACRO  ARG1
                CLRX
puts_loop#:     LDA     ARG1,X
                BEQ     puts_ret#
                JSR     putchar
                INCX
                BRA     puts_loop#

puts_ret#:      .ENDM

********************** THIS IS FILE PORTS.H **********************

DDRA            EQU     $0004
PRTA            EQU     $0000
DDRB            EQU     $0005
PRTB            EQU     $0001
DDRC            EQU     $0006
PRTC            EQU     $0002
DDRD            EQU     $0007
PRTD            EQU     $0003

RW              EQU     2
RS              EQU     3
ENABLE          EQU     4
BUSY            EQU     7

lcdctrl         EQU     PRTD
lcddata         EQU     PRTC
lcdcdir         EQU     DDRD
lcdddir         EQU     DDRC

OPTIONINIT:     EQU     11000010b
COPCR:          EQU     $1E

TCNTH           EQU     $18
TCNTL           EQU     $19
OCHR            EQU     $16
OCLR            EQU     $17
TCR             EQU     $12
TSR             EQU     $13

ONE_MSEC        EQU     307d

********************** THIS IS THE MAIN FILE, LCD.ASM **********************

                INCLUDE ports.h
                INCLUDE lcd.h

                PUBLIC  initlcd,putchar,lcdclr,lcdloc,lcdcmd

                CODE

*****************************************************************************
* SUBROUTINE
*       initlcd - initialize LCD display
*
* NOTES
*       destroys accumulator
*****************************************************************************

initlcd:
                LDA     #FFh                    * set ports to OUTPUT
                STA     lcdcdir
                STA     lcdddir

                LDA     #00111000b              * select 8-bit bus
                JSR     lcdcmd

                LDA     #(DISPLAY_ON^BLINK_ON)

                JSR     lcdcmd                  * with block style cursor

                LDA     #01h                    * clear screen
                JSR     lcdcmd

                RTS                             * that's all, folks

*****************************************************************************
* SUBROUTINE
*       putchar - put a byte out the LCD data port
*
* INPUT
*       A - byte to display
*****************************************************************************

putchar:
                JSR     wait                    * wait until LCD is ready
                STA     lcddata                 * store data on data bus

                BCLR    RW,lcdctrl              * RW=0 (write)
                BSET    RS,lcdctrl              * RS=1 (DATA)

                BSET    ENABLE,lcdctrl          * strobe ENABLE line
                BCLR    ENABLE,lcdctrl

                RTS                             * that's all, folks

*****************************************************************************
* SUBROUTINE
*       lcdcmd - send a command to the LCD display
*
* INPUT
*       A - byte to put into LCD command register
*****************************************************************************

lcdcmd:
                JSR     wait
                STA     lcddata
                BCLR    RW,lcdctrl
                BCLR    RS,lcdctrl
                BSET    ENABLE,lcdctrl
                BCLR    ENABLE,lcdctrl

                RTS
        
*****************************************************************************
* SUBROUTINE
*       wait - spinlocks until LCD display is ready to accept data or
*               a command
*****************************************************************************

wait:
                CLR     lcdddir                 * set data port to inputs

                BCLR    RS,lcdctrl              * RS=0 (select register)
                BSET    RW,lcdctrl              * RW=1 (read)
loop:
                BSET    ENABLE,lcdctrl          * strobe ENABLE on
                BRCLR   BUSY,lcddata,ready      * if busy, quit now

notready:
                BCLR    ENABLE,lcdctrl          * otherwise, turn off ENABLE
                BRA     loop                    * and test it again

ready:
                BCLR    ENABLE,lcdctrl          * turn off ENABLE bit
                DEC     lcdddir                 * reset data back to outputs

                RTS                             * that's all, folks

*****************************************************************************
* SUBROUTINE
*       puts - put a string on the LCD display
*
* INPUT
*       X - points to start of NULL-TERMINATED string
*
* NOTES
*       destroys accumulator
*****************************************************************************

puts:
                LDA     $160,X
                BEQ     puts_ret
                JSR     putchar
                INCX
                BRA     puts

puts_ret:
                RTS

*****************************************************************************
* SUBROUTINE
*       lcdloc - position the cursor of the LCD display
*
* INPUT
*       A - position to which to set cursor
*
* NOTES
*       MSB of A gets set on return
*****************************************************************************

lcdloc:
                ORA     #80h
                JSR     lcdcmd

                RTS

*****************************************************************************
* SUBROUTINE
*       lcdclr - clear LCD display
*
* NOTES
*       accumulator destroyed
*****************************************************************************

lcdclr:
                LDA     #01h
                JSR     lcdcmd

                RTS

8. Code for PC & 20x4 display

(From "J. R." Clyde A. Spidell)

Check the controler on your LCD if it is a HD44780, the following code will work if modified a bit.

This is my code.. it is for a 20x4 LCD but the commands are the same... You will need to midify the set cursor location routine...


//Code for 20x4 LCD
//Copyright (c) 1994
//by "J. R." Clyde A. Spidell

#include "dos.h"
#include "stdio.h"
#include "timer.h"
#include "lcd.h"
#include "charlcd.h"
#include "error.h"

//Constants
const unsigned int LCD_BASE = 0x100;
const unsigned int LCD_CMD = 0x00;
const unsigned int LCD_STATUS = 0x02;
const unsigned int LCD_READ = 0x06;
const unsigned int LCD_WRITE = 0x04;
const unsigned char CLEAR_DISPLAY = 0x01;
const unsigned char HOME_CURSOR = 0x02;
const unsigned char SET_DEFAULT_MOVE_CURSOR_RIGHT = 0x06;
const unsigned char DISPLAY_ON = 0x0C;
const unsigned char CURSOR_ON = 0x02;
const unsigned char BLINK_ON = 0x01;
const unsigned char MOVE_CURSOR_RIGHT = 0x14;
const unsigned char MOVE_CURSOR_LEFT = 0x10;
const unsigned char SET_8BITS_4LINES_10DOTS = 0x3C;
const unsigned char SET_DRAM_ADDRESS = 0x80;
const unsigned char BUSY_FLAG = 0x80;
const unsigned int ROWS = 4;
const unsigned int COLUMNS_PER_ROW = 20;
unsigned long SHORT_DELAY = 5;
unsigned long LONG_DELAY = 0xb00;

void CharLCDHomeCursor(void)
{	outp( LCD_BASE+LCD_CMD, HOME_CURSOR );
	microdelay( LONG_DELAY );
}

//ClearScreen.  The cursor is sent to the home position
void CharLCDClearScreen(void)
{
	LCDHomeCursor();
	outp( LCD_BASE+LCD_CMD, CLEAR_DISPLAY );
	microdelay( LONG_DELAY );
}

//Set Cursor Location
//This sets the cursor's position using row/column notation
//The top left corner is row 0, column 0
//The bottom right corner is row 3, column 19
void CharLCDSetCursorLoc(int row, int column)
{
	int position_number;
	row %= ROWS;
	column %= COLUMNS_PER_ROW;
	switch (row)
	{
		case 1:	row=2; break;
		case 2:	row=1; break;
	}
	position_number = row*COLUMNS_PER_ROW + column;
	if (position_number > 39)
		position_number += 24;
	outp( LCD_BASE+LCD_CMD, SET_DRAM_ADDRESS | position_number );
	microdelay( SHORT_DELAY );
}

//Print String
//This will display a given string on the lcd screen
void CharLCDPrintString(int row, int column, const char far* print_string)
{	char far* current_char;
	(const char far*) current_char = print_string;
	LCDSetCursorLoc(row,column);
	while (*current_char != 0 )
	{
		if ( *current_char == '\n' )
			column = COLUMNS_PER_ROW;
		else
		{	outp( LCD_BASE+LCD_WRITE, *current_char );
			microdelay( SHORT_DELAY );
			column++;
		}
		if ( column >= COLUMNS_PER_ROW )
		{	++row;
			column = 0;
			LCDSetCursorLoc( row, column );
		}
		current_char++;
	}
}

//Print Character
//This will display a given character on the lcd screen
void CharLCDPrintChar( int row, int column, const char c )
{	LCDSetCursorLoc(row,column);
	outp( LCD_BASE+LCD_WRITE, c );
	microdelay( SHORT_DELAY );
}

//Put Character
//This will display a given character on the lcd screen
//Arguments are:Character to be printed
//Assumes last cursor location
void CharLCDPutChar( char c )
{	outp( LCD_BASE+LCD_WRITE, c );
	microdelay( LONG_DELAY );
}

//LCD Initialization
void CharLCD(void)
{
	outp( LCD_BASE+LCD_CMD, SET_8BITS_4LINES_10DOTS );
	microdelay( LONG_DELAY );
	outp( LCD_BASE+LCD_CMD, SET_DEFAULT_MOVE_CURSOR_RIGHT );
	microdelay( LONG_DELAY );
	outp( LCD_BASE+LCD_CMD, CLEAR_DISPLAY);
	microdelay( LONG_DELAY );
	outp( LCD_BASE+LCD_CMD, DISPLAY_ON+CURSOR_ON+BLINK_ON );
	microdelay( LONG_DELAY );
}

9. Test Hitachi LCD with Parallel Port on a PC

(From David Tait (david.tait@man.ac.uk))


/*
 *  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) (note: early versions of Turbo C had
 *  a bug in the delay routine which is needed by this program).  To
 *  simplify things long delays are used rather than checking the BUSY
 *  flag.
 *
 *
 *  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 */
}


Please check attribution section for Author of this document! This article was written by filipg@repairfaq.org [mailto]. The most recent version is available on the WWW server http://www.repairfaq.org/filipg/ [Copyright] [Disclaimer]