;****************************************************************************
; Test program for LCD 4-bits interface routines
;
; Also, this file can be used as an skeleton to develop an application
; using an LCD display module.
;
; If interrupts are not used all code presented between the ORG
; 0x004 directive and the label main can be removed. In addition
; the variable assignments for 'w_temp' and 'status_temp' can
; be removed.
;
;****************************************************************************
;
	list      p=16F84          ; list directive to define processor
	#include <p16F84x.inc>     ; processor specific variable definitions

	__CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _RC_OSC

; '__CONFIG' directive is used to embed configuration data within .asm file.
; The lables following the directive are located in the P16F84X.INC file. The
; following values are possible:
;
;      _CP_ON       Code Protection ON
;      _CP_OFF      Code Protection OFF
;
;      _PWRTE_ON    Power-Up Timer Enable Bit ON
;      _PWRTE_OFF   Power-Up Timer Enable Bit ON
;
;      _WDT_ON      Watch Dog Timer ON
;      _WDT_OFF     Watch Dog Timer OFF
;
;      _LP_OSC      Low Power crystal oscillator (32Khz-200Khz)
;      _XT_OSC      XT crystal oscillator (100Khz-4MHz)
;      _HS_OSC      HS crystal oscillator (4Mhz-10Mhz)
;      _RC_OSC      RC oscillator



;****************************************************************************
; Definitions for variables allocated in internal RAM
;****************************************************************************

; Context saving when entering into de Interrupt Service routine
;----------------------------------------------------------------------------
; #define USE_ISR      ; un-comment this definition if interrupts are  used

 ifdef USE_ISR
    cblock 0x10
       w_temp        ; variable used for context saving
       status_temp   ; variable used for context saving
    endc
 else
    cblock 0x10
    endc
 endif


; LCD module
;----------------------------------------------------------------------------

; LCD presentation atributes:

#define DISPLAY_ON   0x04 | 0x08     ; Display ON
#define CURSOR_ON    0x02 | 0x08     ; Cursor ON
#define BLINKING_ON  0x01 | 0x08     ; Blink character at cursor position

; LCD variables:

   cblock
      LCD_aux      ; nibble to write is kept in low nibble of this byte
      LCD_addr     ; current address
      LCD_saveW    ; to save W
      LCD_char     ; char to write to LCD (used only by WriteChar, putc)
   endc

; Variables for the timming routines (Wait_Wx1ms, Wait_Wx100us, Wait_Wx10us)
;----------------------------------------------------------------------------

   cblock
      nWaitTime
      nWaitT2
      nWaitT3
   endc

   cblock
      nAux1
   endc


;****************************************************************************
; Data in EEPROM
;****************************************************************************
;   de "Probando esto.",0

;****************************************************************************
; Program code starts here
;****************************************************************************
Reset:
	org    0x000         ; processor reset vector
	goto   Start         ; go to beginning of program

;****************************************************************************
;  Interrupt Service Routine (ISR)
;****************************************************************************
ISR:
	org    0x004             ; interrupt vector location
 ifndef USE_ISR
	goto   Start
 else
	movwf  w_temp            ; save off current W register contents
	movf   STATUS,w          ; move status register into W register
	movwf  status_temp       ; save off contents of STATUS register


; isr code can go here or be located as a call subroutine elsewhere


	movf    status_temp,w     ; retrieve copy of STATUS register
	movwf   STATUS            ; restore pre-isr STATUS register contents
	swapf   w_temp,f
	swapf   w_temp,w          ; restore pre-isr W register contents
	retfie                    ; return from interrupt
 endif


;****************************************************************************
;  Reset code starts here
;****************************************************************************
Start:
    ; initialize port b: all lines are outputs with weak pull-up
	clrf   portb                ; output data latches to 0
	bsf    status,rp0           ; select bank 1
	bcf    option_reg,not_rbpu  ; enable weak pull-up resistors
	clrf   trisb                ; all lines as outputs
	bcf    status,rp0           ; select bank 0


