;; -----------------------------------------------------------------------------
;; Company           : Brno University of Technology
;; -----------------------------------------------------------------------------
;; FileName          : $URL: file:///D:/repos/svn/I2CA/trunk/root/firmware/main.asm $
;; -----------------------------------------------------------------------------
;; Author            : Milos Juhas
;; Last Modified     : $Date: 2011-05-13 17:08:30 +0200 (pi, 13 5 2011) $
;; Last Modified By  : $Author: d@@N $
;; Revision          : $Rev: 29 $
;; -----------------------------------------------------------------------------
;; Description       : I2C Analyzer Firmware - main file
;;                     
;; -----------------------------------------------------------------------------


;; *****************************************************************************
;; CODING GUIDELINES
;; *****************************************************************************
;; nazvy registrov      male pismena
;; nazvy registrov      .def + male pismena + prefix 'r_'
;; bity v registroch    .equ + velke pismena
;; konstanty            .equ + velke pismena + prefix c_
;; vyctovy typ          .equ + velke pismena + prefix e_
;; nazov macra          velke pismena
;; instrukcie           male pismena
;; klucove slova        male pismena


;; *****************************************************************************
;; INCLUDES
;; *****************************************************************************

  .include  <m8def.inc>                         ; ATmega8 definition file


;; *****************************************************************************
;; REGISTERS
;; *****************************************************************************

  .def      r_rx_cmd                = r0        ; input buffer for 1st byte
  .def      r_rx_data               = r1        ; input buffer for 2nd byte
  .def      r_tx_cmd                = r2        ; output buffer for 1st byte
  .def      r_tx_data               = r3        ; output buffer for 2nd byte
  .def      r_bkp1_xl               = r4        ; backup register 1 for XL
  .def      r_bkp1_xh               = r5        ; backup register 1 for XH
  .def      r_bkp1_yl               = r6        ; backup register 1 for YL
  .def      r_bkp1_yh               = r7        ; backup register 1 for YH
  .def      r_bkp1_zl               = r8        ; backup register 1 for ZL
  .def      r_bkp1_zh               = r9        ; backup register 1 for ZH
  .def      r_bkp2_xl               = r10       ; backup register 2 for XL
  .def      r_bkp2_xh               = r11       ; backup register 2 for XH
  .def      r_bkp2_yl               = r12       ; backup register 2 for YL
  .def      r_bkp2_yh               = r13       ; backup register 2 for YH
  .def      r_bkp2_zl               = r14       ; backup register 2 for ZL
  .def      r_bkp2_zh               = r15       ; backup register 2 for ZH
  .def      r_tmp0                  = r16       ; temporary register 0
  .def      r_tmp1                  = r17       ; temporary register 1
  .def      r_sr0                   = r18       ; status register 0
    .equ      RXBI                    = 0       ; - UART RX Byte Index
    .equ      RXCR                    = 1       ; - UART Command Ready
    .equ      TXMT                    = 2       ; - UART Tx Mutex
    .equ      OPM0                    = 4       ; - Operation Mode bit 0
    .equ      OPM1                    = 5       ; - Operation Mode bit 1
    .equ      OPM2                    = 6       ; - Operation Mode bit 2
    .equ      OPM3                    = 7       ; - Operation Mode bit 3
  .def      r_cmd                   = r22       ; register with command byte
    .equ      CMD0                    = 0       ; - Command bit 0
    .equ      CMD1                    = 1       ; - Command bit 1
    .equ      CMD2                    = 2       ; - Command bit 2
    .equ      ARGE                    = 3       ; - Argument Enable flag
    .equ      HPC                     = 4       ; - High Priority Command flag
  .def      r_data                  = r23       ; register with data byte
  .def      r_cnt1                  = r24       ; counter 1
  .def      r_cnt2                  = r25       ; counter 2

  ;; I2C BUS
  .equ      SDA                     = 4         ; SDA pin number
  .equ      SCL                     = 5         ; SCL pin number
  .equ      pin_i2c                 = pinc
  .equ      port_i2c                = portc     ; port
  .equ      ddr_i2c                 = ddrc      ; direction register

  ;; SCL


  ;; Yellow LED
  .equ      LEDY                    = 6         ; LED pin number
  .equ      port_led                = portd     ; port
  .equ      ddr_led                 = ddrd      ; direction register

;; *****************************************************************************
;; CONSTANTS
;; *****************************************************************************

  .equ      c_FCPU                  = 80        ; cpu frequecny (8 MHz)
  .equ      c_TWSR_MASK             = 0xF8      ; clear TWPS bits


;; ******** Descriptors ********************************************************

  .equ      c_D_DEV_ID              = 0x0A      ; Device ID
  .equ      c_D_DEV_HW              = 0x11      ; Hardware version and revision
  .equ      c_D_DEV_FW              = 0x10      ; Firmware version and revision


