;****************************************************************************
;TITLE: Program to store and recover one data word from EEPROM, depending on
;       choice of user.
;
;Format of instruction : c aaaa dddd
;   where, c is choice : read EEPROM (R) or write EEPROM (W)
;          aaaa is EEPROM address     
;          dddd is data word to be stored (if choice is W)
;
;Crystal frequency : 3.58 MHz.
;Baud rate : 2400
;
;FUNCTIONS
;*********
;receive  : receive 4 digits from comp & pack
;transmit : transmit the 4 packed digits to comp
;hextoasc : converts hex to ascii  
;asctohex : converts ascii digit to hex   
;rxcomp   : receive ascii digit from comp
;txcomp   : transmit ascii digit to comp
;epwrite  : write a data byte to EEPROM (whole routine)
;ack      : get acknowledge from EEPROM
;rxep     : receive a byte from EEPROM
;txep     : transmit a byte to EEPROM
;epread   : receive a data byte from EEPROM, in random read (whole routine)
;****************************************************************************


.include "2313def.inc"

.def rtemp   = r16
.def treg    = r17             ;data (in ASCII) to be transmitted to computer
.def rreg    = r18             ;data (in ASCII) received from computer
.def cnt     = r19             ;counter 0
.def cnt1    = r20             ;counter 1
.def datmsb  = r21             ;msb of packed data                                  
.def datlsb  = r22             ;lsb of packed data
.def datbyt  = r23             ;data received from or transmitted to EEPROM
.def choice  = r24      
.def rtemp1  = r25
.def epaddr1 = r27              ;registers to store higher and lower
.def epaddr2 = r26              ;       addresses of the EEPROM

.cseg 
.org 0
        rjmp RESET

RESET:  ldi rtemp, $a0          ; initialize stack
        out SPL, rtemp
        
        ldi rtemp,$5A           ; Setting baud rate to 2400 at crystal 
        out UBRR,rtemp          ; frequency of 3.58 MHz

        rcall rxcomp            ; get choice -- whether to read from or write
        mov choice,rreg         ; to EEPROM

        cpi choice,$52          ; if choice = R 
        breq nxt2               ;       then jump to nxt2
        cpi choice,$57          ; else if choice = W
        breq nxt2               ;               then jump to nxt2
        rjmp nxt1               ;       else jump to nxt1
        

nxt2:   rcall rxcomp            ; wait for key press
        cpi rreg,$20            ;       & check if it is space
        brne nxt2

        ldi rtemp1,0
        rcall receive           ; get memory address from user & mov MSB into
        mov epaddr1,datmsb      ; epaddr1 and LSB into epaddr2
        mov epaddr2,datlsb

bck:    rcall rxcomp            ; wait for key press
        cpi rreg,$20            ;       & check if it is space
        brne bck

        cpi choice,$52          ; if choice is to read from EEPROM
        brne nxt
        rcall epread            ;       then read a byte from EEPROM
        rcall transmit          ;       and transmit it computer.
        mov rtemp,epaddr2       ;       increment lower address &
        inc epaddr2             ;       compensate for roll over
        rol rtemp               ;       Note : page size in the EEPROM is
        brcc nxt3               ;       128 bytes. So separate roll over scheme
        ori epaddr2,$80         ;       reqd. for transition, from 7F to 00 and 
        rjmp nxt4               ;       from FF to 80, in the lower address. Hence 
nxt3:   andi epaddr2,$7F        ;       the check for 8th bit in lower address.
nxt4:   rcall epread            ;       read a byte from EEPROM
        rcall transmit          ;       and transmit it to computer.
        rjmp nxt1

nxt:    ldi rtemp1,0            ; if choice is to write to EEPROM
        rcall receive           ;       then read a byte from computer
        rcall epwrite           ;       and store it in EEPROM
   
nxt1:   ldi treg,$0a            ; leave a line after a command
        rcall txcomp
        rjmp RESET              ; loop again
        


;*****************************************************************************
;'receive'
;Aim
;       Function to receive 16 bits (4 digits) from comp, using the serial
;       port.
;Registers used
;       cnt     number the loop is to be run. For one run, two digits are
;               taken, converted to hex and packed.
;       rtemp1  value given to cnt
;       rreg    register in which packed received hex data present
;       datmsb  registers in which data bytes are stored and returned to 
;       datlsb          calling function (MSB & LSB)
;Functions called
;       rxcomp
;       asctohex
;*****************************************************************************

