; ***************************************************************************
; copyright   : (C) 2025 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_COM2W_COM2WN_RUN_H
#define AVR_MODULES_COM2W_COM2WN_RUN_H


; WORK IN PROGRESS


.cseg



; ---------------------------------------------------------------------------
; @routine COM2WN_Run
;
; @return CFLAG set if something done, cleared otherwise
; @clobbers all

COM2WN_Run:
  clr r16
; first handle lowlevel (e.g. handle all CLK changes and DATA from ringbuffer)
COM2WN_Run_loop1:
    push r16
      rcall com2wnLowLevelRun
    pop r16
    brcc COM2WN_Run_handleRunModes
    rcall com2wnSetR16OnCarrySet
  rjmp COM2WN_Run_loop1

; then handle messages etc
COM2WN_Run_handleRunModes:
  ldi r19, COM_PORTS
  ldi yl, LOW(com2w0_iface)             ; first interface
  ldi yh, HIGH(com2w0_iface)
COM2WN_Run_loop2:
    push r16
      push r19
        rcall com2wnRunMode             ; (clobbers possibly all but Y)
      pop r19
    pop r16
    rcall com2wnSetR16OnCarrySet
    ldi r20, COM2W_IFACE_SIZE
    add yl, r20
    adc yh, r20
    sub yh, r20
    dec r19
  brne COM2WN_Run_loop2
  tst r16
  clc
  breq COM2WN_Run_ret
  sec
COM2WN_Run_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wnSetR16OnCarrySet
;

com2wnSetR16OnCarrySet:
  brcc com2wnSetR16OnCarrySet_ret
  ldi r16, 1
com2wnSetR16OnCarrySet_ret:
  ret
; @end


; ---------------------------------------------------------------------------
; @routine COM2WN_Periodically
;

COM2WN_Periodically:
  ldi r19, COM_PORTS
  ldi yl, LOW(com2w0_iface)             ; first interface
  ldi yh, HIGH(com2w0_iface)
COM2WN_Periodically_loop:               ; loop through all interfaces
    push r19
      rcall com2wPeriodically           ; (R16)
    pop r19
    ldi r20, COM2W_IFACE_SIZE
    add yl, r20
    adc yh, r20
    sub yh, r20
    dec r19
  brne COM2WN_Periodically_loop
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wnLowLevelRun
;

com2wnLowLevelRun:
.if COM_DATA_INPUT == COM_CLK_INPUT
  ; begin routine for single port
  ldi yl, LOW(com2wnIoRingBuffer)
  ldi yh, HIGH(com2wnIoRingBuffer)
  rcall RingBufferY_ReadByteGuarded         ; (R17, R18, X)
  brcc com2wnLowLevelRun_ret
  
  lds r18, com2wnIoCurrentClockStates
  eor r18, r16                              ; r18: changed bits in clk states
  sts com2wnIoCurrentClockStates, r16       ; store new state
  ; r16=clock byte, r17=data byte, r18=clock change byte
  ldi r19, COM_PORTS
  ldi yl, LOW(com2w0_iface)                 ; first interface
  ldi yh, HIGH(com2w0_iface)
com2wnLowLevelRun_loop:              ; loop through all interfaces
    ldd r20, Y+COM2W_IFACE_OFFS_PINMASK_CLK
    mov r21, r20
    and r21, r18                            ; CLK for interface changed?
    breq com2wnLowLevelRun_nextIface
    push r16
      push r18
        push r19
          mov r17, r16                      ; same byte on onePort system
          rcall com2wnActOnClock            ; (r16, r17, r18, X)
        pop r19
      pop r18
    pop r16
com2wnLowLevelRun_nextIface:
    ldi r20, COM2W_IFACE_SIZE
    add yl, r20
    adc yh, r20
    sub yh, r20
    dec r19
  brne com2wnLowLevelRun_loop
  sec
com2wnLowLevelRun_ret:
  ret
  ; end routine for single port