;; *****************************************************************************
;; ENUMERATION TYPES
;; *****************************************************************************

;; ******** Operation Modes ****************************************************
  .equ      e_M_INIT                = 0x2       ; Init Mode
  .equ      e_M_P_INIT              = 0x3       ; Post Init Mode
  .equ      e_M_ACTIVE              = 0x4       ; Active Mode
  .equ      e_M_ACTIVE_M            = 0x5       ; - Master (submode)
  .equ      e_M_ACTIVE_S            = 0x6       ; - Slave (submode)
  .equ      e_M_ACTIVE_P            = 0x1       ; - Passive (submode)
  .equ      e_M_IDLE                = 0x8       ; Idle Mode
  .equ      e_M_P_IDLE              = 0xB       ; Post Idle Mode
  .equ      e_M_SERVICE             = 0xF       ; Service Mode (not implemented)


;; ******** Rx Commands ********************************************************
;; Commands Received from UART
  
  ; low priority commands
  .equ      e_RC_DUMMY              = 0x00      ; Dummy Frame
  .equ      e_RC_ECHO               = 0x08      ; Echo
  .equ      e_RC_GET_ID             = 0x01      ; Get Device ID
  .equ      e_RC_GET_HW             = 0x02      ; Get HWID
  .equ      e_RC_GET_FW             = 0x03      ; Get FWID
  .equ      e_RC_GET_NAME           = 0x04      ; Get Device Name
  .equ      e_RC_GET_FCPU           = 0x05      ; Get Oscillator Frequency
  .equ      e_RC_GET_OPMOD          = 0x06      ; Get Operation Mode
  .equ      e_RC_SET_OPMOD          = 0x0E      ; Set Operation Mode
  
  ; high priority commands
            
            ; MASTER
  .equ      e_RC_M_START            = 0x11      ; Send Start Condition
  .equ      e_RC_M_STOP             = 0x12      ; Send Stop Condition
  .equ      e_RC_M_SEND             = 0x1B      ; Send Data
  .equ      e_RC_M_REC_A            = 0x14      ; Receive Data with ACK
  .equ      e_RC_M_REC_N            = 0x15      ; Receive Data with NACK
  .equ      e_RC_M_GET_BR           = 0x16      ; Get TWBR
  .equ      e_RC_M_SET_BR           = 0x1E      ; Set TWBR

            ; SLAVE
  .equ      e_RC_S_ENABLE           = 0x11      ; ACK after own addr
  .equ      e_RC_S_DISABLE          = 0x12      ; NACK after own addr
  .equ      e_RC_S_SEND             = 0x1B      ; Send Data
  .equ      e_RC_S_REC_A            = 0x14      ; Receive Data with ACK
  .equ      e_RC_S_REC_N            = 0x15      ; Receive Data with NACK
  .equ      e_RC_S_SET_AR1          = 0x1E      ; Set Addr byte 1
  .equ      e_RC_S_SET_AR2          = 0x1F      ; Set Addr byte 2
            
            ; PASSIVE
  .equ      e_RC_P_START            = 0x11      ; Start Sniffer
  .equ      e_RC_P_STOP             = 0x12      ; Stop Sniffer
  .equ      e_RC_P_FORMAT           = 0x13      ; 


;; ******** Tx Commands ********************************************************
;; e_TC_...
  .equ      e_TC_ACK                = 0x00      ; 
    .equ      e_ACK_OK                = 0x00
    .equ      e_ACK_UNKNW_CMD         = 0x0F
    .equ      e_ACK_RX_ERR            = 0xF0
    .equ      e_ACK_ERR               = 0xFF
;; *****************************************************************************
;; MACROS
;; *****************************************************************************

  ;; inicializacia LED
  .macro    LED_INIT
            sbi         ddr_led,    LEDY
  .endmacro

  ;; rozsvietenie LED
  .macro    LED_ON
            cbi         port_led,   LEDY
  .endmacro

  ;; zhasnnutie LED
  .macro    LED_OFF
            sbi         port_led,   LEDY
  .endmacro

  ;; cakanie na novy prikaz
  .macro    WAIT_FOR_CMD
            sbrs        r_sr0,      RXCR
            rjmp        PC-1
  .endmacro

  ;; cakanie kym UDR nie je prazdne
  .macro    WAIT_TX_RDY
            sbis        ucsra,      UDRE
            rjmp        PC-1
  .endmacro

  ;; nastavenie OP MODE v r_sr0
  .macro    SET_OP_MODE
            andi        r_sr0,      ~((1 << OPM3) | (1 << OPM2) | (1 << OPM1) | (1 << OPM0))
            ori         r_sr0,      (@0 << 4)
  .endmacro

  ;; vynulovanie RXCR, ak je mozne prijat dalsie data
  .macro    RXCR_CLEAR
            cbr         r_sr0,      (1 << RXCR) ; clear rx_byte            
  .endmacro
