  ;  *** COMPILED WITH SX/B VERSION 1.10 ***
                                 ;' =========================================================================
                                 ;'
                                 ;'   File...... SERIAL_LCD.SXB
                                 ;'   Purpose... Serial LCD Controller with 8 IO bits
                                 ;'   Author.... Jon Williams
                                 ;'              (c) Parallax, Inc. -- All Rights Reserved
                                 ;'   E-mail.... support@parallax.com
                                 ;'   Started... 06 OCT 2004
                                 ;'   Updated... 15 DEC 2004
                                 ;'
                                 ;' =========================================================================


                                 ;' -------------------------------------------------------------------------
                                 ;' Program Description
                                 ;' -------------------------------------------------------------------------
                                 ;'
                                 ;' Implements a simple serial LCD controller using the SX28. 
                                 ;'
                                 ;' This controller is designed to be command-compatible with SEETRON 2x16
                                 ;' LCD displays, but is fixed at 9600 baud and uses open-true serial mode. 
                                 ;' It also offers extended functions with RC of the SX28
                                 ;'
                                 ;' By sending the instruction prefix ($FE) twice, this LCD controller
                                 ;' enters "extended" function mode.  The following extended functions are 
                                 ;' currently available:
                                 ;'
                                 ;' $F0, ddr  : Set IO port DDR (1 = input, 0 = output; default is %11111111)
                                 ;' $F1       : Read IO port
                                 ;' $F2, bits : Write bits to IO port
                                 ;' $FF       : Read LCD RAM (from current cursor position)


                                 ;' -------------------------------------------------------------------------
                                 ;' Device Settings
                                 ;' -------------------------------------------------------------------------

DEVICE          SX28, OSCXT2, TURBO, STACKX, OPTIONX ;DEVICE          SX28, OSCXT2, TURBO, STACKX, OPTIONX

  ; DEFINE SYSTEM VARIABLES
__RAM         EQU  $00
__PARAMCNT    EQU  FSR
__PARAM1      EQU  $08
__PARAM2      EQU  $09
__PARAM3      EQU  $0A
__PARAM4      EQU  $0B
__TRISA       EQU  $F9
__TRISB       EQU  $FA
__TRISC       EQU  $FB
__INTPARAMFSR EQU  $FC
__REMAINDER   EQU  $08

; Define ShiftIn & ShiftOut Constants
LSBFIRST      EQU  0
MSBFIRST      EQU  1
MSBPRE        EQU  0
LSBPRE        EQU  1
MSBPOST       EQU  2
LSBPOST       EQU  3

FREQ 4_000_000                   ;FREQ            4_000_000



                                 ;' -------------------------------------------------------------------------
                                 ;' IO Pins
                                 ;' -------------------------------------------------------------------------

Sio           EQU  RA.0          ;Sio             VAR     RA.0                    ' pull-up via 4.7K

LcdE          EQU  RA.1          ;LcdE            VAR     RA.1                    ' pull-down via 4.7K

LcdRW         EQU  RA.2          ;LcdRW           VAR     RA.2

LcdRS         EQU  RA.3          ;LcdRS           VAR     RA.3

TRIS_Ctrl     EQU  'TRIS_A'      ;TRIS_Ctrl       VAR     TRIS_A


LcdBus        EQU  RB            ;LcdBus          VAR     RB                      ' RB.0 --> DB0, ...

TRIS_Lcd      EQU  'TRIS_B'      ;TRIS_Lcd        VAR     TRIS_B


IoPort        EQU  RC            ;IoPort          VAR     RC

TRIS_IO       EQU  'TRIS_C'      ;TRIS_IO         VAR     TRIS_C



                                 ;' -------------------------------------------------------------------------
                                 ;' Constants
                                 ;' -------------------------------------------------------------------------

Baud          EQU  "OT9600"      ;Baud            CON     "OT9600"