.else
  ; begin routine for double port
  ldi yl, LOW(com2wnIoRingBuffer)
  ldi yh, HIGH(com2wnIoRingBuffer)
  push r15
    in r15, SREG
    cli
    rcall RingBufferY_ReadByte                ; (R17, R18, X)
    brcc com2wnLowLevelRun_popr15Ret
    push r16
      rcall RingBufferY_ReadByte              ; (R17, R18, X)
      mov r17, r16
    pop r16
    out SREG, r15
  pop r15
  
  lds r18, com2wnIoCurrentClockStates
  eor r18, r16                              ; r18: changed bits in clk states
  sts com2wnIoCurrentClockStates, r16       ; store new state
  ; r16=clock byte, r17=data byte, r18=clock change byte
  ldi r19, COM_PORTS
  ldi yl, LOW(com2w0_iface)                 ; first interface
  ldi yh, HIGH(com2w0_iface)
com2wnLowLevelRun_loop:              ; loop through all interfaces
    ldd r20, Y+COM2W_IFACE_OFFS_PINMASK_CLK
    mov r21, r20
    and r21, r18                            ; CLK for interface changed?
    breq com2wnLowLevelRun_nextIface
    push r16
      push r17
        push r18
          push r19
            and r16, r20                      ; only keep CLK mask for current interface
            ldd r20, Y+COM2W_IFACE_OFFS_PINMASK_DATA
            and r17, r20                      ; only keep DATA mask for current interface
            rcall com2wnActOnClock            ; (r16, r17, r18, X)
          pop r19
        pop r18
      pop r17
    pop r16
com2wnLowLevelRun_nextIface:
    ldi r20, COM2W_IFACE_SIZE
    add yl, r20
    adc yh, r20
    sub yh, r20
    dec r19
  brne com2wnLowLevelRun_loop
  sec
  rjmp com2wnLowLevelRun_ret
com2wnLowLevelRun_popr15Ret:
    out SREG, r15
  pop r15
  clc
com2wnLowLevelRun_ret:
  ret
  ; end routine for double port
.endif
; @end




; ---------------------------------------------------------------------------
; @routine com2wPeriodically @global
;
; @param Y   pointer to interface data
; @clobbers R16

com2wPeriodically:
  push r15
    in r15, SREG
    cli
    rcall NET_Interface_Periodically         ; (R16)
    ldd r16, Y+COM2W_IFACE_OFFS_MODECOUNTER
    inc r16
    breq com2wPeriodically_end
    std Y+COM2W_IFACE_OFFS_MODECOUNTER, r16
com2wPeriodically_end:
    out SREG, r15
  pop r15
  ret
; @end





; ---------------------------------------------------------------------------
; @routine com2wSetMode
;
; Doesn't change processor status flags!
;
; @param R16 mode
; @clobbers R17

com2wSetMode:
  push r15
    in r15, SREG
    cli
    ldd r17, Y+COM2W_IFACE_OFFS_MODE
    cp r16, r17
    breq com2wSetMode_end
    std Y+COM2W_IFACE_OFFS_MODE, r16
    clr r17
    std Y+COM2W_IFACE_OFFS_MODECOUNTER, r17
com2wSetMode_end:
    out SREG, r15
  pop r15
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wnStartReading
;
; @param Y pointer to interface data in SRAM
; @clobbers R16, R17, X

com2wnStartReading:
  mov xl, yl
  mov xh, yh
  adiw xh:xl, COM2W_IFACE_OFFS_BUFFER
  std Y+COM2W_IFACE_OFFS_BUFPOS_LOW, xl
  std Y+COM2W_IFACE_OFFS_BUFPOS_HIGH, xh
  ldi r16, COM2W_BUFFER_SIZE
  std Y+COM2W_IFACE_OFFS_BUFLEFT, r16
  clr r16
  std Y+COM2W_IFACE_OFFS_BUFUSED, r16
  std Y+COM2W_IFACE_OFFS_BITCOUNTER, r16
  std Y+COM2W_IFACE_OFFS_CURRBYTE, r16
  ldi r16, COM2W_MODE_READING
  rcall com2wSetMode                      ; (R17)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wnRunMode
;
; @param Y pointer to interface data in SRAM
; @clobbers all

com2wnRunMode:
  cpi r16, COM2W_MODE_NUM
  brcs com2wnRunMode_jump
  ldi r16, COM2W_MODE_IDLE              ; unknown mode, set to idle
  rcall com2wSetMode                    ; (R17)
  sec
  ret
