; hola.asm (FASM, Linux x86_64, usando libc)
;   fasm Floor_X11LCF01.asm Floor_X11LCF01.o
;   gcc -no-pie Floor_X11LCF01.o -o Floor_X11LCF01 -lX11
;   ./Floor_X11LCF01

format ELF64

public main
extrn XOpenDisplay
extrn XDefaultScreen
extrn XRootWindow
extrn XBlackPixel
extrn XWhitePixel
extrn XCreateSimpleWindow
extrn XSelectInput
extrn XStoreName
extrn XMapWindow
extrn XNextEvent
extrn XCloseDisplay
extrn exit
extrn XPending
extrn XCreateGC
extrn XDefaultVisual
extrn XCreateImage
extrn XPutImage
extrn XFlush
extrn usleep
extrn XDestroyImage
extrn XFreeGC
extrn XDestroyWindow
extrn XSetWMNormalHints

cdXSize        equ  640
cdYSize        equ  400
ZPixMap        equ  2
XIMAGE_DATA    equ  16
KEYCODE_OFFSET equ  84
ESC_KEYCODE    equ  9
XSH_FLAGS      equ  0
XSH_MIN_WIDTH  equ  24
XSH_MIN_HEIGTH equ  28
XSH_MAX_WIDTH  equ  32
XSH_MAX_HEIGHT equ  36

section '.bss' writeable
  wnd           rq   1
  dsp           rq   1
  vnt           rq   1
  scrn          rq   1
  rtw           rq   1
  gc            rq   1
  dv            rq   1
  img           rq   1
  pxl           rq   1
  vbMotion      rb   1
  event_buffer  rb   256
  size_hints    rb   64
  imgbf         rq   cdXSize * cdYSize

section '.data' writeable
    window_title    db  "caballero - Floor demo", 0

section '.text' executable

PintaObjeto:
  push    rbx
  push    rcx
  push    rdx
  push    rsi
  push    rdi
  
  mov     rdi, [pxl]
  movzx   rcx, byte [vbMotion]
  mov     rdx, cdYSize - 1
  .Filas:
    mov     rbx, cdXSize - 1
    .Columnas:
      mov     rsi, rdx
      add     rsi, rcx
      mov     rax, rbx
      sub     rax, rcx
      xor     rax, rsi
      and     rax, 0xFF
      mov     ah, al
      stosd
      dec     rbx
    jns     .Columnas
    dec     rdx
  jns     .Filas
  inc     byte [vbMotion]
  
  pop     rdi
  pop     rsi
  pop     rdx
  pop     rcx
  pop     rbx
ret

main:
  push     rbp
  mov      rbp, rsp
  
  ; 1. Display  *dsp = XOpenDisplay(NULL);
  xor      rdi, rdi
  call     XOpenDisplay
  test     rax, rax
  jz       .error
  mov      [dsp], rax
  
  ; 2. int scrn = DefaultScreen(dsp);
  mov      rdi, [dsp]
  call     XDefaultScreen
  mov      [scrn], rax
  
  ; 3. RootWindow (dsp, scrn)
  mov      rdi, [dsp]
  mov      rsi, [scrn]
  call     XRootWindow
  mov      [rtw], rax

  ; 4. wnd = XCreateSimpleWindow(dsp, rtw, 0, 0, cdXSize, cdYSize, 0, 0, 0)
  mov      rdi, [dsp]
  mov      rsi, [rtw]
  mov      rdx, 0
  mov      rcx, 0
  mov      r8, cdXSize
  mov      r9, cdYSize
  push     0              ; XWhitePixel
  push     0              ; XBlackPixel
  push     0              ; border width
  call     XCreateSimpleWindow
  add      rsp, 8*3       ; Limpiamos los 3 push
  mov      [wnd], rax
  
  ; Evitamos redimensionamiento de ventana
  mov      qword [size_hints + XSH_FLAGS], 48   ; PMinSize | PMaxSize
  mov      dword [size_hints + XSH_MIN_WIDTH], cdXSize     ; min size
  mov      dword [size_hints + XSH_MIN_HEIGTH], cdYSize
  mov      dword [size_hints + XSH_MAX_WIDTH], cdXSize     ; max size
  mov      dword [size_hints + XSH_MAX_HEIGHT], cdYSize
  mov      rdi, [dsp]
  mov      rsi, [wnd]
  lea      rdx, [size_hints]
  call     XSetWMNormalHints

  ; 5. XSelectInput (dsp, wnd, ExposureMask | KeyPressMask);
  mov      rdi, [dsp]
  mov      rsi, [wnd]
  mov      rdx, 33             ; KeyPressMask | ExposureMask
  call     XSelectInput

  mov      rdi, [dsp]
  mov      rsi, [wnd]
  mov      rdx, window_title
  call     XStoreName

  mov      rdi, [dsp]
  mov      rsi, [wnd]
  call     XMapWindow
  
  mov      rdi, [dsp]
  mov      rsi, [wnd]
  mov      rdx, 0
  mov      rcx, 0
  call     XCreateGC
  mov      [gc], rax
  
  mov      rdi, [dsp]
  mov      rsi, [scrn]
  call     XDefaultVisual
  mov      [dv], rax
  
  mov      rdi, [dsp]
  mov      rsi, [dv]
  mov      rdx, 24
  mov      rcx, ZPixMap
  mov      r8, 0
  mov      r9, imgbf
  push     0
  push     32
  push     cdYSize
  push     cdXSize
  call     XCreateImage
  add      rsp, 8*4       ; Limpiamos los 4 push
  mov      [img], rax
  ;add      rax, XIMAGE_DATA
  mov      rax, [rax + XIMAGE_DATA] 
  mov      [pxl], rax    ; Aquí tenemos img->data

  .event_loop:
    mov      rdi, [dsp]
    call     XPending
    cmp      rax, 0
    je       .Marcha
    mov      rdi, [dsp]
    mov      rsi, event_buffer
    call     XNextEvent
    cmp      dword [event_buffer], 2     ; 2 = KeyPress

    jnz      .Marcha           ; No hemos pulsado ninguna tecla
    cmp      dword [event_buffer + KEYCODE_OFFSET], ESC_KEYCODE
    jz       .Fin_Prog
    
    ; Aquí empezamos la marcha
    .Marcha:
    call     PintaObjeto
    
    mov      rdi, [dsp]
    mov      rsi, [wnd]
    mov      rdx, [gc]
    mov      rcx, [img]
    mov      r8, 0
    mov      r9, 0
    push     cdYSize
    push     cdXSize
    push     0
    push     0
    call     XPutImage
    add      rsp, 8*4       ; Limpiamos los 4 push
    
    mov      rdi, [dsp]
    call     XFlush
    
    mov      rdi, 16000
    call     usleep    
  jmp     .event_loop
  
  ; Salida limpia
  .Fin_Prog:
  mov      rdi, [img]
  call     XDestroyImage
  
  mov      rdi, [dsp]
  mov      rsi, [gc]
  call     XFreeGC
  
  mov      rdi, [dsp]
  mov      rsi, [wnd]
  call     XDestroyWindow
  
  mov      rdi, [dsp]
  call     XCloseDisplay
  
  pop      rbp
  xor      rdi, rdi
  call     exit

  .error:
    pop      rbp
    mov      rdi, 1
    call     exit
      