;; ******** RESET VECTOR *******************************************************
  .equ      RESETaddr               =  0x0000      ; reset vector


;; *****************************************************************************
;; INTERRUPT VECTORS
;; *****************************************************************************
  .cseg
  .org      RESETaddr                           ; where to jump when reset
            rjmp        START
  .org      INT0addr                            ; INT0 interrupt vector
            rjmp        I_INT0
  .org      INT1addr                            ; INT1 interrupt vector
            rjmp        I_INT1
  .org      URXCaddr                            ; UART Rx Complete interrupt vector
            rjmp        I_UART_R
  .org      0x02A                               ; end of interrupt vectors

;; *****************************************************************************
;; INTERRUPTS
;; *****************************************************************************

;; ******** UART CMD or DATA received ******************************************
I_UART_R:
            ; check if buffer is free
            sbrs        r_sr0,      RXCR
            rjmp        _I_UART_R_08
            rcall       RX_ERR_RCVR
            reti
_I_UART_R_08:
            ; parity error test            
            sbis        ucsra,      UPE
            rjmp        _I_UART_R_10
            rcall       RX_ERR_RCVR 
            led_on
            reti

_I_UART_R_10:
            ; CMD vs DATA byte
            sbrs        r_sr0,      RXBI
            rjmp        _I_UART_R_20            ; jump if rx_byte = 0
_I_UART_R_18:
            ; rx_byte_index = 1 (DATA)
            in          r_data,     udr         ; r_rx_data = udr
            cbr         r_sr0,      (1 << RXBI) ; clear rx_byte_index
            sbr         r_sr0,      (1 << RXCR) ; set rx_cmd_rdy
            reti
_I_UART_R_20:
            ; rx_byte_index = 0 (CMD)
            in          r_cmd,      udr         ; r_rx_cmd = udr
            ;; cbr         r_sr0,      (1 << RXBI) | (1 << RXCR)    ; clear bits

            sbrc        r_cmd,      ARGE        ; ARGE = 1 => RXBI = 1, RXCR = 0
            sbr         r_sr0,      (1 << RXBI) ; set rx_byte_index
            sbrs        r_cmd,      ARGE        ; ARGE = 0 => RXBI = 0, RXCR = 1
            sbr         r_sr0,      (1 << RXCR) ; set rx_cmd_rdy

            reti            

;; ******** INT0 - MONITOR SCL *************************************************
I_INT0:     
            
            reti

;; ******** INT1 - MONITOR SCL *************************************************
I_INT1:
            
            reti

;; *****************************************************************************
;; PROGRAM START
;; *****************************************************************************


START:       

;; ******** INIT MODE **********************************************************

INIT_MODE:
;; ******** SR0 Initialization *************************************************            
            ldi         r_sr0,      0
            SET_OP_MODE             e_M_INIT

;; ******** SW LED Initialization **********************************************            
            
            ; Nastavi pin so zltou LED ako vystupny
            LED_INIT

            ; LED bude v INIT_MODE svietit
            LED_ON

;; ******** Stack Pointer Initialization ***************************************            
            ldi         r_tmp0,     LOW(RAMEND)
            out         spl,        r_tmp0
            ldi         r_tmp0,     HIGH(RAMEND)
            out         sph,        r_tmp0

;; ******** UART Initialization ************************************************            
            
            ; baudrate, 103 = 9600 baud @ 8 MHz; 0 = 1 Mbaud
            ldi         r_tmp0,     0
            out         ubrrl,      r_tmp0
            clr         r_tmp0
            out         ubrrh,      r_tmp0
             
            ; double speed mode
            ldi         r_tmp0,     (1 << U2X)
            out         ucsra,      r_tmp0

            ; Transciever Enable, Receiver Enable, Receiver Interrupt Enable
            ldi         r_tmp0,     (1 << TXEN) | (1 << RXEN) | (1 << RXCIE)	; TX, RX, RX Interrupt
            out         ucsrb,      r_tmp0

            ; Asynchronous Mode, 1 Stop Bit, Even Parity
            ldi         r_tmp0,     (1 << URSEL) | (3 << UCSZ0) | (1 << UPM1)	; Async, 1 StopBit, Even parity
            out         ucsrc,      r_tmp0

