; ***************************************************************************
; copyright   : (C) 2026 by Martin Preuss
; email       : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome".                              *
; * Please see toplevel file COPYING of that project for license details.   *
; ***************************************************************************

#ifndef AVR_MODULES_COM2W2_IO_H
#define AVR_MODULES_COM2W2_IO_H


.cseg




; ---------------------------------------------------------------------------
; @routine com2w2EnableClkIrq
;
; @clobbers 

com2w2EnableClkIrq:
.ifdef COM_ENABLEIRQ_FN
  bigcall COM_ENABLEIRQ_FN
.else
  push r16
    inr r16, COM_IRQ_ADDR_CLK
    sbr r16, (1<<COM_IRQ_BIT_CLK)          ; enable pin change irq for CLK line
    outr COM_IRQ_ADDR_CLK, r16
  pop r16
.endif
  
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2w2DisableClkIrq
;
; @clobbers none

com2w2DisableClkIrq:
  push r16
    inr r16, COM_IRQ_ADDR_CLK
    cbr r16, (1<<COM_IRQ_BIT_CLK)        ; disable pin change irq for CLK line
    outr COM_IRQ_ADDR_CLK, r16
  pop r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2w2ClkSetHigh
;
; @clobbers none

com2w2ClkSetHigh:
  cbi COM_CLK_DDR, COM_CLK_PIN           ; set CLK as input
.ifdef COM_CLK_PUE
;  cbi COM_CLK_PUE, COM_CLK_PIN           ; disable pullup on CLK
.else
  cbi COM_CLK_OUTPUT, COM_CLK_PIN        ; disable pullup on CLK
.endif
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2w2ClkSetLow
;
; @clobbers none

com2w2ClkSetLow:
  sbi COM_CLK_DDR, COM_CLK_PIN          ; set CLK as output
  cbi COM_CLK_OUTPUT, COM_CLK_PIN       ; set CLK low
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2w2DataSetHigh
;
; @clobbers none

com2w2DataSetHigh:
  cbi COM_DATA_DDR, COM_DATA_PIN           ; set DATA as input
.ifdef COM_DATA_PUE
;  cbi COM_DATA_PUE, COM_CLK_PIN           ; disable pullup on DATA
.else
  cbi COM_DATA_OUTPUT, COM_DATA_PIN        ; disable pullup on DATA
.endif
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2w2DataSetLow
;
; @clobbers none

com2w2DataSetLow:
  sbi COM_DATA_DDR, COM_DATA_PIN          ; set DATA as output
  cbi COM_DATA_OUTPUT, COM_DATA_PIN       ; set DATA low
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2w2WaitForClockHighMulti10Us
;
; Wait for high CLK
;
; @param  R20 multiple of 10us to wait (e.g. "2" for "20" us)
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers: r20, r22

com2w2WaitForClockHighMulti10Us:
.if clock > 1000000
; begin version for > 1000000 Hz
  ldi r22, clock/1000000
com2w2WaitForClockHighMulti10Us_loop0:
    push r20
com2w2WaitForClockHighMulti10Us_loop:                      ; 10 cycles per loop
        sbic COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
          rjmp com2w2WaitForClockHighMulti10Us_stateReached ; (+2 if taken)
        sbic COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
          rjmp com2w2WaitForClockHighMulti10Us_stateReached ; (+2 if taken)
        sbic COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
          rjmp com2w2WaitForClockHighMulti10Us_stateReached ; (+2 if taken)
        nop                                               ; +1
        dec r20                                           ; +1
      brne com2w2WaitForClockHighMulti10Us_loop            ; +2 if TRUE, +1 if FALSE
    pop r20
    dec r22                                               ; +1
  brne com2w2WaitForClockHighMulti10Us_loop0               ; +1 if FALSE, +2 if TRUE
  clc                                                     ; +1
  ret                                                     ; +4
com2w2WaitForClockHighMulti10Us_stateReached:
  pop r20
  sec                                                 ; +1
  ret                                                 ; +4
; end version for > 1000000 Hz
.elif clock < 1000000
  .error "Clock speed too low"
.else
; begin version for 1000000 Hz
com2w2WaitForClockHighMulti10Us_loop:                  ; 10 cycles per loop
    sbic COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
      rjmp com2w2WaitForClockHighMulti10Us_stateReached ; (+2 if taken)
    sbic COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
      rjmp com2w2WaitForClockHighMulti10Us_stateReached ; (+2 if taken)
    sbic COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
      rjmp com2w2WaitForClockHighMulti10Us_stateReached ; (+2 if taken)
    nop                                               ; +1
    dec r20                                           ; +1
  brne com2w2WaitForClockHighMulti10Us_loop             ; +2 if TRUE, +1 if FALSE
  clc                                                 ; +1
  ret                                                 ; +4