Cmd           EQU  $FE           ;Cmd             CON     $FE                     ' LCD command prefix


                                 ;' shortcut string constants

DELAY         EQU  "GOSUB US_Delay, " ;DELAY		CON	"GOSUB US_Delay, "

LCDCMD        EQU  "GOSUB LCD_Cmd, " ;LCDCMD		CON	"GOSUB LCD_Cmd, "

LCDOUT        EQU  "GOSUB LCD_Write, " ;LCDOUT          CON     "GOSUB LCD_Write, "

LCDIN         EQU  "GOSUB LCD_Read, " ;LCDIN           CON     "GOSUB LCD_Read, " 

SERTX         EQU  "GOSUB TX_Byte, " ;SERTX		CON	"GOSUB TX_Byte, "

SERRX         EQU  "GOSUB RX_Byte, " ;SERRX        	CON     "GOSUB RX_Byte, "



                                 ;' -------------------------------------------------------------------------
                                 ;' Variables
                                 ;' -------------------------------------------------------------------------

serByte       EQU  0x0C          ;serByte         VAR     Byte                    ' serial IO byte


regAddr       EQU  0x0D          ;regAddr         VAR     Byte                    ' return address (subs)

temp1         EQU  0x0E          ;temp1           VAR     Byte			' parameters

temp2         EQU  0x0F          ;temp2           VAR     Byte



                                 ;' =========================================================================
RESET __PROGSTART                ;  PROGRAM Start
__BREAK:                        
  BREAK                         
  RETP                          
__PROGSTART:                    
  JNB TO,__RESTORETRIS          
  JB PD,__POWERUP               
  CLR W                         
  MODE $09                      
  MOV !RB,W                     
  MOV FSR,W                     
  MODE $0F                      
  CJE FSR,#0,__POWERUP          
  CJNE FSR,#$FF,__RESTORETRIS   
__POWERUP:                      
  CLR RA                        
  CLR RB                        
  CLR RC                        
  CLR FSR                       
  SB FSR.4                      
  SETB FSR.3                    
  CLR IND                       
  IJNZ FSR,$-3                  
  MOV FSR,#__TRISA              
  MOV IND,#255                  
  INC FSR                       
  MOV IND,W                     
  INC FSR                       
  MOV IND,W                     
__RESTORETRIS:                  
  MOV FSR,#__TRISA              
  MOV !RA,IND                   
  INC FSR                       
  MOV !RB,IND                   
  INC FSR                       
  MOV !RC,IND                   
  BANK $00                      
  JMP Start                     

                                 ;' =========================================================================


                                 ;' -------------------------------------------------------------------------
                                 ;' Subroutines / Jump Table
                                 ;' -------------------------------------------------------------------------

                                 ;' Use: GOSUB US_Delay, base, multiplier
                                 ;' -- pause for 'base' x 'multiplier' microseconds

US_Delay:                        ;US_Delay:                                       ' delay x * y microseconds

  JMP @_US_Delay                 ;  GOTO @_US_Delay



                                 ;' Initialize LCD for 8-bit, 2-line interface

Init_LCD:                        ;Init_LCD:                                       ' initialize LCD

  JMP @_Init_LCD                 ;  GOTO @_Init_LCD



                                 ;' Use: GOSUB LCD_Cmd, cmdByte
                                 ;' -- send 'cmdByte' to LCD with RS line low

LCD_Cmd:                         ;LCD_Cmd:                                        ' write command byte

  JMP @_LCD_Cmd                  ;  GOTO @_LCD_Cmd



                                 ;' Use: GOSUB LCD_Cmd, char
                                 ;' -- send 'char' to LCD with RS line high

LCD_Write:                       ;LCD_Write:                                      ' write character byte

  JMP @_LCD_Write                ;  GOTO @_LCD_Write



                                 ;' Use: GOSUB LCD_Read, @char
                                 ;' -- returns value at LCD cursor position in 'char'