;****************************************************************************
; Main program loop starts here
;****************************************************************************
Main:

; Test basic routines
;
	call   LCD_Initialize
	movlw  'H'
	call   LCD_SendData    ;1
	movlw  'o'
	call   LCD_SendData    ;2
	movlw  'l'
	call   LCD_SendData    ;3
	movlw  'a'
	call   LCD_SendData    ;4
	movlw  ','
	call   LCD_SendData    ;5
	movlw  ' '
	call   LCD_SendData    ;6
	movlw  'D'
	call   LCD_SendData    ;7
	movlw  'o'
	call   LCD_SendData    ;8
	movlw  0x0c0           ; set address
	call   LCD_SendCmd
	movlw  'r'
	call   LCD_SendData    ;1
	movlw  'i'
	call   LCD_SendData    ;2
	movlw  'n'
	call   LCD_SendData    ;3
	movlw  'd'
	call   LCD_SendData    ;4
	movlw  'a'
	call   LCD_SendData    ;5
	movlw  ' '
	call   LCD_SendData    ;6
	movlw  '3'
	call   LCD_SendData    ;7
	movlw  '.'
	call   LCD_SendData    ;8

;  Test LCD_Home and logical addressing routines

	call   wait_5secs
	call   LCD_Home
	movlw  'E'
	call   LCD_putc    ;1
	movlw  's'
	call   LCD_putc    ;2
	movlw  't'
	call   LCD_putc    ;3
	movlw  'o'
	call   LCD_putc    ;4
	movlw  ' '
	call   LCD_putc    ;5
	movlw  'e'
	call   LCD_putc    ;6
	movlw  's'
	call   LCD_putc    ;7
	movlw  ' '
	call   LCD_putc    ;8
	movlw  'u'
	call   LCD_putc    ;9
	movlw  'n'
	call   LCD_putc    ;10
	movlw  'a'
	call   LCD_putc    ;11
	movlw  ' '
	call   LCD_putc    ;12
	movlw  'p'
	call   LCD_putc    ;13
	movlw  'r'
	call   LCD_putc    ;14
	movlw  'u'
	call   LCD_putc    ;15
	movlw  'e'
	call   LCD_putc    ;16

loop2:                          ; RB0 --> LED + 470 ohm --> gnd
	bsf    portb,0          ; LED ON for 500ms
	movlw  250
	call   Wait_Wx1ms
	movlw  250
	call   Wait_Wx1ms
	bcf    portb,0          ; LED OFF for 500ms
	movlw  250
	call   Wait_Wx1ms
	movlw  250
	call   Wait_Wx1ms
	goto   loop2

wait_5secs:
	movlw  20               ; wait 5 secs = 20x250ms
	movwf  nAux1
loop1:  movlw  250
	call   Wait_Wx1ms
	decfsz nAux1,f
	goto   loop1
	return

;****************************************************************************
;****************************************************************************
;**  Subroutines start here
;****************************************************************************
;****************************************************************************


