;* --------------------------------------------------------------------------------------
;*  Copyright (C) 2004 Dallas Semiconductor Corporation, All Rights Reserved.
;* 
;*  Permission is hereby granted, free of charge, to any person obtaining a
;*  copy of this software and associated documentation files (the "Software"),
;*  to deal in the Software without restriction, including without limitation
;*  the rights to use, copy, modify, merge, publish, distribute, sublicense,
;*  and/or sell copies of the Software, and to permit persons to whom the
;*  Software is furnished to do so, subject to the following conditions:
;* 
;*  The above copyright notice and this permission notice shall be included
;*  in all copies or substantial portions of the Software.
;* 
;*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
;*  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;*  MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
;*  IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
;*  OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
;*  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
;*  OTHER DEALINGS IN THE SOFTWARE.
;* 
;*  Except as contained in this notice, the name of Dallas Semiconductor
;*  shall not be used except as stated in the Dallas Semiconductor
;*  Branding Policy.
;*
;*  Module:        lcd
;*  Description:   Convenience LCD Controller routines 
;*  Filename:      maxq2000_lcd.asm
;*  Date:          October 13, 2004
;*  Version:       1.00
;*
;*  Contains convenience functions for displaying decimal numbers and times
;*  on the MAXQ2000 Evaluation Kit's LCD screen.
;*
;* --------------------------------------------------------------------------------------
#include "maxQ2000.inc"

LCD_PATTERN_0 equ 03Fh
LCD_PATTERN_1 equ 006h
LCD_PATTERN_2 equ 05Bh
LCD_PATTERN_3 equ 04Fh
LCD_PATTERN_4 equ 066h
LCD_PATTERN_5 equ 06Dh
LCD_PATTERN_6 equ 07Dh
LCD_PATTERN_7 equ 007h
LCD_PATTERN_8 equ 07Fh
LCD_PATTERN_9 equ 067h
LCD_PATTERN_A equ 077h
LCD_PATTERN_B equ 07Ch
LCD_PATTERN_C equ 039h
LCD_PATTERN_D equ 05Eh
LCD_PATTERN_E equ 079h
LCD_PATTERN_F equ 071h

  .code

LCD_PATTERN_TABLE:
  dw LCD_PATTERN_0
  dw LCD_PATTERN_1
  dw LCD_PATTERN_2
  dw LCD_PATTERN_3
  dw LCD_PATTERN_4
  dw LCD_PATTERN_5
  dw LCD_PATTERN_6
  dw LCD_PATTERN_7
  dw LCD_PATTERN_8
  dw LCD_PATTERN_9
  dw LCD_PATTERN_A
  dw LCD_PATTERN_B
  dw LCD_PATTERN_C
  dw LCD_PATTERN_D
  dw LCD_PATTERN_E
  dw LCD_PATTERN_F


;********************************************************************
;*
;* Function:    init_lcd
;*
;* Description: Initialize the LCD on the MAXQ2000 Evaluation Kit.
;*
;* Input:       None
;*
;* Output:      None
;*
;* Destroys:    None
;*
;********************************************************************
init_lcd::
    move  LCRA, #03E0h                 ; set frame rate and duty cycle
    move  LCFG, #0F3h                  ; set pins for LCD controller
    ret

;********************************************************************
;*
;* Function:    lcd_getlcddigit
;*
;* Description: Gets the appropriate LCD pattern for the input 
;*              number, assumed from 0-15.  Numbers above 9 will be
;*              given values for hex display.
;*
;* Input:       ACC - input number, assumed to be between 0 and 15
;*
;* Output:      GR - pattern to write to LCD register
;*
;* Destroys:    ACC, PSF, DP[0], DPC (set to 0)
;*
;********************************************************************
lcd_getlcddigit::
    move  DPC, #4                      ; select word mode for DP[0]
    ;
    ; For some reason, assembler won't let me do 
    ;     'add #(LCD_PATTERN_TABLE | 8000h)'
    ;
    add   #w:LCD_PATTERN_TABLE
    or    #8000h                       ; form a pointer to our buffer
    move  DP[0], ACC                   ; now we can read from LCD_PATTERNS
    call  UROM_moveDP0                 ; read from code space
    move  DPC, #0                      ; pointer back to byte mode
    ret

;********************************************************************
;*
;* Function:    lcd_shownumber
;*              lcd_shownumber0
;*
;* Description: Displays a number in decimal form on the LCD screen.
;*              'lcd_shownumber' will show the number without 
;*              leading zero's.  'lcd_shownumber0' will show leading
;*              zero's.  Both flavors of the function accept and
;*              display negative numbers.
;* 
;*              Note that the maximum number that can be displayed is
;*              +/- 19999.  Any number with higher magnitude will 
;*              be displayed with a '1' in the ten-thousands place.
;*              For instance, the number 45286 would be displayed as
;*              '15286'.
;*
;* Input:       ACC - input number
;*
;* Output:      None (number written to LCD controller)
;*
;* Destroys:    PSF, GR, DP[0], DP[1], A[8:0], LC[0], AP/APC=0
;*
;********************************************************************
lcd_shownumber0:
    move  DP[1], #1                    ; flag that we want all our leading 0's
    sjump lcd_shownumber_common        ; and join the common code

lcd_shownumber::
    move  DP[1], #0                    ; DP[1] will flag if we have seen non-0

lcd_shownumber_common:
    move  A[1], ACC                    ; store our value for the moment
    jump  s, lcd_shownum_neg           ; see if this is a negative number
    move  LCD4, #0                     ; make sure there is no negative sign
    sjump lcd_shownum_noneg