LCD_Read:                        ;LCD_Read:                                       ' read from cursor position

  JMP @_LCD_Read                 ;  GOTO @_LCD_Read



                                 ;' Use: GOSUB RX_Byte, @char
                                 ;' -- reads byte from serial input and places in 'char'

RX_Byte:                         ;RX_Byte:                                        ' receive serial byte

  JMP @_RX_Byte                  ;  GOTO @_RX_Byte



                                 ;' Use: GOSUB TX_Byte, char
                                 ;' -- transmits 'char' over serial connnection

TX_Byte:                         ;TX_Byte:                                        ' transmit serial byte

  JMP @_TX_Byte                  ;  GOTO @_TX_Byte



                                 ;' -------------------------------------------------------------------------
                                 ;' Program Code
                                 ;' -------------------------------------------------------------------------

Start:                           ;Start:

  MODE $0F                       ;  TRIS_Ctrl = %0001                             ' RA.0 = input (serial)
  MOV FSR,#__TRISA              
  MOV W,#%0001                  
  MOV IND,W                     
  MOV !RA,W                     
  BANK $00                      

  MODE $0F                       ;  TRIS_Lcd = %00000000                          ' all outputs
  MOV FSR,#__TRISB              
  MOV W,#%00000000              
  MOV IND,W                     
  MOV !RB,W                     
  BANK $00                      

  CLR __PARAMCNT                 ;  GOSUB Init_LCD
  CALL Init_LCD                 


Main:                            ;Main:

 ERROR 16  UNKNOWN COMMAND "LET"  ;   SERRX @serByte				' wait for byte

  CJE serByte,#Cmd,Do_Command    ;  IF serByte = Cmd THEN Do_Command              ' command?      


Do_Write:                        ;Do_Write:

 ERROR 16  UNKNOWN COMMAND "LET"  ;   LCDOUT serByte				' no, write char byte

  JMP Main                       ;  GOTO Main


Do_Command:                      ;Do_Command:

 ERROR 16  UNKNOWN COMMAND "LET"  ;   SERRX @serByte                      		' get command byte

  CJE serByte,#Cmd,Extended_Cmd  ;  IF serByte = Cmd THEN Extended_Cmd            ' extended command?

 ERROR 16  UNKNOWN COMMAND "LET"  ;   LCDCMD serByte                        	' no, write cmd byte

  JMP Main                       ;  GOTO Main


Extended_Cmd:                    ;Extended_Cmd:

 ERROR 16  UNKNOWN COMMAND "LET"  ;   SERRX @serByte                       		' get extended command


Set_IO:                          ;Set_IO:

  CJNE serByte,#$F0,Read_IO      ;  IF serByte <> $F0 THEN Read_IO		' check command

 ERROR 16  UNKNOWN COMMAND "LET"  ;   SERRX @serByte                       		' get port configuration

  MODE $0F                       ;  TRIS_IO = serByte                             ' setup IO port
  MOV !RC,serByte               
  MOV FSR,#__TRISC              
  MOV IND,serByte               
  BANK $00                      

  JMP Main                       ;  GOTO Main


Read_IO:                         ;Read_IO:

  CJNE serByte,#$F1,Write_IO     ;  IF serByte <> $F1 THEN Write_IO		' check command

 ERROR 16  UNKNOWN COMMAND "LET"  ;   DELAY 250, 4                        		' delay for host setup

 ERROR 16  UNKNOWN COMMAND "LET"  ;   SERTX IoPort                         		' send port bits to host

  JMP Main                       ;  GOTO Main


Write_IO:                        ;Write_IO:

  CJNE serByte,#$F2,Read_Byte    ;  IF serByte <> $F2 THEN Read_Byte		' check command

 ERROR 16  UNKNOWN COMMAND "LET"  ;   SERRX @serByte                       		' get port bits from host

  MOV IoPort,serByte             ;  IoPort = serByte                              ' put bits on the port

  JMP Main                       ;  GOTO Main