com2w2WaitForClockHighMulti10Us_stateReached:
  sec                                                 ; +1
  ret                                                 ; +4
; end version for 1000000 Hz
.endif
; @end



; ---------------------------------------------------------------------------
; @routine com2w2WaitForClockLowMulti10Us
;
; Wait for low CLK
;
; @param  R20 multiple of 10us to wait (e.g. "2" for "20" us)
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers: r20, r22

com2w2WaitForClockLowMulti10Us:
.if clock > 1000000
; begin version for > 1000000 Hz
  ldi r22, clock/1000000
com2w2WaitForClockLowMulti10Us_loop0:
    push r20
com2w2WaitForClockLowMulti10Us_loop:                   ; 10 cycles per loop
        sbis COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
          rjmp com2w2WaitForClockLowMulti10Us_stateReached ; (+2 if taken)
        sbis COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
          rjmp com2w2WaitForClockLowMulti10Us_stateReached ; (+2 if taken)
        sbis COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
          rjmp com2w2WaitForClockLowMulti10Us_stateReached ; (+2 if taken)
        nop                                             ; +1
        dec r20                                         ; +1
      brne com2w2WaitForClockLowMulti10Us_loop           ; +2 if TRUE, +1 if FALSE
    pop r20
    dec r22                                           ; +1
  brne com2w2WaitForClockLowMulti10Us_loop0            ; +1 if FALSE, +2 if TRUE
  clc                                                 ; +1
  ret                                                 ; +4
com2w2WaitForClockLowMulti10Us_stateReached:
  pop r20
  sec                                                 ; +1
  ret                                                 ; +4
; end version for > 1000000 Hz
.elif clock < 1000000
  .error "Clock speed too low"
.else
; begin version for 1000000 Hz
com2w2WaitForClockLowMulti10Us_loop:                   ; 10 cycles per loop
    sbis COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
      rjmp com2w2WaitForClockLowMulti10Us_stateReached ; (+2 if taken)
    sbis COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
      rjmp com2w2WaitForClockLowMulti10Us_stateReached ; (+2 if taken)
    sbis COM_CLK_INPUT, COM_CLK_PIN                   ; +2 if skipped, +1 if taken
      rjmp com2w2WaitForClockLowMulti10Us_stateReached ; (+2 if taken)
    nop                                               ; +1
    dec r20                                           ; +1
  brne com2w2WaitForClockLowMulti10Us_loop             ; +2 if TRUE, +1 if FALSE
  clc                                                 ; +1
  ret                                                 ; +4
com2w2WaitForClockLowMulti10Us_stateReached:
  sec                                                 ; +1
  ret                                                 ; +4
; end version for 1000000 Hz
.endif
; @end



; ---------------------------------------------------------------------------
; @routine com2w2WaitTime1
;
; waits for longer period (e.g. 30ns)
;
; @clobbers R22

com2w2WaitTime1:
  Utils_WaitNanoSecs COM2W2_WAITTIME1, 7, r22            ; wait for longer time (minus RCALL and RET)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2w2WaitTime2
;
; waits for shorter period (e.g. 10ns)
;
; @clobbers R22

com2w2WaitTime2:
  Utils_WaitNanoSecs COM2W2_WAITTIME2, 7, r22            ; wait for shorter time (minus RCALL and RET)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2w2PcintIsr @global @isr
;
; ISR for PCINT0/1
;
; @clobbers: none

com2w2PcintIsr:
  push r15
    in r15, SREG
    sbic COM_CLK_INPUT, COM_CLK_PIN
      rjmp com2w2PcintIsr_end
    ; low, read packet
    push r16
      push r17
        push r18
          push r19
            push r20
              push r21
                push r22
                  push r24
                    push r25
                      push xl
                        push xh
                          push yl
                            push yh
                              ldi yl, LOW(com2w2_iface)
                              ldi yh, HIGH(com2w2_iface)
                              rcall com2w2ReceiveNextPkg         ; (R16, R17, R18, R19, R20, R21, R22, R24, R25, X)
                            pop yh
                          pop yl
                        pop xh
                      pop xl
                    pop r25
                  pop r24
                pop r22
              pop r21
            pop r20
          pop r19
        pop r18
      pop r17
    pop r16
com2w2PcintIsr_end:
    out SREG, r15
  pop r15
  reti
; @end





#endif