;****************************************************************************
;  4-bit LCD control routines for the PIC16F84
;
;  This routines are intended to control an LCD display built around the
;  HD44780 Hitachi controller, or compatible, using only one
;  controller chip (typically 1x16, 1x20, 2x16 or 2x20 displays). The
;  HD44780 is very common and widely used by most display makers.
;
;  C.Salmeron   27/Mar/99
;
;----------------------------------------------------------------------------
; USAGE:
;----------------------------------------------------------------------------
;   STEP 1. Un-comment the following definitions and code block and move it
;           to the begining of the program, at an appropiate place.
;
;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
;; LCD presentation atributes:
;
;#define DISPLAY_ON   0x04 | 0x08     ; Display ON
;#define CURSOR_ON    0x02 | 0x08     ; Cursor ON
;#define BLINKING_ON  0x01 | 0x08     ; Blink character at cursor position
;
;; LCD variables:
;
;   cblock
;      LCD_aux     ; the nibble to write is kept in low nibble of this byte
;      LCD_addr    ; cursor position (physical address)
;      LCD_saveW   ; to save W
;   endc
;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
;----------------------------------------------------------------------------
; STEP 2:   LCD Configuration
;
;  It is assumed that LCD is conected to port B as follows:
;     D7 -> RB7
;     D6 -> RB6
;     D5 -> RB5
;     D4 -> RB4
;     RS -> RB3
;     RW -> RB2
;     E  -> RB1
;
;  If you desire to change the above described configuration, take into
;  account that data lines D7-D4 must be assigned to a nibble of a PIC16F84
;  port, either the high nibble of PORTB (RB7-RB4) or the low nibble of
;  either porta (RA3-RA0) or portb (RB3-RB0). D7 must be assigned to the
;  most significative bit of the chosen port nibble.

;  If the configuration is changed, change the following definitions
;  as appropiate (it is not necessary to move this code to another part of
;  the program):
;
ioLCD_E     equ   1          ;port bit assigned to LCD control line E
ioLCD_RW    equ   2          ;port bit assigned to LCD control line RW
ioLCD_RS    equ   3          ;port bit assigned to LCD control line RS
ioLCD_D7    equ   7          ;port bit assigned to LCD data line D7

#define port_E    portb      ;port used for LCD control line E
#define port_RW   portb      ;port used for LCD control line RW
#define port_RS   portb      ;port used for LCD control line RS
#define port_LCD  portb      ;port used for LCD data lines
#define tris_LCD  trisb      ;tris register for LCD data lines
#define ioMaskW   00000000b  ;mask to program LCD data port to write to LCD
			       ; 0 = all RBx io pins are outputs
#define ioMaskR   11110000b  ;mask to program LCD data port to read from LCD
			       ; 1 = io pins RB7-RB4 are inputs

#define LCD_lines  1         ; display type: 1 line or 2 lines

;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
;-- ONLY FOR MY EYES --------------------------------------------------------
;
;  My LCD moule is wired as follows:
;
;    14  [Cinta plana-gris]  (D7)
;    ...             ...
;     8  [Cinta plana-gris]  (D1)   - pines no usados: dejar al aire
;     7  [Cinta plana-azul]  (D0)

;     6  [Naranja]   (E)
;     5  [Verde]     (R/W)
;     4  [Amarillo]  (RS)
;     3  [Azul]      (Vo)  = Cursor de potenciometro 10K entre +5 y 0v/-5v
;     2  [Rojo]      (Vdd) = +5v
;     1  [Negro]     (Vss) = 0v
;
;----------------------------------------------------------------------------
; STEP 3 (and last): Optional
;   To save program space, remove the LCD routines not used by you app.
;
; The following routines are available:
;
; a) Low level functions. (All necessary. Do not remove any of them):
;  iLCD_WaitReady     // wait until LCD module not busy
;  iLCD_SendByte      // Send to LCD the byte (data or command) received in W
;
; b) Basic functions (All necessary. Do not remove any of them):
;  LCD_SendData       // Write to LCD the data byte received in W
;                        uses: iLCD_SendByte, iLCD_WaitReady
;  LCD_SendCmd        // Send a command to LCD
;                        uses: iLCD_WaitReady, iLCD_SendByte
;  LCD_Initialize     // Inicialize LCD. MUST BE THE FIRST FUNCTION TO CALL
;                        Uses: LCD_SendCmd
;
; c) Additional functions (remove any not needed in your app.):
;  LCD_CleanUp        // Clear display and move cursor to home position
;  LCD_CursorLeft     // Shift cursor one position to the left
;  LCD_CursorRight    // Shift cursor one position to the right
;  LCD_Home           // Move cursor and display window to home position
;  LCD_SetDataAddr    // Set address for data write (DD RAM)
;  LCD_GetAddr        // Return current address