receive: mov cnt,rtemp1     
rebck:   rcall rxcomp    ; get one digit from comp
         rcall asctohex  ; convert received data to hex
         swap rreg       ; swap nibbles
         cpi cnt,0       ; check where to move data (msb(0) or lsb(1))
         brne renxt
         mov datmsb,rreg ; copy rreg to msb
         rjmp renxt2
renxt:   mov datlsb,rreg ; copy rreg to lsb
renxt2:  rcall rxcomp    ; get next digit from comp
         rcall asctohex
         cpi cnt,0       ; check if part of lsb or msb
         brne renxt1
         add datmsb,rreg
         rjmp renxt3
renxt1:  add datlsb,rreg
renxt3:  inc cnt
         cpi cnt,$01     ; check if 4 digits got
         breq rebck
         ret             ; go to main if all data received



;****************************************************************************
;'transmit'
;Aim
;       Function to transmit 8 bits (2 digits) to comp, using the serial
;       port.
;Registers used
;       datbyt   contains data received from calling function
;       treg     contains packed hex data to be transmitted
;Functions called
;       txcomp
;       hextoasc
;****************************************************************************

transmit: mov treg,datbyt
          swap treg             ; swap nibbles
          rcall hextoasc        ; convert one digit to ASCII       
          rcall txcomp          ;         and transmit            
          mov treg,datbyt
          rcall hextoasc        ; convert next digit to ASCII
          rcall txcomp          ;         and transmit
          ret                   ; return if complete data transmitted


;*****************************************************************************          
;'hextoasc'
;Aim
;       Convert hex to ASCII. Hex value is passed from & ASCII value is passed
;       back to calling function in register treg.
;Registers used
;       rtemp   temporary register
;       treg    register containing data
;Functions called
;       none
;*****************************************************************************

hextoasc: andi treg,$0f
          cpi treg,$0a
          brsh henxt1
          ldi rtemp,$30
          rjmp henxt2
henxt1:   ldi rtemp,$37       
henxt2:   add treg,rtemp
          ret


;*****************************************************************************
;'asctohex'
;Aim
;       Convert ASCII to hex. ASCII value to be converted is passed from & hex
;       value is passed back to calling function in register rreg.
;Registers used
;       rreg    register containing data
;Functions called
;       none
;*****************************************************************************

asctohex: cpi rreg,$40
          brsh asnxt
          subi rreg,$30
          rjmp asnxt1
asnxt:    subi rreg,$37
asnxt1:   ret

;*****************************************************************************
;'rxcomp'
;Aim
;       Function to receive data (1 digit) from computer. Value received is 
;       passed to calling function through register rreg.
;Registers used
;       rtemp   temporary register
;       rreg    register containing data received
;Functions called
;       none
;*****************************************************************************

rxcomp: ldi rtemp, $10  ; enable receiver
        out UCR,rtemp
rxpoll: sbis USR,7      ; poll to check if char received
        rjmp rxpoll
        in rreg,UDR     ; put received data in rreg
        ret

;*****************************************************************************
;'txcomp'
;Aim
;       Function to transmit data (1 digit) to computer. The value to be 
;       transmitted is passed from calling function into register treg.
;Registers used
;       rtemp   temporary register
;       treg    register containing data to be transmitted
;Functions called
;       none
;*****************************************************************************
                        
txcomp: ldi rtemp, $08  ; Enable transmitter
        out UCR,rtemp
        out UDR,treg
txpoll: sbis USR,5      ; Poll to check end of transmission
        rjmp txpoll
        ret

;******************************************************************************
;'epwrite'
;Aim
;       Complete function to write a byte into eprom
;Registers used
;       datbyt          register in which data to be transmitted is stored
;       rtemp           temporary register
;       epaddr1,epaddr2 address registers (msb,lsb)
;       datlsb          value passed from main
;Functions called
;       ack
;       txep
;*****************************************************************************

epwrite: ldi  rtemp, 0b11111111  ; configure B as output port  
         out DDRB,rtemp
         sbi PORTB,PINB4         ; start bit: set SDA
         sbi PORTB,PINB5         ;            set SCL
         cbi PORTB,PINB4         ;            clear SDA
         ldi datbyt,0b10100000   ; load device address of EEPROM
         rcall txep
         rcall ack
         mov datbyt,epaddr1      ; load memory location 1 
         rcall txep
         rcall ack
         mov datbyt,epaddr2      ; load memory location 2
         rcall txep
         rcall ack
         mov datbyt,datmsb       ; write first byte to EEPROM
         rcall txep
         rcall ack
         mov datbyt,datlsb       ; write second byte to EEPROM
         rcall txep
         rcall ack
         cbi PORTB,PINB4         ; stop bit:clear SDA
         sbi PORTB,PINB5         ;          set SCL
         sbi PORTB,PINB4         ;          set SDA
         ret

