;* --------------------------------------------------------------------------------------
;*  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.
;*
;* --------------------------------------------------------------------------------------
;* 
;*  This demonstration is meant to demonstrate I2C communication using the MAXQ2000.
;*  Using an AT24C32A I2C EEPROM, the demonstration application lets you read and
;*  write different bytes in the EEPROM.  
;*
;*  The LCD display shows two values (in hex) separated by a colon.  The first value
;*  is the current address in the EEPROM being displayed.  The second value is the
;*  actual byte read out of the EEPROM.  You can use the first push button to increment
;*  the address and the second push button to write (value+1) back to the EEPROM.  
;*  Press the third button to reset the application, which effectively sets the 
;*  address back to 0.
;* 
;*  Turn ON the following switches for this demonstration:
;*
;*       SW6.2, SW6.5: Enable the push buttons
;*       SW6.6, SW6.7: Enable I2C communication with the AT24C32A
;*       SW6.8 : Enable the LED panel
;*
;* --------------------------------------------------------------------------------------
#include "..\api\maxQ2000.inc"

  .code
  .keep
    ;
    ; Some basic initialization.  Set Accumulator Controls to normal.
    ; Set interrupt vector.  Set data pointer controls.  
    ;
    move  APC, #0                      ; no accumulator-modulo behavior
    move  AP, #0                       ; select accumulator 0
    move  DPC, #0                      ; select DP[0], byte mode
    move  IV, #interrupt_handler       ; set interrupt vector

    call  init_push_buttons            ; setup push button configuration
    call  init_lcd                     ; initialize the EV Kit LCD screen
    move  ACC, IMR                     ; need to mask in module 0 interrupts
    or    #1                           ; low bit --> module 0
    move  IMR, ACC                     ; store new interrupt mask
    move  IC, #1                       ; initialize global interrupts
    move  GR, #0                       ; GR contains the current address
    call  I2C_Init                     ; initialize the I2C lines
    call  DisplayCurrentLocation       ; display the current location in the EEPROM
    sjump $

;*****************************************************************************
;
; Initialize the push buttons.  We will be using push buttons 10 and 15 in 
; this application.  Set for falling edge triggered interrupts.  Enables 
; interrupts from module 1 in the system interrupt mask.
;
; Input:     None
; Output:    None
; Destroyed: ACC, PSW
;
;*****************************************************************************
init_push_buttons:
    move  EIE1, #84h                   ; enable interrupts for EI 10 and 15
    move  EIES1, #84h                  ; select falling edge for EI 10 and 15
    move  ACC, IMR                     ; get the interrupt mask enable
    or    #2                           ; include interrupts for module 1
    move  IMR, ACC                     ; store new interrupt mask enable value
    ret

;*****************************************************************************
;
; Interrupt handler for the application.  We are only expecting interrupts
; on external interrupt 10 and 15.  
;
; 
;*****************************************************************************
interrupt_handler:
    ;
    ; No need to save anything.  Our application is in an sjump $ loop.
    ;
    move  APC, #0
    move  AP, #0                       ; select A[3] for our accumulator
    move  ACC, EIF1                    ; get the interrupt flag
    and   #80h                         ; check for interrupt 15
    jump  z, interrupt_not_15
    ;
    ; External interrupt 15 fired.  Increment the address and display the new value.
    ;
    move  ACC, GR                      ; get the current EEPROM address
    add   #1                           ; increment the address
    cmp   #200h                        ; let's limit ourselves to addresses we can show
    jump  ne, interrupt_address_ok
    move  ACC, #0                      ; reset to the 0 address

interrupt_address_ok:
    move  GR, ACC                      ; store the new address
    call  DisplayCurrentLocation       ; display the current location in the EEPROM
    ; 
    ; Fallthrough to check for the other interrupt
    ;

interrupt_not_15:
    move  ACC, EIF1                    ; get the interrupt flag
    and   #04h                         ; check for interrupt 10
    jump  z, interrupt_not_10
    ;
    ; External interrupt 10 fired.  Add 1 to the current value in the EEPROM.
    ;
    call  GetCurrentValue              ; Current EEPROM value goes into ACC
    add   #1                           ; increment the value
    call  WriteCurrentValue            ; Write the value to the EEPROM
    call  DisplayCurrentLocation       ; display the new value...make sure it took