;  LCD_putc           // Write char to current address.
;  LCD_WriteChar      // escribir char en posic indicada
;  LCD_puts           // write string *dptr to current cursor position
;  LCD_putns          // write string *sp to current cursor position
;
;****************************************************************************

;===========================================================================
;===========================================================================
;; a) Internal functions. All needed
;===========================================================================
;===========================================================================

;----------------------------------------------------------------------------
; iLCD_WaitReady  (LWR)
;   Wait until LCD is not busy.
;----------------------------------------------------------------------------
iLCD_WaitReady:
   movwf LCD_saveW            ; save w

   ; set all data lines as inputs
   bsf   status,rp0           ; select bank 1
   movlw ioMaskR              ; mask to set LCD data lines as inputs
   movwf tris_LCD             ; use mask in W to program port
   bcf   status,rp0           ; select bank 0

   bcf   port_RS,ioLCD_RS    ; RS=0   signal it is a command
   bsf   port_RW,ioLCD_RW    ; R/W=1  signal it is a read operation
   nop                       ; wait 1us to comply with timming requirements

LWR_IsBusy?:
   bsf   port_E,ioLCD_E      ; E=1  enable LCD
   nop                       ; wait 1us to comply with timming requirements
   movf  port_LCD,w          ; read data lines
   movwf LCD_aux             ; and save
   bcf   port_E,ioLCD_E      ; E=0  disable LCD
   btfss LCD_aux,ioLCD_D7    ; test busy line.
   goto  LWR_NotBusy         ; jump if LCD not busy

   ; LCD is busy. Read the low order nibble and ignore it
   bsf   port_E,ioLCD_E      ; E=1  enable LCD
   nop                       ; wait 1us to comply with timming requirements
   bcf   port_E,ioLCD_E      ; E=0  disable LCD
   goto  LWR_IsBusy?         ; loop

; LCD is not busy. Read and ignore low nibble, and return
LWR_NotBusy:
   bsf   port_E,ioLCD_E      ; E=1  enable LCD
   nop                       ; wait 1us to comply with timming requirements
   bcf   port_E,ioLCD_E      ; E=0  disable LCD

   ; reprogram all LCD data lines as outputs
   bsf   status,rp0          ; select bank 1
   movlw ioMaskW             ; mask to set LCD data lines as outputs
   movwf tris_LCD            ; program port
   bcf   status,rp0          ; select bank 0

   movf  LCD_saveW,w         ; restore w
   return

;----------------------------------------------------------------------------
;  iLCD_SendByte (LSB)
;    Send to LCD the byte (data or command) received in W. It is assumed
;    that lines RS and RW have been properly set before calling this function.
;
;    As it is a 4-bits interface, data is sent as two nibbles, one nibble
;    at a time.
;----------------------------------------------------------------------------
iLCD_SendByte:
   ; high nibble must be sent first
   movwf LCD_saveW          ; save byte to send
   movwf LCD_aux            ; high nibble of byte to send is moved to ..
   swapf LCD_aux,f          ; .. low nibble of LCD_aux
   call  LSB_WriteNibble    ; send low nibble of LCD_aux

   ; now prepare and send low nibble
   movf  LCD_saveW,w        ; retrieve byte to send
   movwf LCD_aux            ; move it to LCD_aux
;  goto  LSB_WriteNibble    ; send low nibble of LCD_aux

; Send the low nibble of LCD_aux to LCD
LSB_WriteNibble:
   bsf    port_E,ioLCD_E      ; E=1    enable LCD
   movlw  0x0f
   andwf  LCD_aux,f           ; high nibble of data to zeros
 if ioLCD_D7 == 7        ; if D7-D4 conected to high nibble of PIC port
   andwf  port_LCD,f          ; clear high nibble of port (data lines D7-D4)
   swapf  LCD_aux,f           ; move data to send to high nibble
 else                    ; else if D7-D4 conected to low nibble
   movlw  0x0f0
   andwf  port_LCD,f          ; clear low nibble of port (data lines D3-D0)
 endif
   movf   LCD_aux,w           ; move data to W
   iorwf  port_LCD,f          ; write data to port
   nop
   bcf    port_E,ioLCD_E      ; E=0. LCD reads data when falling edge of E
   movlw  5                   ; wait 50 uS for LCD internal process
   goto   Wait_Wx10us


