; ***************************************************************************
; 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 AQH_AVR_ILI9341_IMAGES_ASM
#define AQH_AVR_ILI9341_IMAGES_ASM



; ***************************************************************************
; data

.dseg

ili9341ImageColormap:
  .byte 8



; ***************************************************************************
; code

.cseg



; ---------------------------------------------------------------------------
; @routine ili9341ImageDraw

; @param  R1:R0   background color
; @param  R5:R4   X (dest)
; @param  R7:R6   Y (dest)
; @param Z pointer to image
; @clobbers any

ili9341ImageDraw:
  adiw zh:zl, RES_IMAGE_OFFS_TYPE_LO
  lpm r18, Z+
  lpm r19, Z
  sbiw zh:zl, RES_IMAGE_OFFS_TYPE_LO+1
  tst r19
  brne ili9341ImageDraw_ret
  cpi r18, DISPLAY_IMAGETYPE_IDX2
  breq ili9341ImageDraw_idx2
  cpi r18, DISPLAY_IMAGETYPE_IDX2RLE
  breq ili9341ImageDraw_idx2rle
  ; add more here
  rjmp ili9341ImageDraw_ret
ili9341ImageDraw_idx2:
  rcall ili9341ImageIdx2Draw              ; (any)
  rjmp ili9341ImageDraw_ret
ili9341ImageDraw_idx2rle:
  rcall ili9341ImageIdx2rleDraw
  rjmp ili9341ImageDraw_ret
  ; add more here
ili9341ImageDraw_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ili9341ImageIdx2Draw

; @param  R1:R0   background color
; @param  R5:R4   X (dest)
; @param  R7:R6   Y (dest)
; @param Z pointer to IDX2 image
; @clobbers any

ili9341ImageIdx2Draw:
  rcall ili9341ImageIdx2CopyColormap        ; (R16, X)

  ; read image width and height
  adiw zh:zl, RES_IMAGE_OFFS_WIDTH_LO
  lpm r8, Z+                                ; read width (lo)
  lpm r9, Z+
  lpm r10, Z+                               ; read height
  lpm r11, Z
  adiw zh:zl, (RES_IMAGE_OFFS_PIXELS_LO-RES_IMAGE_OFFS_HEIGHT_HI) ; skip 3 bytes
  lpm r16, Z+
  lpm zh, Z
  mov zl, r16
  or r16, zh
  breq ili9341ImageIdx2Draw_ret

  ; write image
  push r15
    in r15, SREG
    cli
    
    rcall ili9341BeginSpi                   ; (r16, r17)
    rcall ili9341SetAddressWindow           ; (R16, R20, R21)
  
    ldi r16, ILI9341_CMD_RAMWR              ; start writing ro RAM
    rcall ili9341SendCommand

    cbi SPIHW_SS_OUTPUT, SPIHW_SS_PIN       ; SS low
    sbi ILI9341_DC_OUTPUT, ILI9341_DC_PIN   ; D high (DATA)

    rcall ili9341ImageIdx2WritePixels       ; (R10, R16, R18, R19, R20, R21, R22, R23, R24, R25, X, Z)

    sbi SPIHW_SS_OUTPUT, SPIHW_SS_PIN       ; SS high
    rcall ili9341EndSpi                     ; (R16)
    out SREG, r15
  pop r15

ili9341ImageIdx2Draw_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ili9341ImageIdx2CopyColormap

; @param  R1:R0   background color
; @param Z pointer to IDX2 image
; @clobbers r16, X

ili9341ImageIdx2CopyColormap:
  ; get colormap
  ldi xl, LOW(ili9341ImageColormap)
  ldi xh, HIGH(ili9341ImageColormap)
  push zl
    push zh
      adiw zh:zl, RES_IMAGE_OFFS_COLORMAP_LO
      lpm r16, Z+
      lpm zh, Z
      mov zl, r16
      or r16, zh
      breq ili9341ImageIdx2CopyColormap_l1  ; no color map
      adiw zh:zl, 2                         ; skip numOfEntries
      rcall ili9341ImageReadColormap