lcd_shownum_neg:
    move  LCD4, #80h                   ; light up the 'negative' sign
    neg                                ; complement the value in ACC

lcd_shownum_noneg:
    move  A[1], #0                     ; Set up to divide A[1]:A[0] / A[3]:A[2]
    move  A[2], #10000                 ; divide by 10000
    move  A[3], #0
    call  Div32                        ; leftover in 3:2, result in 1:0
    jump  z, lcd_shownum_below10K      ; check to see if our number was above 10000
    move  ACC, LCD4                    ; get the leading digit
    or    #40h                         ; set the '1' segment
    move  LCD4, ACC                    ; 
    move  DP[1], #1                    ; denote we have seen our first non-zero digit

lcd_shownum_below10K:
    move  A[1], #0                     ; set up for divide by 1000
    move  A[0], A[2]                   ; leftover from last divide
    move  A[3], #0                     
    move  A[2], #1000                  ; get the thousand's place number
    call  Div32
    move  A[1], ACC                    ; get the result of divide by 1000
    move  AP, #1                       ; use accumulator 1
    or    DP[1]                        ; or-in our 'seen a non-zero' flag
    jump  z, lcd_shownum_1K_zero       ; see if we should show a number or not
    move  DP[1], #1                    ; denote we have seen a non-zero digit
    move  AP, #0                       ; restore accumulator 0
    call  lcd_getlcddigit              ; get the pattern to put in LCD3
    move  LCD3, GR                     ; thousands place programmed & displayed
    sjump lcd_shownum_100              ; now let's handle the hundred's place

lcd_shownum_1K_zero:
    move  LCD3, #0
    move  AP, #0                       ; restore accumulator 0

lcd_shownum_100:
    move  A[1], #0                     ; set up for divide by 100
    move  A[0], A[2]                   ; leftover from last divide
    move  A[3], #0                     
    move  A[2], #100                   ; get the hundred's place number
    call  Div32
    move  A[1], ACC                    ; get the result of divide by 100
    move  AP, #1                       ; use accumulator 1
    or    DP[1]                        ; or-in our 'seen a non-zero' flag
    jump  z, lcd_shownum_100_zero      ; see if we should show a number or not
    move  DP[1], #1                    ; denote we have seen a non-zero digit
    move  AP, #0                       ; restore accumulator 0
    call  lcd_getlcddigit              ; get the pattern to put in LCD2
    move  LCD2, GR                     ; hundreds place programmed & displayed
    sjump lcd_shownum_10               ; now let's handle the ten's place

lcd_shownum_100_zero:
    move  LCD2, #0
    move  AP, #0                       ; restore accumulator 0
 
lcd_shownum_10:   
    move  A[1], #0                     ; set up for divide by 10
    move  A[0], A[2]                   ; leftover from last divide
    move  A[3], #0                     
    move  A[2], #10                    ; get the ten's place number
    call  Div32
    move  A[1], ACC                    ; get the result of divide by 10
    move  AP, #1                       ; use accumulator 1
    or    DP[1]                        ; or-in our 'seen a non-zero' flag
    jump  z, lcd_shownum_10_zero       ; see if we should show a number or not
    move  AP, #0                       ; restore accumulator 0
    call  lcd_getlcddigit              ; get the pattern to put in LCD1
    move  LCD1, GR                     ; tens place programmed & displayed
    sjump lcd_shownum_1                ; now let's handle the one's place

lcd_shownum_10_zero:
    move  LCD1, #0
    move  AP, #0                       ; restore accumulator 0

lcd_shownum_1:
    ;
    ; The one's place is simply the leftover of our last divide.  
    ; Show it no matter what.
    ;
    move  ACC, A[2]
    call  lcd_getlcddigit              ; get the pattern to put in LCD1
    move  LCD0, GR                     ; show the one's place
    ret



;********************************************************************
;*
;* Function:    lcd_showtime
;*
;* Description: Shows a time on the LCD screen.  The time will be in 
;*              24-hour format (with the leading '0' showing).  The
;*              colon between the hours and minutes will be active.
;*
;* Input:       A[1] - input number of hours
;*              A[0] - input number of minutes
;*
;* Output:      None (time data written to LCD controller)
;*
;* Destroys:    PSF, GR, DP[0], DP[1], A[8:4], LC[0], AP/APC=0
;*
;********************************************************************
lcd_showtime::
    move  DP[1], A[0]                  ; store the number of minutes
    ;
    ; Translate the hours
    ;
    move  A[0], A[1]                   ; get set up to divide hours by 10
    move  A[1], #0
    move  A[2], #10                    ; divide/mod by 10
    move  A[3], #0
    call  Div32                        ; hours / 10 is in A[0], hours % 10 in A[2]
    call  lcd_getlcddigit              ; translate hours div 10
    move  LCD3, GR                     ; set high digit of the hours
    move  ACC, A[2]
    call  lcd_getlcddigit              ; translate hours mod 10
    move  LCD2, GR                     ; set low digit of the hours

    ;
    ; Translate the minutes
    ;
    move  A[0], DP[1]                  ; get set up to divide minutes by 10
    move  A[1], #0
    move  A[2], #10                    ; divide/mod by 10
    move  A[3], #0
    call  Div32                        ; mins / 10 is in A[0], mins % 10 in A[2]
    call  lcd_getlcddigit              ; translate mins div 10
    move  LCD1, GR                     ; set high digit of the mins
    move  ACC, A[2]
    call  lcd_getlcddigit              ; translate mins mod 10
    move  LCD0, GR                     ; set low digit of the mins

    move  LCD4, #10h                   ; set the clock's 'colon'
    ret


end