;; ******** TWI Initialization *************************************************

            ; Set SCLK frequency to 100 kHz (TWBR = 32, TWPS = 0)
            ldi         r_tmp0,     32
            out         twbr,       r_tmp0
            in          r_tmp0,     twsr
            andi        r_tmp0,     ~((1 << twps0) | (1 << twps1))
            out         twsr,       r_tmp0

            ; global interrupt enable
            sei

            ldi         zl,         LOW(POST_INIT_MODE)
            ldi         zh,         HIGH(POST_INIT_MODE)

;; ******** POST-INIT MODE *****************************************************
;; connection test command and change mode request are supported

POST_INIT_MODE:
            SET_OP_MODE             e_M_P_INIT
            LED_OFF
            ;;;RJMP        _PM_START
            WAIT_FOR_CMD             
            rcall       LOW_PRIORITY_D
            ijmp


;; ******** ACTIVE MODE ********************************************************

ACTIVE_MODE:
            SET_OP_MODE             e_M_ACTIVE
_ACTIVE_MODE_08:
            WAIT_FOR_CMD
            sbrs        r_cmd,      HPC
            rcall       LOW_PRIORITY_D
            ijmp                                ; jump to correct mode

MASTER_MODE:
            SET_OP_MODE             e_M_ACTIVE_M
            
            ;; RXCR = 0
            sbrs        r_sr0,      RXCR
            rjmp        _ACTIVE_MODE_08
            

            ;; START CONDITION
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_M_START
            breq        _MM_START
            ;; SEND DATA
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_M_SEND
            breq        _MM_TX_DATA
            ;; RECEIVE DATA WITH ACK
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_M_REC_A
            breq        _MM_RX_DATA_ACK
            ;; RECEIVE DATA WITH NACK
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_M_REC_N
            breq        _MM_RX_DATA_NACK
            ;; STOP CONDITION
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_M_STOP
            breq        _MM_STOP
            ;; SET TWBR
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_M_SET_BR
            breq        _MM_S_SCL
            ;; GET TWBR
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_M_GET_BR
            breq        _MM_G_SCL
            ;; UNKNOW COMMAND
            sbrc        r_cmd,      HPC
            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08

            ;; send START condition
_MM_START:
            ldi         r_tmp0,     (1 << TWINT) | (1 << TWSTA) | (1 << TWEN)
            out         twcr,       r_tmp0
            rcall       TWI_WAIT_I

            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd
            ; data
            WAIT_TX_RDY
            in          r_tmp0,     twsr
            out         udr,        r_tmp0
            
            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08

            ;; send data or address
_MM_TX_DATA:
            out         twdr,       r_data
            ldi         r_tmp0,     (1 << TWINT) | (1 << TWEN)
            out         twcr,       r_tmp0
            rcall       TWI_WAIT_I            
                        
            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd
            ; data
            WAIT_TX_RDY
            in          r_tmp0,     twsr
            out         udr,        r_tmp0            
            
            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08

            ;; receive data, send ack
_MM_RX_DATA_ACK:
            
            ldi         r_tmp0,     (1 << TWINT) | (1 << TWEA) | (1 << TWEN)
            out         twcr,       r_tmp0
            rcall       TWI_WAIT_I    
            
            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd
            ; data
            WAIT_TX_RDY
            in          r_tmp0,     twdr
            out         udr,        r_tmp0

            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08

            ;; receive data, send nack
_MM_RX_DATA_NACK:
            
            ldi         r_tmp0,     (1 << TWINT) | (1 << TWEN)
            out         twcr,       r_tmp0
            rcall       TWI_WAIT_I    

            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd
            ; data
            WAIT_TX_RDY
            in          r_tmp0,     twdr
            out         udr,        r_tmp0

            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08
            
            ;; send stop condition
_MM_STOP:
            ldi         r_tmp0,        (1 << TWINT) | (1 << TWEN) | (1 << TWSTO)
            out         twcr,       r_tmp0

            mov         r_tmp0,     r_cmd
            ldi         r_tmp1,     e_ACK_OK
            rcall       SEND_ACK
            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08
            
            ;; set or get TWBR
_MM_S_SCL:
            out         twbr,       r_rx_data

_MM_G_SCL:        
            mov         r_tmp0,     r_cmd
            in          r_tmp1,     twbr
            rcall       SEND_ACK
            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08

SLAVE_MODE:
            SET_OP_MODE             e_M_ACTIVE_S
            ;; RXCR = 0
            sbrs        r_sr0,      RXCR
            rjmp        _ACTIVE_MODE_08
            
            ;; ENABLE ACK AFTER ADDR
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_S_ENABLE
            breq        _SM_S_ACK
            ;; DISABLE ACK AFTER ADDR
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_S_DISABLE
            breq        _SM_S_NACK
            ;; SEND DATA
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_S_SEND
            breq        _SM_TX_DATA
            ;; RECEIVE DATA WITH ACK
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_S_REC_A
            breq        _SM_RX_DATA_ACK
            ;; RECEIVE DATA WITH NACK
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_S_REC_N
            breq        _SM_RX_DATA_NACK
            ;; SET ADDRESS BYTE 1
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_S_SET_AR1
            brne        _SM_JMP008
            rjmp        _SM_S_AR1
            ;; SET ADDRESS BYTE 2