ili9341ImageIdx2CopyColormap_l1:
    pop zh
  pop zl
  sts ili9341ImageColormap, r0
  sts ili9341ImageColormap+1, r1            ; store background color in pos 0 of colormap
  ret
; @end

  

; ---------------------------------------------------------------------------
; @routine ili9341ImageIdx2WritePixels

; @param  r9:r8   W
; @param  r11:r10 H
; @param  Z pointer to pixel data of IDX2 image
; @clobbers R10, R16, R17, R20, R21, R22, R23, R24, R25, X, Z

ili9341ImageIdx2WritePixels:
  clr r22
ili9341ImageIdx2WritePixels_vertLoop:
    mov r20, r8                                ; runWidth
ili9341ImageIdx2WritePixels_horizLoop:
      tst r22                                  ; pixelCounterInByte
      brne ili9341ImageIdx2WritePixels_l1
      lpm r23, Z+                              ; runByte
      ldi r22, 4                               ; 4 pixels per byte
ili9341ImageIdx2WritePixels_l1:
      clr r21                                  ; temp (receives current pixel)
      lsl r23                                  ; shift upper two bits into runtime var
      rol r21
      lsl r23
      rol r21
      lsl r21                                  ; 2 bytes per color
      ldi xl, LOW(ili9341ImageColormap)
      ldi xh, HIGH(ili9341ImageColormap)
      add xl, r21                              ; add to X -> pos in palette
      adc xh, r21
      sub xh, r21
      ld r17, X+
      ld r16, X
      ; send to SPI (high byte first)
;      rcall SPIHW_MasterTransfer               ; (R16)
      M_SPIHW_MASTER_SEND_BYTE r16
;      mov r16, r17
;      rcall SPIHW_MasterTransfer               ; (R16)
      M_SPIHW_MASTER_SEND_BYTE r17
      
      dec r22                                  ; pixelCounterInByte
      dec r20                                  ; runWidth
    brne ili9341ImageIdx2WritePixels_horizLoop
    dec r10                                    ; height
  brne ili9341ImageIdx2WritePixels_vertLoop
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ili9341ImageIdx2rleDraw

; @param  R1:R0   background color
; @param  R5:R4   X (dest)
; @param  R7:R6   Y (dest)
; @param Z pointer to IDX2 image
; @clobbers any

ili9341ImageIdx2rleDraw:
  rcall ili9341ImageIdx2CopyColormap        ; (R16, X)

  ; read image width and height
  adiw zh:zl, RES_IMAGE_OFFS_WIDTH_LO
  lpm r8, Z+                                ; read width (lo)
  lpm r9, Z+
  lpm r10, Z+                               ; read height
  lpm r11, Z
  adiw zh:zl, (RES_IMAGE_OFFS_PIXELS_LO-RES_IMAGE_OFFS_HEIGHT_HI) ; skip 3 bytes
  lpm r16, Z+
  lpm zh, Z
  mov zl, r16
  or r16, zh
  breq ili9341ImageIdx2rleDraw_ret

  ; write image
  push r15
    in r15, SREG
    cli
    
    rcall ili9341BeginSpi                   ; (r16, r17)
    rcall ili9341SetAddressWindow           ; (R16, R20, R21)
  
    ldi r16, ILI9341_CMD_RAMWR              ; start writing ro RAM
    rcall ili9341SendCommand

    cbi SPIHW_SS_OUTPUT, SPIHW_SS_PIN       ; SS low
    sbi ILI9341_DC_OUTPUT, ILI9341_DC_PIN   ; D high (DATA)

    rcall ili9341ImageIdx2rleWritePixels    ; (R10, R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, X, Z)

    sbi SPIHW_SS_OUTPUT, SPIHW_SS_PIN       ; SS high
    rcall ili9341EndSpi                     ; (R16)
    out SREG, r15
  pop r15