com2wnRunMode_jump:
  ldi zl, LOW(com2wnModeJumpTable)
  ldi zh, HIGH(com2wnModeJumpTable)
  add zl, r16
  adc zh, r16
  sub zh, r16
  ijmp
com2wnModeJumpTable:
  rjmp com2wnRunIdle
  rjmp com2wnRunReading
  rjmp com2wnRunSkipping
  rjmp com2wnRunWriting
; @end



; ---------------------------------------------------------------------------
; @routine com2wnRunIdle
;
; @param Y pointer to interface data in SRAM
; @clobbers R16, R17, R22, R24, R25, X

com2wnRunIdle:
;rjmp com2wnRunIdle_end ; DEBUG
  push r15
    in r15, SREG
    cli
    ; look for outbound message
    rcall NET_Interface_PeekNextOutgoingMsgNum ; r16=msgNum
    brcs com2wnRunIdle_haveMsg
    out SREG, r15
  pop r15
  clc
  rjmp com2wnRunIdle_end

com2wnRunIdle_haveMsg:
    mov r24, r16
    ldi r16, COM2W_MODE_WRITING
    rcall com2wSetMode                         ; (R17)
    mov r16, r24
    out SREG, r15
  pop r15

  push r16
    rcall NET_Buffer_Locate                    ; (R17)
    adiw xh:xl, 1
    rcall com2wnSendMsg                        ; (R16, R17, R22, R24, R25, X)
    push r15                           
      in r15, SREG                             ; save SREG (no CLI, we want to save CFLAG only)
      ldi r16, COM2W_MODE_IDLE
      rcall com2wSetMode                       ; (R17)
      out SREG, r15                            ; restore SREG
    pop r15
  pop r16
  brcc com2wnRunIdle_end

  push r15
    in r15, SREG
    cli
    rcall NET_Interface_GetNextOutgoingMsgNum  ; take current msg off the queue
    rcall NET_Buffer_ReleaseByNum              ; (R16, X)
    out SREG, r15
  pop r15
  sec
com2wnRunIdle_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wnRunReading
;
; @param Y pointer to interface data in SRAM
; @clobbers none

com2wnRunReading:
  ; check for timeout (Y+NET_IFACE_OFFS_READTIMER)
  ldd r16, Y+NET_IFACE_OFFS_READTIMER
  cpi r16, COM2W_READING_MAXREADCOUNTER
  brcc com2wnRunReading_goIdle
  ldd r16, Y+COM2W_IFACE_OFFS_MODECOUNTER
  cpi r16, COM2W_READING_MAXMODECOUNTER
  brcc com2wnRunReading_goIdle
  clc
  rjmp com2wnRunReading_end
com2wnRunReading_goIdle:
  ldi r16, NET_IFACE_OFFS_ERR_IO_LOW
  rcall NET_Interface_IncCounter16     ; (R24, R25)
  ldi r16, COM2W_MODE_IDLE
  rcall com2wSetMode                   ; (r17)
  sec
com2wnRunReading_end:
  ret
; @end


; ---------------------------------------------------------------------------
; @routine com2wnRunSkipping
;
; @param Y pointer to interface data in SRAM
; @clobbers r16 (r17)

com2wnRunSkipping:
  ; check for timeout (Y+NET_IFACE_OFFS_READTIMER)
  ldd r16, Y+NET_IFACE_OFFS_READTIMER
  cpi r16, COM2W_SKIPPING_MAXREADCOUNTER
  brcc com2wnRunSkipping_goIdle
  ldd r16, Y+COM2W_IFACE_OFFS_MODECOUNTER
  cpi r16, COM2W_SKIPPING_MAXMODECOUNTER
  brcc com2wnRunSkipping_goIdle
  clc
  rjmp com2wnRunSkipping_end
com2wnRunSkipping_goIdle:
  ldi r16, COM2W_MODE_IDLE
  rcall com2wSetMode                   ; (r17)
  sec
com2wnRunSkipping_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wnRunWriting
;
; @param Y pointer to interface data in SRAM
; @clobbers none

com2wnRunWriting:
  ; TODO: check for timeout
  clc
  ret
; @end



#endif ; AVR_MODULES_COM2W_COM2WN_RUN_H