_SM_JMP008:
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_S_REC_N
            breq        _SM_RX_DATA_NACK
            ;; UNKNOW COMMAND
            sbrc        r_cmd,      HPC
            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08
            
            ;; slave mode enable
_SM_S_ACK:
            ldi         r_tmp0,     (1 << TWINT) | (1 << TWEA) | (1 << TWEN)
            out         twcr,       r_tmp0
            rcall       TWI_WAIT_I

            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd

            ; data
            WAIT_TX_RDY
            in          r_tmp0,     twsr
            out         udr,        r_tmp0
            
            RXCR_CLEAR          
            rjmp        _ACTIVE_MODE_08

            ;; slave mode disable
_SM_S_NACK:
            ldi         r_tmp0,     (1 << TWINT)
            out         twcr,       r_tmp0

            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd

            ; data
            WAIT_TX_RDY
            ldi         r_tmp0,     e_ACK_OK
            out         udr,        r_tmp0
            
            RXCR_CLEAR          
            rjmp        _ACTIVE_MODE_08
            ;; send data
_SM_TX_DATA:
            out         twdr,       r_data
            ldi         r_tmp0,     (1 << TWINT) | (1 << TWEN)
            out         twcr,       r_tmp0
            rcall       TWI_WAIT_I

            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd
            
            ; status
            WAIT_TX_RDY
            in          r_tmp0,     twsr
            out         udr,        r_tmp0
           
            RXCR_CLEAR          
            rjmp        _ACTIVE_MODE_08
            
            ;; receive data with ack
_SM_RX_DATA_ACK:
            ldi         r_tmp0,     (1 << TWINT) | (1 << TWEA) | (1 << TWEN)
            out         twcr,       r_tmp0
            rcall       TWI_WAIT_I

            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd
            
            ; status
            WAIT_TX_RDY
            in          r_tmp0,     twsr
            out         udr,        r_tmp0

            ; data
            WAIT_TX_RDY
            in          r_tmp0,     twdr
            out         udr,        r_tmp0
            
            RXCR_CLEAR          
            rjmp        _ACTIVE_MODE_08
            
            ;; receive data with nack
_SM_RX_DATA_NACK:
            ldi         r_tmp0,     (1 << TWINT) | (1 << TWEN)
            out         twcr,       r_tmp0
            rcall       TWI_WAIT_I

            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd
            
            ; status
            WAIT_TX_RDY
            in          r_tmp0,     twsr
            out         udr,        r_tmp0

            ; data
            WAIT_TX_RDY
            in          r_tmp0,     twdr
            out         udr,        r_tmp0
            
            RXCR_CLEAR          
            rjmp        _ACTIVE_MODE_08
            
            ;; set address byte1 (7b/10b low)
_SM_S_AR1:
            out         twar,       r_rx_data
            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd

            ; data
            WAIT_TX_RDY
            ldi         r_tmp0,     e_ACK_OK
            out         udr,        r_tmp0

            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08

            ;; set address byte2 (10b low)
_SM_S_AR2:
            mov         r_tmp1,       r_rx_data
            ; cmd
            WAIT_TX_RDY
            out         udr,        r_cmd

            ; data
            WAIT_TX_RDY
            ldi         r_tmp0,     e_ACK_OK
            out         udr,        r_tmp0

            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08

PASSIVE_MODE:
            SET_OP_MODE             e_M_ACTIVE_P
            
            ;; RXCR = 0
            sbrs        r_sr0,      RXCR
            rjmp        _ACTIVE_MODE_08
            
            ;; START
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_P_START
            breq        _PM_START
            ;; STOP
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_P_STOP
            breq        _PM_START
            ;; UNKNOW COMMAND
            sbrc        r_cmd,      HPC
            RXCR_CLEAR
            rjmp        _ACTIVE_MODE_08



_PM_START:
            ; clear buffer
            ldi         r_data,     3
            RXCR_CLEAR
            

            ;; 1st START condition 
__PMS_1ST_SC:                    
            ; wait on SCL = 1 && SDA = 1
            sbis        pin_i2c,    SCL
            rjmp        __PMS_1ST_SC
            sbis        pin_i2c,    SDA
            rjmp        __PMS_1ST_SC