;===========================================================================
;===========================================================================
;; b) Basic functions. All needed
;===========================================================================
;===========================================================================

;----------------------------------------------------------------------------
;  LCD_SendData
;    Write to LCD the data byte received in W. After write, the address is
;    incremented or decremented by one, according to the entry mode
;----------------------------------------------------------------------------
LCD_SendData:
   call  iLCD_WaitReady      ; wait until LCD not bussy
   bcf   port_RW,ioLCD_RW    ; R/W=0  signal it is a write operation
   bsf   port_RS,ioLCD_RS    ; RS=1   signal it is a data byte
   goto  iLCD_SendByte

;----------------------------------------------------------------------------
; LCD_SendCmd
;    Send to LCD the command byte received in W
;
; LCD_SetDataAddr
;   Set address for data write to the address specified in W
;   (line 1: 80h-A7h) or (line 2: C0h-E7h). Note that changing the address
;   is just sending the address as a command: bit 7 to one is the command
;   "set address"
;---------------------------------------------------------------------------
LCD_SetDataAddr:
LCD_SendCmd:
   call  iLCD_WaitReady      ; wait until LCD not bussy
   bcf   port_RW,ioLCD_RW    ; R/W=0  signal it is a write operation
   bcf   port_RS,ioLCD_RS    ; RS=0   signal it is a command byte
   goto  iLCD_SendByte

;----------------------------------------------------------------------------
;  LCD_Initialize
;   At power on, if power supply raises from 0v to 4.5v in less than 10mS 
;   but not faster than 1mS, the LCD module will default to the following
;   settings:
;      1. Clear display
;      2. 8-bits interface, 1 line display, 5x7 dot font
;      3. Display OFF, Cursor ON, Blink OFF
;      4. Entry mode: increment, no display shift
;      5. DD RAM is selected
;
;   Whether the LCD is initialized or not, this function (LCD_Initialize)
;   will change the LCD module to the following settings:
;      1. Clear display
;      2. 4-bits interface, 2 lines display, 5x7 dot font
;      3. Display ON, Cursor ON, Blink OFF
;      4. Entry mode: increment, no display shift
;      5. DD RAM is selected
;
;   For other settings at initialization time, change this routine.
;----------------------------------------------------------------------------
LCD_Initialize:
   movlw  15
   call   Wait_Wx1ms       ;  wait 15mS after power-up
 if LCD_lines==2
   movlw  28h              ;  4-bits interface, 2 lines, 5x7 dot font
 else
   movlw  20h              ;  4-bits interface, 1 line, 5x7 dot font
 endif
   call   LCD_SendCmd
   movlw  5
   call   Wait_Wx1ms       ;  wait 5mS
 if LCD_lines==2
   movlw  28h              ;  4-bits interface, 2 lines, 5x7 dot font
 else
   movlw  20h              ;  4-bits interface, 1 line, 5x7 dot font
 endif
   call   LCD_SendCmd
   movlw  10               ;  wait 100uS
   call   Wait_Wx10us
   movlw  06h              ;  entry mode: increment mode, no shift display
   call   LCD_SendCmd
   movlw  0Eh              ;  Cursor ON, Display ON, Blinking OFF
   call   LCD_SendCmd
   movlw  01h              ;  Clear display and cursor home
   goto   LCD_SendCmd

;===========================================================================
;===========================================================================
;; c) Additional functions. Remove those not used by your application
;===========================================================================
;===========================================================================