;****************************************************************************
;'txep'
;Aim
;       Function to write a byte to EEPROM. Value to be written is passed from
;       calling function in register datbyt      
;Registers used
;       cnt     to store no of bits
;       datbyt  to store the data 
;Functions called
;       none       
;*****************************************************************************

txep:   ldi cnt, $08            ; load counter 
txbck:  rol datbyt              ; rotate data left through carry
        cbi PORTB,PINB5         ; clear SCL
        brcc txnxt              ; if carry set
        sbi PORTB,PINB4         ;    set SDA
        rjmp txnxt1             ; else
txnxt:  cbi PORTB,PINB4         ;    clear SDA
txnxt1: sbi PORTB,PINB5         ; set SCL
        dec cnt                 ; decrement counter
        brne txbck              ; if counter not zero, go back
        nop
        cbi PORTB,PINB5         ; clear SCL
        ret

;****************************************************************************
;'ack'
;Aim
;       To get the acknowledge signal from the EEPROM       
;Registers used
;       none
;Functions used
;       none
;****************************************************************************

ack:    sbi PORTB,PINB5         ; set SCL
        ldi rtemp,0b11101111    ; configure pinb4 as input
        out DDRB,rtemp
acbck:  sbic PINB,PINB4         ; goto calling function if SDA clear
        rjmp acbck              ;      else go back to ack
        cbi PORTB,PINB5         ; clear SCL
        ldi rtemp,0b11111111    ; configure portb as output
        out DDRB,rtemp
        ret

;******************************************************************************
;'rxep'
;Aim
;       Function to read a byte from EEPROM. Value read is returned in 
;       register datbyt      
;Registers used
;       cnt     to store no of bits
;       datbyt  to store the data read
;Functions called
;       none
;*****************************************************************************

rxep:   ldi  rtemp, 0b11101111   ; configure PB4 as input  
        out DDRB,rtemp
        sbi PORTB,PINB4
        ldi cnt,$08             ; load counter
        ldi datbyt, 0
rxbck:  sbi PORTB,PINB5         ; set SCL 
        sbic PINB,PINB4         ; if SDA set then
        sec                     ;        set carry
        sbis PINB,PINB4         ; else 
        clc                     ;       clear carry
        rol datbyt              ; rotate data received from EEPROM left
        cbi PORTB,PINB5         ; clear SCL
        dec cnt                 ; decrement counter
        brne rxbck              ; if counter equals zero, goto main
        ret                     ;    else jump back

;******************************************************************************
;'epread'
;Aim
;       Complete function to read a byte from EEPROM. 
;Registers used
;       datbyt          register in which data read is stored
;       rtemp           temporary register
;       epaddr1,epaddr2 address registers (msb,lsb)
;Functions called
;       ack
;       txep
;       rxep
;*****************************************************************************

epread: ldi  rtemp, 0b11111111   ; configure B as output port  
        out DDRB,rtemp
        sbi PORTB,PINB4          ; start bit: set SDA
        sbi PORTB,PINB5          ;            set SCL
        cbi PORTB,PINB4          ;            clear SDA
        ldi datbyt,0b10100000    ; load device address
        rcall txep
        rcall ack
        mov datbyt,epaddr1       ; load memory location 1 
        rcall txep
        rcall ack
        mov datbyt,epaddr2       ; load memory location 2
        rcall txep
        rcall ack
        sbi PORTB,PINB4          ; start bit: set SDA
        sbi PORTB,PINB5          ;            set SCL
        cbi PORTB,PINB4          ;            clear SDA
        ldi datbyt,0b10100001    ; load device address
        rcall txep
        rcall ack
        rcall rxep               ; read data from EEPROM
        sbi PORTB,PINB5          ; give clock pulse: set SCL
        nop
        cbi PORTB,PINB5          ;                   clear SCL
        ldi  rtemp, 0b11111111   ; configure B as output port
        out DDRB,rtemp
        cbi PORTB,PINB4          ; stop bit: clear SDA 
        sbi PORTB,PINB5          ;           set SCL
        sbi PORTB,PINB4          ;           set SDA
        ret      

;******************************************************************************