__PMS_WAIT_04:
            ; wait on SCL = 1 && SDA = 1 -> 0 (start condition)
            sbrc        r_sr0,      RXCR
            rjmp        _PM_START
            sbis        pin_i2c,    SDA
            rjmp        __PMS_WAIT_06
            sbic        pin_i2c,    SCL
            rjmp        __PMS_WAIT_04
            rjmp        __PMS_1ST_SC

__PMS_WAIT_06:
            ; STOP condition is not allowed after START condition
            sbic        pin_i2c,    SDA
            rjmp        __PMS_1ST_SC
            sbic        pin_i2c,    SCL
            rjmp        __PMS_WAIT_06
            ldi         r_cnt1,     1

__PMS_WAIT_08:
            ; sample SDA immediately after SCL 0 -> 1
            sbis        pin_i2c,    SCL
            rjmp        __PMS_WAIT_08
            sbis        pin_i2c,    SDA
            rjmp        __PMS_SDA_0
__PMS_SDA_1:
__PMS_WAIT_16:
            ; SCL = 1 -> 0 | SDA = 1 -> 0
            sbis        pin_i2c,    SDA
            rjmp        __PMS_SC
            sbrc        r_sr0,      RXCR        ; skip if RXCR = 1
            rjmp        _PM_STOP
            sbic        pin_i2c,    SCL
            rjmp        __PMS_WAIT_16
            sec
            rol         r_data
            lsl         r_data
            inc         r_cnt1
            andi        r_cnt1,     3
            brne        __PMS_WAIT_08
            ; send data
            out         udr,        r_data
            ldi         r_data,     0
            rjmp        __PMS_WAIT_08

__PMS_SDA_0:
__PMS_WAIT_24:
            ; SCL = 1 -> 0 | SDA = 0 -> 1
            sbic        pin_i2c,   SDA
            rjmp        __PMS_PC
            sbrc        r_sr0,     RXCR
            rjmp        _PM_STOP
            sbic        pin_i2c,   SCL
            rjmp        __PMS_WAIT_24
            lsl         r_data
            lsl         r_data
            inc         r_cnt1
            andi        r_cnt1,     3
            brne        __PMS_WAIT_08
            ; send data
            out         udr,        r_data
            ldi         r_data,     0
            rjmp        __PMS_WAIT_08

            ;; START condition
__PMS_PSC:
__PMS_SC:
__PMS_WAIT_32:
            ; SCL = 1 -> 0
            sbrc        r_sr0,      RXCR    ; skip if RXCR = 1
            rjmp        _PM_STOP
            sbic        pin_i2c,    SCL
            rjmp        __PMS_WAIT_32
            lsl         r_data
            lsl         r_data
            ori         r_data,     3
            inc         r_cnt1
            andi        r_cnt1,     3
            brne        __PMS_WAIT_08
            ; send data
            out         udr,        r_data
            ldi         r_data,     0
            rjmp        __PMS_WAIT_08

            ;; STOP condition
__PMS_PC:
            lsr         r_data
            lsr         r_data
            ori         r_data,     1
            inc         r_cnt1
            andi        r_cnt1,     3
            brne        __PMS_WAIT_40
            out         udr,        r_data
            ldi         r_data,     0
            

__PMS_WAIT_40:
            ; SCL = 1 -> 0 | SDA = 1 -> 0
            sbrc        r_sr0,      RXCR
            rjmp        _PM_STOP
            sbis        pin_i2c,    SDA
            rjmp        __PMS_PSC
            rjmp        __PMS_WAIT_40

__PMS_PC_SEND:
            ; send data
            out         udr,        r_data
            ldi         r_data,     0
            rjmp        __PMS_WAIT_08
            
            ;; stop sniffering
_PM_STOP:
            
            RXCR_CLEAR
            andi        r_cnt1,     3
            brne        __PMP_END
            WAIT_TX_RDY
            out         udr,        r_data;
__PMP_END:
            ldi         r_data,     0
            rjmp        _ACTIVE_MODE_08


;; ******** IDLE MODE **********************************************************

IDLE_MODE:  
            SET_OP_MODE             e_M_IDLE
            

            ; enable sleep mode
            in          r_tmp0,     sreg
            push        r_tmp0
            sei
            in          r_tmp0,     mcucr
            sbr         r_tmp0,     (1 << SE)
            out         mcucr,      r_tmp0
            sleep
            pop         r_tmp0
            out         sreg,       r_tmp0

            ; disable sleep mode
            cbr         r_tmp0,     (1 << SE)
            out         mcucr,      r_tmp0
            
            ; set post-idle mode
            ldi         zl,         LOW(POST_IDLE_MODE)
            ldi         zh,         HIGH(POST_IDLE_MODE)

;; ******** POST-IDLE MODE *****************************************************
            