;----------------------------------------------------------------------------
;  LCD_CleanUp
;   Clears display and returns cursor to home position (address 00)
;----------------------------------------------------------------------------
LCD_CleanUp:
   movlw 0x01                 ; command 01
   call  LCD_SendCmd
   movlw 2                    ; wait 2 mS
   goto  Wait_Wx1ms

;----------------------------------------------------------------------------
;  LCD_CursorLeft:
;   Shift cursor one position to the left
;----------------------------------------------------------------------------
LCD_CursorLeft:
   movlw 0x10
   goto  LCD_SendCmd

;----------------------------------------------------------------------------
;  LCD_CursorRight:
;   Shift cursor one position to the right
;----------------------------------------------------------------------------
LCD_CursorRight:
   movlw 0x14
   goto  LCD_SendCmd

;----------------------------------------------------------------------------
;  LCD_Home:
;   Command 02H: Returns cursor to home position. Returns shifted display
;   to original position. Does not clear display.
;   LCD execution time: 40uS to 1.6mS
;----------------------------------------------------------------------------
LCD_Home:
   movlw 0x02              ; Command 02H
   call  LCD_SendCmd
   movlw 2                 ; wait 2 mS
   goto  Wait_Wx1ms

;----------------------------------------------------------------------------
;  LCD_GetAddr  (LGA)
;   Current address is returned in "LCD_addr"
;----------------------------------------------------------------------------
LCD_GetAddr:
   call   iLCD_WaitReady

   ; set all data lines as inputs
   bsf    status,rp0           ; select bank 1
   movlw  ioMaskR              ; mask to set LCD data lines as inputs
   movwf  tris_LCD             ; use mask in W to program port
   bcf    status,rp0           ; select bank 0

   ; read address, high nibble
   bcf    port_RS,ioLCD_RS    ; RS=0   signal it is a command
   bsf    port_RW,ioLCD_RW    ; R/W=1  signal it is a read operation
   nop                        ; wait 1us to comply with timming requirements
   bsf    port_E,ioLCD_E      ; E=1  enable LCD
   nop                        ; wait 1us to comply with timming requirements
   movf   port_LCD,w          ; read data lines
   movwf  LCD_addr            ; save
   bcf    port_E,ioLCD_E      ; E=0  disable LCD
 if (ioLCD_D7 != 7)      ; if D7-D4 not conected to high nibble of PIC port
   swapf  LCD_addr,f          ; move data to high nibble
 endif
   movlw  0x0f0
   andwf  LCD_addr,f          ; low nibble to ceros


   ; read the low order nibble
   bsf    port_E,ioLCD_E      ; E=1  enable LCD
   nop                        ; wait 1us to comply with timming requirements
   movf   port_LCD,w          ; read data lines
   movwf  LCD_saveW           ; save data in saveW
   bcf    port_E,ioLCD_E      ; E=0  disable LCD
 if (ioLCD_D7 == 7)      ; if D7-D4 conected to high nibble of PIC port
   swapf  LCD_saveW,f         ; move data to low nibble
 endif
   movlw  0x0f
   andwf  LCD_saveW,f         ; high nibble to ceros
   movf   LCD_saveW,w
   iorwf  LCD_addr,f          ; merge both nibbles

   ; reprogram all LCD data lines as outputs
   bsf    status,rp0          ; select bank 1
   movlw  ioMaskW             ; mask to set LCD data lines as outputs
   movwf  tris_LCD            ; program port
   bcf    status,rp0          ; select bank 0

   return

