; Mini-application for the sensor microcontrollers on the security board.
;
; ### license
;
; ### docco
;
; WRITE-TO and READ-FROM operations are supported.
;
; OPCODE values are defined as:
;   ### todo
;
; PINS:
;   ### TBD
;

        list p = 16f688
        include p16f688.inc
        include ../include/handy.inc

        include ../include/i2c.inc

        __config DEFAULT_CONFIG

; i2c.inc only has the no-prefix entrypoints. Define the prefixed forms.
 extern LOCAL_I2C_S_listen
 extern LOCAL_I2C_S_detect_START
 extern LOCAL_I2C_S_saw_START
 extern LOCAL_I2C_S_read
 extern LOCAL_I2C_S_read_stretch
 extern LOCAL_I2C_S_end_stretch
 extern LOCAL_I2C_S_write

 extern LOCAL_I2C_skip_unknown


; Pin definitions
 constant LOCAL_SCL = 4         ; PORTA
 constant LOCAL_SDA = 5         ; PORTA

 constant ALERT = 2             ; PORTA


        ; Define this module's variables
.gpr0   udata

; The OPCODE sent in a WRITE-TO request on the I2C bus.
OPCODE  res     1

; Configuration mask
 global CONFIG_MASK
CONFIG_MASK res 1
        ; Bit 0 : ### TBD

; What we have *just* read on the sensors. Updated every 5 µs (approx).
LAST_READ       res     1

; A complete mask of all sensors that we've seen tripped since this register
; was last read by the main app. Each read will clear it.
SEEN_TRIPPED    res     1


.reset: org  0x0000
        goto main


.main:  code                    ; relocatable
main:

        bank0

        ; Disable comparators. We're going to use all RA* as INPUTS/OUTPUTS.
        movlw   0x07
        movwf   CMCON0

        ; NOTE: we cannot "pre-latch" the ALERT pin to LOW since *any* read
        ;       of PORTA will copy that pin's state (usually HIGH) into the
        ;       internal latch.

        bank1s

        ; Switch to running at 8 Mhz
        movlw   0x70
        iorwf   OSCCON, F

        ; Ensure AN* are configured as DIGITAL pins.
        clrf    ANSEL

        ; TRISA:
        ; PORTA has two inputs, the ALERT pin, and the LOCAL I2C bus. All
        ; of these begin as INPUTs (HIGH-Z; the default). They will switch
        ; to OUTPUTs when appropriate for their function.

        ; TRISC:
        ; All of PORTC are sensor inputs. Leave their tristates as INPUTs
        ; (HIGH-Z; the default).

        ; Get some configuration information
        read_eeprom EE_CONFIG_MASK
        movwf   CONFIG_MASK

        bank0s

        ; Clear SEEN_TRIPPED, as we haven't "seen" any sensors tripped.
        clrf    SEEN_TRIPPED

        ; ### need to set up the I2C slave module

busy_loop_entry:
        ; Ensure the carry is cleared, as we'll be rotating OUT of the carry
        ; flag. We will only rotate PORTC<6:7> INTO carry, which is always 0.
        clrc

busy_loop:
        ; Check if LOCAL-SDA has transitioned LOW (while SCL remains HIGH).
        ; t(HD;STA) is 4.0 µs minimum, then SCL will transition LOW and hold
        ; for t(LOW) of 4.7 µs until SCL then transitions HIGH to deliver
        ; the first bit. That gives us 17 instructions to see SCL LOW, then
        ; watch for the transition edge.

        ; NOTE: this occurs on the LOCAL bus, which is master'd by one of
        ; our apps, so we *could* adjust the master to be a little more
        ; gentle on the above timing/holds if we see any problems.

        ; The primary loop (below) is about 11 instruction cycles, or
        ; about 5.5 µs. This falls within the above timing restrictions.

        ; Is LOCAL-SDA still HIGH?
        btfss   PORTA, LOCAL_SDA
        goto    local_activity

        ; The sensors are Normally-Closed (NC) to GROUND (LOW). If they trip,
        ; or a wire is cut, then a pullup resistor brings the pin HIGH.
        ;
        ; Sensors are on RA0, RA1, and RC0..RC5. We can shift these into an
        ; 8-bit value.

        rlf     PORTC, W        ; shift once, get it into W
        movwf   LAST_READ
        rlf     LAST_READ, F    ; one more time. now LAST_READ<0:1> is 0.
        movfw   PORTA
        andlw   0x03
        iorwf   LAST_READ, F    ; move PORTA<0:1> into LAST_READ

        ; Z flag is set/cleared by the IORWF. If any bits are set, then a
        ; sensor has been tripped.
        bz      busy_loop

        ; A sensor was tripped!

        ; We record all "seen" tripped sensors until the value is read.
        movfw   LAST_READ
        iorwf   SEEN_TRIPPED, F

        ; Pull the ALERT line low: set the pin's latch to LOW, then switch
        ; the pin to an OUTPUT.
        ; NOTE: the ALERT pin is on PORTA.
        bcf     PORTA, ALERT
        bank1s
        bcf     TRISA, ALERT
        bank0s

        goto    busy_loop


local_activity:
        ; ### do something
        ; ### check OPCODE?? maybe no opcode ... just reading TRIPPED


read_tripped:
        movfw   SEEN_TRIPPED

        ; ### return the value

        ; Reset the tripped register, and return the ALERT pin to its
        ; INPUT/HIGH-Z state.

        clrf    SEEN_TRIPPED
        bank1s
        bsf     TRISA, ALERT
        bank0s

        ; All done! ... return to normal operation.
        goto    busy_loop_entry


; stupid assemblers don't realize EOF means *end*
        end