POST_IDLE_MODE:
            SET_OP_MODE             e_M_P_IDLE
            WAIT_FOR_CMD
            
            rcall       LOW_PRIORITY_D
            ijmp



;; *****************************************************************************
;; FUNCTION
;; *****************************************************************************

;; ******** Send Echo **********************************************************
SEND_ECHO:

            ldi         r_tmp0,     e_RC_ECHO
            mov         r_tmp1,     r_data
            rcall       SEND_ACK
            ret

;; ******** Send Device ID *****************************************************
SEND_DEV_ID:
            ldi         r_tmp0,     e_RC_GET_ID
            ldi         r_tmp1,     c_D_DEV_ID
            rcall       SEND_ACK
            ret

;; ******** Send Device HW Version *********************************************

SEND_DEV_HW:
            ldi         r_tmp0,     e_RC_GET_HW
            ldi         r_tmp1,     c_D_DEV_HW
            rcall       SEND_ACK
            ret

;; ******** Send Device FW Version *********************************************

SEND_DEV_FW:
            ldi         r_tmp0,     e_RC_GET_FW
            ldi         r_tmp1,     c_D_DEV_FW
            rcall       SEND_ACK
            ret

;; ******** Send CPU Frequency *************************************************
;; DATA = 10 * FCPU [MHz] => 80 = 8 MHz

SEND_FCPU:
            ldi         r_tmp0,     e_RC_GET_FCPU
            ldi         r_tmp1,     c_FCPU
            rcall       SEND_ACK
            ret


;; ******** Send Device Name ***************************************************
SEND_DEV_NM:
            movw        r_bkp2_zh:r_bkp2_zl,         z      ; back-up operation mode
            ldi         zl,         low( 2* dta_DEV_NM)
            ldi         zh,         high(2* dta_DEV_NM)
_SEND_NM_NEXT:
            WAIT_TX_RDY
            lpm         r_tmp0,     z+
            out         udr,        r_tmp0
            tst         r_tmp0
            brne        _SEND_NM_NEXT
            movw        z,          r_bkp2_zh:r_bkp2_zl
            ret


;; ******** UART RX Error Recovery *********************************************            
;; call this if there is error in received data

RX_ERR_RCVR:
            ; clear status
            sbrs        r_sr0,      RXCR
            cbr         r_sr0,      (1 << RXBI) ; clear rx_byte            
            in          r_tmp0,     udr         ; clear buffer            
_RX_ERR_RCVR_08:
            ; ..................................
            WAIT_TX_RDY
            ldi         r_tmp0,     e_TC_ACK    ; set NACK value
            out         udr,        r_tmp0      ; send NACK
            WAIT_TX_RDY
            ldi         r_tmp0,     e_ACK_RX_ERR; set NACK value            
            out         udr,        r_tmp0      ; send NACK
            ; ..................................
            ret

;; ******** Send ACK to UART ***************************************************            
;; 1st byte is r_tmp0, 2nd byte is register r_tmp1
SEND_ACK:
            ; cmd
            WAIT_TX_RDY
            out         udr,        r_tmp0
            ; data
            WAIT_TX_RDY
            out         udr,        r_tmp1
            ret
;; ******** Low Priority Decoder ***********************************************            
;; 

LOW_PRIORITY_D:
            ;; DUMMY FRAME
            tst         r_cmd
            breq        _LPD_END
            ;; Echo
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_ECHO
            breq        _LPD_ECHO
            ;; Get Device ID
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_GET_ID
            breq        _LPD_GET_ID
            ;; Get Device Hardware Version and Revision
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_GET_HW
            breq        _LPD_GET_HW
            ;; Get Device Firmware Version and Revision
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_GET_FW
            breq        _LPD_GET_FW
            ;; Get Device Name
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_GET_NAME
            breq        _LPD_GET_NAME
            ;; Get CPU Frequency Code
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_GET_FCPU
            breq        _LPD_GET_FCPU
            ;; Get Operation Mode
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_GET_OPMOD
            breq        _LPD_GET_OPMOD
            ;; Set Operation Mode
            mov         r_tmp0,     r_cmd
            subi        r_tmp0,     e_RC_SET_OPMOD
            breq        _LPD_SET_OPMOD
            ;; Unknow Command
            rjmp        _LPD_UNKNOW


_LPD_END:
            RXCR_CLEAR
            ret
                        
            ;; Command Execution
_LPD_ECHO:
            rcall        SEND_ECHO
            rjmp        _LPD_END
_LPD_GET_ID:
            rcall       SEND_DEV_ID
            rjmp        _LPD_END
_LPD_GET_HW:
            rcall       SEND_DEV_HW
            rjmp        _LPD_END
_LPD_GET_FW:
            rcall       SEND_DEV_FW
            rjmp        _LPD_END