;----------------------------------------------------------------------------
; LCD_WriteChar
;   Writes char LCD_char at logical address LCD_addr. LCD_char is not changed
;
;   The display internal RAM is 80 chars. If the display size is less than
;   80 chars what is on the screen is a "window" on the RAM. What is
;   displayed depends on the entry mode settings. This RAM memory is
;   divided into two lines: addresses 80h-A7h contains the information
;   to display in the first LCD line, and addresses C0h-E7h contains
;   the second line.
;
;   When a 1x16 LCD type built with only one Hitachi controller is used,
;   the first 8 displayed positions are mapped to addresses 80h-87h (first
;   line memory) and the last half of the line is mapped to addresses
;   C0h-C7h (second line memory). This complicates the application
;   program as addressing is not linear. For example, you must split the
;   messages to display.
;
;   In order to avoid the complexities in your program, the routines
;   LCD_putc and LCD_puts take care of all this address housekeeping.
;----------------------------------------------------------------------------
LCD_WriteChar:             ; void LCD_WriteChar(char, LCD_addr) {
   bsf    LCD_addr,7          ;    set msb of LCD_adr 
 if (LCD_lines == 1)
   movlw  0C0h                ;    if (LCD_addr >= C0h)
   subwf  LCD_addr,w
   btfsc  status,c
   goto   LEC_addrOk          ;       goto address OK
   movlw  88h                 ;    if (LCD_addr > 87h)
   subwf  LCD_addr,w
   btfss  status,c
   goto   LEC_addrOk          ;    {
   movlw  38h                 ;       NewAddr = 40h+LCD_addr-8
   addwf  LCD_addr,f          ;    }
 endif
 
LEC_addrOk:                   
   movf   LCD_addr,w          ;    MoveCursor(LCD_addr)
   call   LCD_SetDataAddr
   movf   LCD_char,w          ;    putc(char)
   goto   LCD_SendData        
			      ; }

;----------------------------------------------------------------------------
;  LCD_putc
;   Writes char W at current logical address. Address is incremented.
;----------------------------------------------------------------------------
LCD_putc:                     ;  void LCD_putc(char c)
			      ;  {
   movwf LCD_char             ;     LCD_char = c
   call  LCD_GetAddr          ;     LCD_addr = GetAddr()
   goto  LCD_WriteChar        ;     WriteChar(LCD_char, LCD_addr)
			      ;  }

;----------------------------------------------------------------------------
;  LCD_EEputs  (LEE)
;   Writes ASCIIZ string from EEDATA memory at current LCD logical address.
;   Address is incremented by size of string
;----------------------------------------------------------------------------
LCD_EEputs:                   ; void LCD_EEputs(char *s)
			      ; {
   movwf eeadr                ;    ptr = s
LEE_loop:                     ;    while (*ptr != 0)
   bsf   status,rp0           ;       select bank 1
   bsf   eecon1,rd            ;       perform read:  eedata= *eeadr
   bcf   status,rp0           ;       return to bank 0
   movf  eedata,w             ;       w = *ptr
   btfsc status,z             ;       if (w==0) return. End of string
   return
   call  LCD_putc             ;       putc(w)
   incf  eeadr,f              ;       ptr++
   goto  LEE_loop             ;    }
			      ; }

;----------------------------------------------------------------------------
;  LCD_puts (LPS)
;   Escritura l¢gica en el LCD del string "tira" en la posici¢n actual
;   del cursor.
;   No destruye ningun registro.
;
;    dptr (*tira): ptr a tira a escribir en el LCD
;----------------------------------------------------------------------------
LCD_puts:              ;  void LCD_puts(char *tira)
 ifdef NO_VALE
   push  dph                  ;  {
   push  dpl
   push  acc                  ;  char a

LPS_Otro:                     ;     while((a=*dptr) != '\0')
   movlw 0
   movc  a,@a+dptr
   jnz   LPS_Enviar
   sjmp  LPS_Fin

LPS_Enviar:                   ;     {
   call  LCD_putc             ;         putc(a)
   inc   dptr                 ;         dptr++
   sjmp  LPS_Otro             ;     }

LPS_Fin:                      ;  } /* LCD_puts */
   pop   acc
   pop   dpl
   pop   dph
   ret
endif

