/*
 * RGǴpolis: la capital de RGG
 * Copyright (C) 2022  Iván Alejandro Ávalos Díaz <avalos@disroot.org>,
 *                     Fernando Daniel Hernández López <18030953>,
 *                     Uciel Eguiluz López <18031804>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

    /* Prólogo de función no hoja */
    .macro proc
    push    {r11, lr}
    add     r11, sp, #0
    sub     sp, sp, #16
    .endm

    /* Epílogo de función no hoja */
    .macro endp
    sub     sp, r11, #0
    pop     {r11, pc}
    .endm

    /* Sección de datos */
    .data
	
width: .word 128
height: .word 128
offsetx: .word 0
offsety: .word 0
r:	.byte 0
g:	.byte 0
b:	.byte 0

bmp: .word 0

    /* Sección de código */
    .text
    .global main
    
    /*
     * init_lcd
     */
init_lcd:
    proc

    /* lcdInit (width, height, offsetx, offsety) */
    ldr     r4, _width
    ldr     r0, [r4]
    ldr     r4, _height
    ldr     r1, [r4]
    ldr     r4, _offsetx
    ldr     r2, [r4]
    ldr     r4, _offsety
    ldr     r3, [r4]
    bl      lcdInit(PLT)

    bl      lcdReset(PLT)       // lcdReset ()
    bl      lcdSetup(PLT)       // lcdSetup ()

    /* lcdFillScreen (WHITE) */
    movw    r0, #0xFFFF
    bl      lcdFillScreen(PLT)

    endp

    /*
     * bitmap_open
     * r0 = filename
     */
bitmap_open:
    proc

    /* bmp = bopen (filename) */
    bl      bopen(PLT)
    ldr     r4, _bmp
    str     r0, [r4]

    endp

    /*
     * bitmap_close
     */
bitmap_close:
    proc

    /* bclose (bmp) */
    ldr     r4, _bmp
    ldr     r0, [r4]
    bl      bclose(PLT)

    endp

    /*
     * bitmap_display
     */
bitmap_display:
    proc

    /* Escribir datos de pixeles al framebuffer */
    /* Fuente: https://github.com/nopnop2002/Raspberry-ili9340spi/issues/22 */

    /* lcdWriteCommandByte (0x2A) */
    mov     r0, #0x2A
    bl      lcdWriteCommandByte(PLT)
    
    /* lcdWriteDataWord (0) */
    mov     r0, #0
    bl      lcdWriteDataWord(PLT)
    
    /* lcdWriteDataWord (width - 1) */
    ldr     r4, _width
    ldr     r4, [r4]
    sub     r0, r4, #1          // width - 1
    bl      lcdWriteDataWord(PLT)
    
    /* lcdWriteCommandByte (0x2B) */
    mov     r0, #0x2B
    bl      lcdWriteCommandByte(PLT)
    
    /* lcdWriteDataWord (0) */
    mov     r0, #0
    bl      lcdWriteDataWord(PLT)
    
    /* lcdWriteDataWord (height - 1) */
    ldr     r4, _height
    ldr     r4, [r4]
    sub     r0, r4, #1          // height - 1
    bl      lcdWriteDataWord(PLT)
    
    /* lcdWriteCommandByte (0x2C) */
    mov     r0, #0x2C
    bl      lcdWriteCommandByte(PLT)

    /* width */
    ldr     r8, _width
    ldr     r8, [r8]

    /* height */
    ldr     r9, _height
    ldr     r9, [r9]

    /* Bucle para y */
    mov     r6, #0              // y = 0

loopHeight:
    cmp     r6, r9              // y == height
    beq     loopHeightEnd

    /* Bucle para x */
    mov     r5, #0              // x = 0

loopWidth:
    cmp     r5, r8              // x == width
    beq     loopWidthEnd

    /* get_pixel_rgb (bmp, x, height - y, &r, &g, &b) */

    /* Parámetros por registro (0-3) */
    ldr     r4, _bmp
    ldr     r0, [r4]            // bmp
    mov     r1, r5              // x
    sub     r2, r8, r6          // height - y
    ldr     r3, _r              // &r

    /* Parámetros por pila (4-5) */
    ldr     r4, _g
    str     r4, [sp]            // &g
    ldr     r4, _b
    str     r4, [sp, #4]        // &b

    bl      get_pixel_rgb(PLT)

    /* Convertir RGB888 a RGB565 */
    /* Fuente: https://afterthoughtsoftware.com/posts/convert-rgb888-to-rgb565 */

    ldr     r4, _b
    ldr     r4, [r4]

    mov     r1, r4              // b
    lsr     r1, r1, #3          // b >> 3
    and     r1, r1, #0x1F       // (b >> 3) & 0x1f

    ldr     r4, _g
    ldr     r4, [r4]

    mov     r2, r4              // g
    lsr     r2, r2, #2          // g >> 2
    and     r2, r2, #0x3F       // (g >> 2) & 0x3f
    lsl     r2, r2, #5          // ((g >> 2) & 0x3f) << 5

    ldr     r4, _r
    ldr     r4, [r4]

    mov     r3, r4              // r
    lsr     r3, r3, #3          // r >> 3
    and     r3, r3, #0x1F       // (r >> 3) & 0x1f
    lsl     r3, r3, #11         // ((r >> 3) & 0x1f) << 11

    mov     r0, r1              // out = B
    orr     r0, r0, r2          // out |= G
    orr     r0, r0, r3          // out |= R

    bl      lcdWriteDataWord(PLT)

    add     r5, r5, #1
    b       loopWidth

loopWidthEnd:
    /* Fin del bucle para x */

    add     r6, r6, #1
    b       loopHeight

loopHeightEnd:
    /* Fin del bucle para y */

    endp

    /*
     * main
     * r0 = argc
     * r1 = argv
     */
main:
    proc

    push    {r1}                // argv
	
    bl      init_lcd            // init_lcd ()

    /* bitmap_open (argv[1]) */
    pop     {r0}                // argv
    ldr     r0, [r0, #4]        // argv[1]
    bl      bitmap_open
    
    bl      bitmap_display      // bitmap_display ()
    bl      bitmap_close        // bitmap_close ()

    /* return (0) */
    mov     r0, #0

    endp

/* Puentes para acceder a sección .data desde .text */
    
_width: .word width
_height:    .word height
_offsetx:   .word offsetx
_offsety:   .word offsety
_bmp:   .word bmp

_r: .word r
_g: .word g
_b: .word b