_LPD_GET_NAME:
            rcall       SEND_DEV_NM
            rjmp        _LPD_END
_LPD_GET_FCPU:
            rcall       SEND_FCPU
            rjmp        _LPD_END
_LPD_GET_OPMOD:
            WAIT_TX_RDY
            out         udr,        r_cmd
            mov         r_tmp0,     r_sr0
            andi        r_tmp0,     (1 << OPM3) | (1 << OPM2) | (1 << OPM1) | (1 << OPM0)
            swap        r_tmp0
            WAIT_TX_RDY
            out         udr,        r_tmp0
            rjmp        _LPD_END

_LPD_SET_OPMOD:
            andi        r_data,     ~((1 << OPM3) | (1 << OPM2) | (1 << OPM1) | (1 << OPM0))

            ;; Init Mode
            mov         r_tmp0,     r_data
            subi        r_tmp0,     e_M_INIT
            breq        __LPD_SET_OPMOD_INIT
            ;; Active Mode
            mov         r_tmp0,     r_data
            subi        r_tmp0,     e_M_ACTIVE
            breq        __LPD_SET_OPMOD_ACTIVE

            ;; Active Mode Master
            mov         r_tmp0,     r_data
            subi        r_tmp0,     e_M_ACTIVE_M
            breq        __LPD_SET_OPMOD_ACTIVE_M

            ;; Active Mode Slave
            mov         r_tmp0,     r_data
            subi        r_tmp0,     e_M_ACTIVE_S
            breq        __LPD_SET_OPMOD_ACTIVE_S

            ;; Active Mode Passive
            mov         r_tmp0,     r_data
            subi        r_tmp0,     e_M_ACTIVE_P
            breq        __LPD_SET_OPMOD_ACTIVE_P

            ;; Idle Mode
            mov         r_tmp0,     r_data
            subi        r_tmp0,     e_M_IDLE
            breq        __LPD_SET_OPMOD_IDLE
            
            ;; Unknow or forbidden mode
            ldi         r_tmp0,     0xFF
            rjmp        __LPD_SET_OPMOD_ACK


__LPD_SET_OPMOD_INIT:
            ldi         r_tmp0,     0x00
            ldi         zl,         LOW(INIT_MODE)
            ldi         zh,         HIGH(INIT_MODE)
            rjmp        __LPD_SET_OPMOD_ACK

__LPD_SET_OPMOD_ACTIVE:
            ldi         r_tmp0,     0x00
            ldi         zl,         LOW(ACTIVE_MODE)
            ldi         zh,         HIGH(ACTIVE_MODE)
            rjmp        __LPD_SET_OPMOD_ACK

__LPD_SET_OPMOD_ACTIVE_M:
            ldi         r_tmp0,     0x00
            ldi         zl,         LOW(MASTER_MODE)
            ldi         zh,         HIGH(MASTER_MODE)
            rjmp        __LPD_SET_OPMOD_ACK

__LPD_SET_OPMOD_ACTIVE_S:
            ldi         r_tmp0,     0x00
            ldi         zl,         LOW(SLAVE_MODE)
            ldi         zh,         HIGH(SLAVE_MODE)
            rjmp        __LPD_SET_OPMOD_ACK

__LPD_SET_OPMOD_ACTIVE_P:
            ldi         r_tmp0,     0x00
            ldi         zl,         LOW(PASSIVE_MODE)
            ldi         zh,         HIGH(PASSIVE_MODE)
            rjmp        __LPD_SET_OPMOD_ACK

__LPD_SET_OPMOD_IDLE:
            ldi         r_tmp0,     0x00
            ldi         zl,         LOW(IDLE_MODE)
            ldi         zh,         HIGH(IDLE_MODE)
            rjmp        __LPD_SET_OPMOD_ACK

__LPD_SET_OPMOD_ACK:
            WAIT_TX_RDY
            out         udr,        r_cmd

            WAIT_TX_RDY
            out         udr,        r_tmp0
            rjmp        _LPD_END

_LPD_UNKNOW:
            ;; UNKNOW COMMAND
            ldi         r_tmp0,     e_TC_ACK
            ldi         r_tmp1,     e_ACK_UNKNW_CMD
            rcall       SEND_ACK
            rjmp        _LPD_END


;; ******** Wait on TWI Interrupt *********************************************
;;

TWI_WAIT_I:
            in          r_tmp0,     twcr
            sbrs        r_tmp0,     twint
            rjmp        TWI_WAIT_I
            ret


;; *****************************************************************************
;; DATA IN PROGRAM MEMORY
;; *****************************************************************************

dta_DEV_NM: .DB  "I2C Analyzer",0,0


;; *****************************************************************************
;; EOF
;; *****************************************************************************