;----------------------------------------------------------------------------
;  LCD_putns
;   Escritura l¢gica en el LCD, en la posici¢n actual del cursor, del string
;   definido tras la direcci¢n de retorno: ej.:
;             call    LCD_putns
;             db  "Texto a escribir",0
;
;   DESTRUYE: A, DPTR
;----------------------------------------------------------------------------
LCD_putns:
   return

;****************************************************************************
;****************************************************************************
;**  Timming routines start here
;****************************************************************************
;****************************************************************************

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WNM-  Wait_Wx1ms
;    Execution remains in this routine just the milisecons specified in
;    register W (1-255). If W==0 it is assumed 256ms.
;
;    From a timming viewpoint, the sentence:
;               call  Wait_Wx1ms
;    is executed in exactly  W x 1ms (clock at 4MHz)
;

Wait_Wx1ms:
     movwf  nWaitTime           ; save W.   prolog 1: 1us
WNM_Outer_loop:
     movlw  83                  ; prolog 2: 2us
     movwf  nWaitT2                ; n2 = 83
WNM_Middle_loop:                ; prolog 3: 4us
     movlw  2                      ; n3 = 2
     movwf  nWaitT3
     goto   WNM_Inner_loop      ; to increment prolog 3 in 2us
WNM_Inner_loop:
     decfsz nWaitT3,F
     goto   WNM_Inner_loop
;End_inner:                 ; d3 = 3*n3+prolog3-1 = 3*n3+3 = 9us
     decfsz nWaitT2,F
     goto   WNM_Middle_loop
;End_Middle:                ; d2 = (3+d3)*n2+prolog2-1 = (3+d3)*n2+1 =
     decfsz nWaitTime,f     ;    = (3+9)*83+1 = 997us
     goto   WNM_Outer_loop
;End_outer:                 ; d1 = (3+d2)*n1+prolog1-1 = (3+d2)*n1-2
     return                 ; total = d1+2 = (3+d2)*n1 = (3+997)*n1 = 1000*n1


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WNC- Wait_Wx100us
;    Execution remains in this routine just the tenths of milisecons
;    specified in register W (1-255). If W==0 it is assumed 25.6ms.
;
;    From a timming viewpoint, the sentence:
;               call  Wait_Wx100us
;    is executed in  W x 100us + 1 us (clock at 4MHz)
;

Wait_Wx100us:
     movwf  nWaitTime       ; save W
WNC_Outer_loop:
     movlw  0x20            ; n2 = 32
     movwf  nWaitT2
WNC_Inner_loop:
     decfsz nWaitT2,f
     goto   WNC_Inner_loop
;End_inner:                 ; t2 = 3*n2+1 = 3*32+1 = 97us
     decfsz nWaitTime,f
     goto   WNC_Outer_loop
;End_outer:                 ; t1 = (3+t2)*N-1 = (3+97)*N-1 = 100*N - 1
     return                 ; total = t1+2 = 100*N+1 us



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WNX-  Wait_Wx10us
;    Execution remains in this routine just the hundredths of milisecons
;    specified in register W (1-255). If W==0 it is assumed 256x10us = 2.56ms
;
;    From a timming viewpoint, the sentence:
;               call  Wait_Wx10us
;    is executed in  W x 10us + 1 us (clock at 4MHz)
;

Wait_Wx10us:
     movwf  nWaitTime       ; save W.   prolog 1: 1us
WNX_Outer_loop:
     movlw  0x02            ; n2 = 2
     movwf  nWaitT2
WNX_Inner_loop:
     decfsz nWaitT2,f
     goto   WNX_Inner_loop
;End_inner:                 ; t2 = 3*n2+1 = 3*2+1 = 7us
     decfsz nWaitTime,f
     goto   WNX_Outer_loop
;End_outer:                 ; t1 = (3+t2)*N-1 = (3+7)*N-1 = 10*N - 1
     return                 ; total = t1+2 = 10*N+1 us


   END