Read_Byte:                       ;Read_Byte:

  CJNE serByte,#$FF,Cmd_X        ;  IF serByte <> $FF THEN Cmd_X			' check command

 ERROR 16  UNKNOWN COMMAND "LET"  ;   LCDIN @serByte                      		' read LCD (cursor pos)

 ERROR 16  UNKNOWN COMMAND "LET"  ;   DELAY 250, 4                        		' delay for host setup

 ERROR 16  UNKNOWN COMMAND "LET"  ;   SERTX serByte                        		' send byte to to host

  JMP Main                       ;  GOTO Main


Cmd_X:                           ;Cmd_X:

                                 ;  ' for future expansion
  JMP Main                       ;  GOTO Main



                                 ;' -------------------------------------------------------------------------
                                 ;' Page 1 Code
                                 ;' -------------------------------------------------------------------------

Page_1:                          ;Page_1:

ORG $200                         ;  ADDRESS $200



_US_Delay:                       ;_US_Delay:                                    	' delay x * y microseconds

  MOV temp1,__PARAM1             ;  temp1 = __PARAM1

  MOV temp2,__PARAM2             ;  temp2 = __PARAM2

  MOV __PARAM1,temp1             ;  PAUSEUS temp1 * temp2
  MOV __PARAM2,temp2            
  DJNZ __PARAM2,$-0             
  DJNZ __PARAM1,$-4             

  RETP                           ;  RETURN



_LCD_Cmd:                        ;_LCD_Cmd:                                     	' write command byte

  CLRB LcdRS                     ;  LcdRS = 0

  JMP _LCD_Out                   ;  GOTO _LCD_Out



_LCD_Write:                      ;_LCD_Write:                                   	' write character byte

  SETB LcdRS                     ;  LcdRS = 1



_LCD_Out:                        ;_LCD_Out:                                     	' write byte to LCD

  MOV LcdBus,__PARAM1            ;  LcdBus = __PARAM1



_Blip_E:                         ;_Blip_E:                                      	' move bus into LCD

  SETB LcdE                      ;  LcdE = 1

  MOV __PARAM1,#2                ;  GOSUB _US_Delay, 2, 1
  MOV __PARAM2,#1               
  MOV __PARAMCNT,#2             
  CALL _US_Delay                

  CLRB LcdE                      ;  LcdE = 0

  MOV __PARAM1,#40               ;  GOSUB _US_Delay, 40, 1			' instruction delay
  MOV __PARAM2,#1               
  MOV __PARAMCNT,#2             
  CALL _US_Delay                

  RETP                           ;  RETURN



_LCD_Read:                       ;_LCD_Read:

  MOV regAddr,__PARAM1           ;  regAddr = __PARAM1                            ' save return address

  MODE $0F                       ;  TRIS_Lcd = %11111111                          ' make LCD bus inputs
  MOV FSR,#__TRISB              
  MOV W,#%11111111              
  MOV IND,W                     
  MOV !RB,W                     
  BANK $00                      

  SETB LcdRS                     ;  LcdRS = 1                                     ' character mode

  SETB LcdRW                     ;  LcdRW = 1                                     ' read mode

  SETB LcdE                      ;  LcdE = 1                                      ' move byte to bus

  MOV temp1,LcdBus               ;  temp1 = LcdBus                                ' grab the byte

  MOV W,#__RAM                   ;  __RAM(regAddr) = temp1                        ' put byte into passed var
  ADD W,regAddr                 
  MOV FSR,W                     
  MOV IND,temp1                 
  BANK $00                      

  CLRB LcdE                      ;  LcdE = 0                                      ' complete the read

  CLRB LcdRW                     ;  LcdRW = 0                                     ' return to write mode

  MODE $0F                       ;  TRIS_Lcd = %00000000                          ' make LCD bus outputs
  MOV FSR,#__TRISB              
  MOV W,#%00000000              
  MOV IND,W                     
  MOV !RB,W                     
  BANK $00                      

  RETP                           ;  RETURN