ili9341ImageIdx2rleDraw_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ili9341ImageIdx2rleWritePixels

; @param  r9:r8   W
; @param  r11:r10 H
; @param  Z pointer to pixel data of IDX2 image
; @clobbers R10, R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, X, Z

ili9341ImageIdx2rleWritePixels:
  clr r22                                      ; pixelCounterInByte
  clr r18                                      ; RLE control byte
  clr r19                                      ; RLE repeated byte
ili9341ImageIdx2rleWritePixels_vertLoop:
    mov r20, r8                                ; runWidth
ili9341ImageIdx2rleWritePixels_horizLoop:
      tst r22                                  ; pixelCounterInByte
      brne ili9341ImageIdx2rleWritePixels_l1

      rcall ili9341ImageIdx2rleGetNextByte     ; (R16)
      ldi r22, 4                               ; 4 pixels per byte
ili9341ImageIdx2rleWritePixels_l1:
      clr r21                                  ; temp (receives current pixel)
      lsl r23                                  ; shift upper two bits into runtime var
      rol r21
      lsl r23
      rol r21
      lsl r21                                  ; 2 bytes per color
      ldi xl, LOW(ili9341ImageColormap)
      ldi xh, HIGH(ili9341ImageColormap)
      add xl, r21                              ; add to X -> pos in palette
      adc xh, r21
      sub xh, r21
      ld r17, X+
      ld r16, X
      ; send to SPI (high byte first)
;      rcall SPIHW_MasterTransfer               ; (R16)
      M_SPIHW_MASTER_SEND_BYTE r16
;      mov r16, r17
;      rcall SPIHW_MasterTransfer               ; (R16)
      M_SPIHW_MASTER_SEND_BYTE r17
      
      dec r22                                  ; pixelCounterInByte
      dec r20                                  ; runWidth
    brne ili9341ImageIdx2rleWritePixels_horizLoop
    dec r10                                    ; height
  brne ili9341ImageIdx2rleWritePixels_vertLoop
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ili9341ImageIdx2rleGetNextByte
;
; @param r18 current controly byte (bit 7=1: repeat, bits 0-6: counter), updated on return
; @param r19 current repeat byte, updated on return
; @param Z current pixel data pointer
; @return r23 byte read
; @clobbers r16

ili9341ImageIdx2rleGetNextByte:
  ; get next byte somehow
  mov r16, r18
  andi r16, 0x7f
  brne ili9341ImageIdx2rleGetNextByte_haveBytes
  ; counter is zero, read next control byte
  lpm r18, Z+                                           ; read control byte
  sbrc r18, 7
    lpm r19, Z+                                         ; with repeat, read repeated byte
ili9341ImageIdx2rleGetNextByte_haveBytes:
  sbrc r18, 7                                           ; if bit 7=1:
    mov r23, r19                                        ;   copy repeated byte

  sbrs r18, 7                                           ; if bit 7=0:
    lpm r23, Z+                                         ;   read next byte (norepeat sequence)

  ; dec counter while keeping bit 7
  mov r16, r18
  andi r16, 0x7f                                  ; clear bit 7
  dec r16                                         ; dec counter
  andi r18, 0x80                                  ; clear counter part of CNTRL byte
  or r18, r16                                     ; or new counter into CNTRL byte
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ili9341ImageReadColormap

; @param X dest pointer for colormap in SDRAM (8 bytes, i.e. 4 colors by 2 bytes)
; @param Z src byte pointer of colormap (for LPM!)

ili9341ImageReadColormap:
  ldi r17, 8 ; 8 bytes
ili9341ImageReadColormap_loop:
    lpm r16, Z+
    st X+, r16
    dec r17
  brne ili9341ImageReadColormap_loop
  ret
; @end




#endif