interrupt_not_10:
    move  EIF1, #0                     ; clear the interrupts
    reti

;*****************************************************************************
;
; Get the current value pointed to by GR in the EEPROM.
;
; Input:     GR - current address in the EEPROM
; Output:    c - set on error (no ACK)
;            ACC - value read from the EEPROM
;
;*****************************************************************************
GetCurrentValue:
   ;
   ; First write the address we want to access 
   ;
   call    I2C_Start
   move    Acc, #00A0h                 ; Write
   call    I2C_TxByte
   call    I2C_RxBit                   ; ACK
   jump    C, GetCurrentValue_NoACK
   move    Acc, GRH                    ; Address high
   call    I2C_TxByte
   call    I2C_RxBit                   ; ACK
   jump    C, GetCurrentValue_NoACK
   move    Acc, GRL                    ; Address low
   call    I2C_TxByte
   call    I2C_RxBit                   ; ACK
   jump    C, GetCurrentValue_NoACK
   call    I2C_Stop
   ;
   ; Now read the data
   ;
   call    I2C_Start
   move    Acc, #00A1h                 ; Address=1010, read
   call    I2C_TxByte
   call    I2C_RxBit                   ; ACK
   jump    C, GetCurrentValue_NoACK
   call    I2C_RxByte 
   move    C, #0                       ; denote no error

GetCurrentValue_NoACK:
   call    I2C_Stop
   ret

;*****************************************************************************
;
; Write the value stored in ACC to the EEPROM at the address GR.
;
; Input:     GR - address in EEPROM to write to
;            Acc - value to write
; Output:    c - set on error (no ACK)
;
;*****************************************************************************
WriteCurrentValue:
   move    A[1], ACC
   call    I2C_Start
   move    Acc, #00A0h                 ; Write
   call    I2C_TxByte
   call    I2C_RxBit                   ; ACK
   jump    C, WriteCurrentValue_NoACK
   move    Acc, GRH                    ; Address high
   call    I2C_TxByte
   call    I2C_RxBit                   ; ACK
   jump    C, WriteCurrentValue_NoACK
   move    Acc, GRL                    ; Address low
   call    I2C_TxByte
   call    I2C_RxBit                   ; ACK
   jump    C, WriteCurrentValue_NoACK
   move    Acc, A[1]                   ; Data
   call    I2C_TxByte
   call    I2C_RxBit                   ; ACK
   jump    C, WriteCurrentValue_NoACK

WriteCurrentValue_NoACK:
   call    I2C_Stop
   call    PauseWrite
   ret

;*****************************************************************************
;
; Displays the current address and byte in the EEPROM to the LCD screen.
;
; Input:     GR - address to display
; Output:    None
;
;*****************************************************************************
DisplayCurrentLocation:
    call  GetCurrentValue              ; get the current value in the EEPROM
    move  A[1], A[0]                   ; save the current value
    move  A[2], GR                     ; save the current address
    and   #0Fh                         ; isolate low 4 bits of current value
    call  lcd_getlcddigit              ; get lowest hex digit
    move  LCD0, GR                     ; display lowest digit
    move  A[0], A[1]                   ; restore current value
    xchn                               ; exchange nibbles
    and   #0Fh                         ; isolate 'high' 4 bits of current value
    call  lcd_getlcddigit              ; get next hex digit
    move  LCD1, GR                     ; display high nibble of current value
    move  A[0], A[2]                   ; get current address
    and   #0Fh                         ; isolate low 4 bits of current address
    call  lcd_getlcddigit              ; get hex digit
    move  LCD2, GR                     ; display low nibble of current address
    move  A[0], A[2]                   ; get current address
    xchn                               ; exchange nibbles
    and   #0Fh                         ; isolate 'high' 4 bits of current address
    call  lcd_getlcddigit              ; get hex digit
    move  LCD3, GR                     ; display low nibble of current address
    move  A[0], A[2]                   ; get current address one last time
    and   #100h                        ; see if the 9th bit is set
    jump  z, DisplayCurrentLocation_no1
    move  LCD4, #050h                  ; turn on the leading 1 and the middle colon
    sjump DisplayCurrentLocation_exit

DisplayCurrentLocation_no1:
    move  LCD4, #010h                  ; turn on the middle colon
    
DisplayCurrentLocation_exit:
    move  GR, A[2]                     ; restore our current address
    ret

end