_Init_LCD:                       ;_Init_LCD:

  MOV __PARAM1,#250              ;  GOSUB _US_Delay, 250, 60                    	' power-up delay, 15 ms
  MOV __PARAM2,#60              
  MOV __PARAMCNT,#2             
  CALL _US_Delay                

  MODE $0F                       ;  TRIS_Lcd = %00000000                          ' all outputs
  MOV FSR,#__TRISB              
  MOV W,#%00000000              
  MOV IND,W                     
  MOV !RB,W                     
  BANK $00                      

  MOV LcdBus,#%00110000          ;  LcdBus = %00110000				' 8-bit interface

  CLR __PARAMCNT                 ;  GOSUB _Blip_E
  CALL _Blip_E                  

  MOV __PARAM1,#250              ;  GOSUB _US_Delay, 250, 18                    	' delay 4.5 ms
  MOV __PARAM2,#18              
  MOV __PARAMCNT,#2             
  CALL _US_Delay                

  CLR __PARAMCNT                 ;  GOSUB _Blip_E
  CALL _Blip_E                  

  MOV __PARAM1,#100              ;  GOSUB _US_Delay, 100, 1                     	' delay 100 us
  MOV __PARAM2,#1               
  MOV __PARAMCNT,#2             
  CALL _US_Delay                

  CLR __PARAMCNT                 ;  GOSUB _Blip_E
  CALL _Blip_E                  

  MOV __PARAM1,#%00111000        ;  GOSUB _LCD_Cmd, %00111000                   	' multi-line, 5x7 font
  MOV __PARAMCNT,#1             
  CALL _LCD_Cmd                 

  MOV __PARAM1,#%00001100        ;  GOSUB _LCD_Cmd, %00001100                   	' display on, no cursor
  MOV __PARAMCNT,#1             
  CALL _LCD_Cmd                 

  MOV __PARAM1,#%00000110        ;  GOSUB _LCD_Cmd, %00000110                   	' auto-increment cursor
  MOV __PARAMCNT,#1             
  CALL _LCD_Cmd                 

  MOV __PARAM1,#%00000001        ;  GOSUB _LCD_Cmd, %00000001                   	' clear and home LCD
  MOV __PARAMCNT,#1             
  CALL _LCD_Cmd                 

  MOV __PARAM1,#250              ;  GOSUB _US_Delay, 250, 6			' delay for CLS
  MOV __PARAM2,#6               
  MOV __PARAMCNT,#2             
  CALL _US_Delay                

  RETP                           ;  RETURN



                                 ;' -------------------------------------------------------------------------
                                 ;' Page 2 Code
                                 ;' -------------------------------------------------------------------------

Page_2:                          ;Page_2:

ORG $400                         ;  ADDRESS $400



_RX_Byte:                        ;_RX_Byte:

  MOV regAddr,__PARAM1           ;  regAddr = __PARAM1                            ' save return address

 ERROR  9  SYNTAX ERROR "Baud"  ;   SERIN Sio, Baud, temp1                        ' receive a byte

  MOV W,#__RAM                   ;  __RAM(regAddr) = temp1                        ' put byte into passed var
  ADD W,regAddr                 
  MOV FSR,W                     
  MOV IND,temp1                 
  BANK $00                      

  RETP                           ;  RETURN



_TX_Byte:                        ;_TX_Byte:

  MOV temp1,__PARAM1             ;  temp1 = __PARAM1                              ' save byte to send

  MOV __PARAM1,temp1             ;  SEROUT Sio, Baud, temp1                       ' send the byte
  MOV __PARAM2,#10              
 ERROR  9  SYNTAX ERROR "Baud"  ; 

  RETP                           ;  RETURN

