; EEPROM (related subroutines)
; This is a collection of subroutines for reading, writing, and
; erasing the 93LC56 (or 'C56) serial EEPROM. As a demonstration, it 
; writes a scanning pattern to the 256 bytes of the EEPROM, and then
; reads it back to eight LEDs connected to port rb. 

        device  pic16c54,xt_osc,wdt_off,protect_off
        reset   start

D       =       ra.0    ; Pins DI and DO of the EEPROM
CLK     =       ra.1    ; Clock pin--data valid on rising edge
CS      =       ra.2    ; Chip select--high = active
ROP     =       192     ; Opcode for read
WROP    =       160     ; Opcode for write
EWEN    =       152     ; Opcode to enable erase and write
EWDS    =       128     ; Opcode to disable erase and write
ERAS    =       224     ; Opcode to erase a byte
ERAL    =       144     ; Opcode to erase all bytes
WRAL    =       136     ; Opcode to write all bytes

        org     8
temp    ds      1       ; Temporary variable for EE routines
EEdata  ds      1       ; Data to EEwrite/wrall, from EEread
EEaddr  ds      1       ; Address to EErase, EEwrite, EEread
clocks  ds      1       ; Number of clock cycles for SHiftOUT
tick1   ds      1       ; Timer for demo. 
tick2   ds      1       ; Timer for demo. 

        org     0
start   mov     ra,#0           ; Clear ports
        mov     rb,#0   
        mov     !ra,#0          ; Make all ra pins outputs initially. 
        mov     !rb,#0          ; Output for LEDs. 
        call    EEnable         ; Turn off write/erase protection. 
        mov     EEdata,#1       ; EEdata = 1. 
        mov     EEaddr,#0       ; Start at address 0. 
:loop   call    EEwrite         ; Write pattern to EEPROM
        call    Busy            ; Wait until done writing. 
        rr      EEdata          ; Rotate the pattern right. 
        ijnz    EEaddr,:loop    ; FOR EEaddr = 0 to 255...
        call    EEdisbl         ; Turn write/erase protection back on. 
:loop2  call    EEread          ; Now read back contents of EEPROM. 
        mov     rb,EEdata       ; Display the EEPROM data on LEDs. 
        inc     EEaddr          ; Set up for next address. 
        call    delay           ; Delay to slow down the scanning. 
        jmp     :loop2          ; Do forever. 

; Shout shifts out the bits of the variable temp to the EEPROM data line. 
; All of the other EEPROM routines call Shout. 
Shout   rl      temp            ; Rotate bit7 of temp into carry
        movb    D,c             ; Move carry bit to input of EEPROM
        setb    CLK             ; Clock the bit into EEPROM
        nop                     ; Clock must be high > 500 ns
        clrb    CLK
        djnz    clocks,Shout    ; FOR I = 1 TO clocks...
        ret

; Read the byte in EEaddr into EEdata. 
EEread  mov     temp,#ROP       ; Move the read opcode into temp        
        mov     clocks,#4       ; Number of bits to shift out (op+1)
        setb    CS              ; Select the EEPROM. 
        call    Shout           ; Shift the read opcode out. 
        mov     clocks,#8       ; Number of bits to output (8 addr bits).
        mov     temp,EEaddr     ; Set up to send the address. 
        call    Shout           ; Shift out the address. 
        mov     !ra,#1          ; Now, change D to input. 
        mov     clocks,#8       ; Number of bits to input (8 data bits).
:read   setb    CLK             ; Data valid on rising edge of clock. 
        movb    c,D             ; Data bit into carry. 
        rl      temp            ; Rotate carry into temp. 
        clrb    CLK             ; Falling edge of clock. 
        djnz    clocks,:read    ; FOR I = 1 TO 8...
        mov     EEdata,temp     ; Move temp into EEdata
        mov     !ra,#0          ; Restore D to output. 
        clrb    CS              ; Deselect the EEPROM. 
        ret

; Call to unprotect after EEdisbl or power up or any time EEPROM
; has been write protected. 
EEnable setb    CS              ; Select the EEPROM. 
        mov     clocks,#12      ; Set up to shift out opcode. 
        mov     temp,#EWEN      ; temp = erase/write-enable op code.
        call    Shout           ; Shift temp out to the EEPROM. 
        clrb    CS              ; Done. Deselect EEPROM. 
        ret

; Call to protect against accidental write/erasure. Manufacturer advises
; leaving EEPROM in write-protected state most of the time to prevent accidental
; alteration by a runaway program. 
EEdisbl setb    CS              ; Select the EEPROM. 
        mov     clocks,#12      ; Set up to shift out opcode. 
        mov     temp,#EWDS      ; temp = erase/write-disable op code.
        call    Shout           ; Shift temp out to the EEPROM. 
        clrb    CS              ; Done. Deselect EEPROM. 
        ret

; Write the byte in EEdata to EEaddr. 
EEwrite mov     temp,#WROP      ; Get the write op code. 
        mov     clocks,#4       ; Prepare to shift out 4 msbs. 
        setb    CS              ; Select the EEPROM. 
        call    Shout           ; Shift temp out to the EEPROM. 
        mov     clocks,#8       ; Now prepare for 8 addr bits. 
        mov     temp,EEaddr     ; Move address into temp. 
        call    Shout           ; Shift temp out to the EEPROM. 
        mov     clocks,#8       ; Prepare for 8 data bits. 
        mov     temp,EEdata     ; Move data into temp. 
        call    Shout           ; Shift temp out to the EEPROM. 
        clrb    CS              ; Done. Deselect EEPROM. 
        ret

; Erase the byte in EEaddr. Erasure leaves 0FFh (all 1s) in the byte.
EErase  mov     temp,#ERAS      ; Get the erase op code. 
        mov     clocks,#4       ; Prepare to shift out 4 msbs. 
        setb    CS              ; Select the EEPROM. 
        call    Shout           ; Shift temp out to the EEPROM. 
        mov     clocks,#8       ; Prepare for 8 addr bits. 
        mov     temp,EEaddr     ; Move address into temp. 
        call    Shout           ; Shift address out to the EEPROM. 
        clrb    CS              ; Done. Deselect EEPROM. 
        ret

; Erase the entire EEPROM--all 256 bytes. Call before using EEwrall below. 
EEwipe  setb    CS              ; Select the EEPROM. 
        mov     temp,#ERAL      ; Get the erase-all opcode. 
        mov     clocks,#12      ; Prepare to shift out 12 bits. 
        call    Shout           ; Send op code + don't-care bits.
        clrb    CS              ; Done. Deselect EEPROM. 
        ret

; Write the byte in EEdata to every address. Call EEwipe first.
EEwrall setb    CS              ; Select the EEPROM. 
        mov     temp,#WRAL      ; Get the write-all opcode. 
        mov     clocks,#12      ; Prepare to shift out 12 bits. 
        call    Shout           ; Send op code + don't-care bits.
        mov     clocks,#8       ; Prepare to shift out 8 data bits. 
        mov     temp,EEdata     ; Move data into temp. 
        call    Shout           ; Shift temp out to EEPROM. 
        clrb    CS              ; Done. Deselect EEPROM. 
        ret

; Check flag to determine whether the EEPROM has finished its self-timed
; erase/write. Caution: This will lock up your program until D goes high. 
Busy    mov     !ra,#1          ; Set D to input. 
        setb    CS              ; Select the EEPROM. 
:wait   jnb     D,:wait         ; Wait until D goes high (end of write). 
        clrb    CS              ; Done. Deselect EEPROM. 
        mov     !ra,#0          ; Restore D to output. 
        ret

; Delay routine for demo. 
Delay   djnz    tick1,Delay     
        djnz    tick2,Delay
        ret