;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   64 bit Menuet text editor example
;
;   Compile with FASM 1.60 or above
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

use64

    org   0x0

    db    'MENUET64'              ; Header identifier
    dq    0x01                    ; Version
    dq    START                   ; Start of code
    dq    image_end               ; Size of image
    dq    0x100000                ; Memory for app
    dq    0xffff0                 ; Rsp
    dq    0x00                    ; Prm 
    dq    0x00                    ; Icon

lines equ 30

START:

    call  init

    call  draw_window    ; At first, draw the window

still:

    mov   rax , 10       ; Wait here for event
    int   0x60

    test  rax , 1        ; Window redraw
    jnz   window_event
    test  rax , 2        ; Keyboard press
    jnz   key_event
    test  rax , 4        ; Button press
    jnz   button_event

    jmp   still

window_event:

    call  draw_window
    jmp   still


key_event:

    mov   rax , 2  ; Read the key
    int   0x60

    cmp   rbx , 0  ; Characters
    je    processkey

    test  rbx , 1b ; Control keys
    jnz   still

    ;
    ; Enter
    ;
    cmp   ecx , 'Ente'
    jne   noenter
    ; Make room for next line
    mov   rsi , [texty]
    add   rsi , 1
    imul  rsi , 81
    add   rsi , text
    add   rsi , 100*81
    mov   rdi , rsi
    add   rdi , 81
    mov   rcx , 101*81
    std
    rep   movsb
    cld
    ; Save current cursor position
    mov   rsi , [texty]
    imul  rsi , 81
    add   rsi , [textx]
    add   rsi , text
    push  rsi
    ; Amount of characters to copy to next line
    mov   rcx , 79
    sub   rcx , [textx]
    push  rcx
    ; Clear next line
    add   [texty],dword 1
    mov   rdi , [texty]
    imul  rdi , 81
    add   rdi , text
    push  rdi
    mov   rcx , 81
    mov   rax , 0
    cld
    rep   stosb
    ; Copy to next line
    pop   rdi
    pop   rcx
    push  rcx
    cld
    rep   movsb
    ; Clear to end of line
    pop   rcx
    pop   rdi
    mov   rax , 0
    cld
    rep   stosb
    ; Align line beginning with above line
    mov   [textx],dword 0
  testnextpos:
    mov   rbx , [texty]
    sub   rbx , 1
    imul  rbx , 81
    add   rbx , [textx]
    add   rbx , text
    add   [textx],dword 1
    cmp   [textx],dword 79
    je    textx0
    cmp   [rbx],byte ' '
    jbe   testnextpos
    sub   [textx],dword 1
    call  draw_text_area
    jmp   still
  textx0:
    mov   [textx],dword 0
    call  draw_text_area
    jmp   still
  noenter:
    ;
    ; PageUp
    ;
    cmp   ecx , 'PgUp'
    jne   nopageup
    cmp   [texty],dword lines-1
    jae   tyfine
    mov   [texty],dword lines-1
  tyfine:
    cmp   [startline],dword lines-1
    jae   slfine
    mov   [startline],dword lines-1
  slfine:
    sub   [texty],dword lines-1
    sub   [startline],dword lines-1
    call  draw_text_area
    jmp   still
  nopageup:
    ;
    ; PageDown
    ;
    cmp   ecx , 'PgDo'
    jne   nopagedown
    add   [texty],dword lines-1
    add   [startline],dword lines-1
    call  draw_text_area
    jmp   still
  nopagedown:
    ;
    ; Home
    ;
    cmp   ecx , 'Home'
    jne   nohome
    mov   [textx],dword 0
    call  draw_text_area
    jmp   still
  nohome:
    ;
    ; End
    ;
    cmp   ecx , 'End '
    jne   noend
    mov   [textx],dword 79
  newends:
    sub   [textx],dword 1
    cmp   [textx],dword 0
    je    dta
    mov   rbx , [texty]
    imul  rbx , 81
    add   rbx , [textx]
    add   rbx , text
    cmp   [rbx-1],byte ' '
    jbe   newends
  dta:
    call  draw_text_area
    jmp   still
  noend:
    ;
    ; Backspace
    ;
    cmp   ecx , 'Back'
    jne   nobackspace
    cmp   [textx],dword 0
    je    notobeginning
    sub   [textx],dword 1
    jmp   commanddelete
  notobeginning:
    cmp   [texty],dword 0
    je    still
    sub   [texty],dword 1
    mov   rbx , [texty]
    imul  rbx , 81
    add   rbx , text+79
    mov   [textx],dword 79
  newlastc:
    sub   rbx , 1
    sub   [textx],dword 1
    cmp   [textx],dword 0
    je    commanddelete
    cmp   [rbx],byte ' '
    jbe   newlastc
    add   [textx],dword 1
    jmp   commanddelete
  nobackspace:
    ;
    ; Delete
    ;
    cmp   ecx , 'Dele'
    jne   nodelete
  commanddelete:
    ; Text after cursor
    mov   rbx , [texty]
    imul  rbx , 81
    add   rbx , [textx]
    add   rbx , text
    mov   rcx , [texty]
    imul  rcx , 81
    add   rcx , text+79
  newlettertest:
    cmp   [rbx],byte ' '
    ja    removedletter
    add   rbx , 1
    cmp   rbx , rcx
    jb    newlettertest
    ; Move line below to the end of current line
    mov   rdi , [texty]
    imul  rdi , 81
    add   rdi , [textx]
    add   rdi , text
    mov   rsi , [texty]
    add   rsi , 1
    imul  rsi , 81
    add   rsi , text
    mov   rcx , 79
    sub   rcx , [textx]
    cld
    rep   movsb
    ; Delete line below
    mov   rdi , [texty]
    add   rdi , 1
    imul  rdi , 81
    add   rdi , text
    mov   rsi , rdi
    add   rsi , 81
    mov   rcx , 81*100
    mov   rax , 0
    cld
    rep   movsb
    call  draw_text_area
    jmp   still
  removedletter:
    ; Delete letter at cursor position
    mov   rax , [textx]
    mov   rbx , [texty]
    imul  rbx , 81
    add   rbx , rax
    add   rbx , text
    mov   rsi , rbx
    mov   rdi , rsi
    add   rsi , 1
    mov   rcx , 80
    sub   rcx , [textx]
    cld
    rep   movsb
    call  draw_text_area
    jmp   still
  nodelete:
    ;
    ; Up Arrow
    ;
    cmp   ecx , 'Up-A'
    jne   noup
    cmp   [texty],dword 0
    je    still
    sub   [texty],dword 1
    call  draw_text_area
    jmp   still
  noup:
    ;
    ; Down Arrow
    ;
    cmp   ecx , 'Down'
    jne   nodown
    add   [texty],dword 1
    call  draw_text_area
    jmp   still
  nodown:
    ;
    ; Left Arrow
    ;
    cmp   ecx , 'Left'
    jne   noleft
    cmp   [textx],dword 0
    je    still
    sub   [textx],dword 1
    call  draw_text_area
    jmp   still
  noleft:
    ;
    ; Right Arrow
    ;
    cmp   ecx , 'Righ'
    jne   noright
    cmp   [textx],dword 79
    je    still
    add   [textx],dword 1
    call  draw_text_area
    jmp   still
  noright:
    jmp   still

  processkey:
    ;
    ; Add Character
    ;
    mov   rax , [textx]
    mov   rbx , [texty]
    imul  rbx , 81
    add   rbx , rax
    add   rbx , text
    push  rcx
    mov   rsi , rbx
    mov   rdi , rsi
    add   rdi , 1
    mov   rcx , 79
    sub   rcx , [textx]
    add   rsi , rcx
    add   rdi , rcx
    sub   rsi , 1
    sub   rdi , 1
    std
    rep   movsb
    cld
    pop   rcx
    mov   [rbx],cl
    mov   rdx , [texty]
    imul  rdx , 81
    add   rdx , text
  newspacec:
    cmp   [rdx],byte 0
    jne   nosetspace
    mov   [rdx],byte ' '
  nosetspace:
    add   rdx , 1
    cmp   rdx , rbx
    jb    newspacec
    add   [textx],dword 1
    cmp   [textx],dword 79
    jb    textxfine
    mov   [textx],dword 79
  textxfine:
    call  draw_text_area
    jmp   still


button_event:

    mov   rax , 17
    int   0x60

    ; rax = status
    ; rbx = button id

    cmp   rbx , 0x10000001
    jne   no_application_terminate_button
    mov   rax , 0x200
    int   0x60
  no_application_terminate_button:

    cmp   rbx , 0x106
    jne   no_application_terminate_menu
    mov   rax , 0x200
    int   0x60
  no_application_terminate_menu:

    jmp   still


draw_window:

    mov   rax , 12                      ; Beginning of window draw
    mov   rbx , 1
    int   0x60

    mov   rax , 0                       ; Draw window
    mov   rbx , 100 shl 32 + 6*80+16    ; X start & size
    mov   rcx , 50 shl 32 + 50+12*lines ; Y start & size
    mov   rdx , 0x0000000000FFFFFF      ; Type    & border color  
    mov   r8  , 0x0000000000000001      ; Flags (set as 1)
    mov   r9  , window_label            ; Zero or pointer to label
    mov   r10 , menu_struct             ; Zero or pointer to menu struct
    int   0x60

    mov   rdi , checksums               ; Clear checksums to draw all
    mov   rcx , 90
    mov   rax , -1
    cld
    rep   stosq
    call  draw_text_area                ; Draw the text area

    mov   rax , 12                      ; End of window draw
    mov   rbx , 2
    int   0x60

    ret


init:

    mov   rax , 141 ; Enable system font
    mov   rbx , 1
    mov   rcx , 1
    mov   rdx , 5 shl 32 + 5
    mov   r8  , 9 shl 32 + 12
    int   0x60

    mov   rdi , clear_start
    mov   rcx , clear_end - clear_start
    mov   rax , 0
    cld
    rep   stosb

    ret



draw_text_area:

    mov   rax , [texty]
    sub   rax , [startline]
    cmp   rax , lines-1
    jb    slf
    mov   rax , [texty]
    sub   rax , lines-1
    mov   [startline],rax
  slf:

    mov   rax , [texty]
    cmp   rax , [startline]
    jae   slf2
    mov   rax , [texty]
    mov   [startline],rax
  slf2:

    mov   rax , 4                            ; Display text
    mov   rbx , [startline]
    imul  rbx , 81
    add   rbx , text
    mov   rcx , 8                            ; X position
    mov   rdx , 42                           ; Y position
    mov   rsi , 0x000000                     ; Color
    mov   r9  , 1                            ; Font
    mov   r10 , 0 ; line
    mov   r14 , 0
  newline:
    call  checksum
    cmp   r15 , 0
    je    nocschange
    push  rax rbx rcx rdx                           
    mov   rax , 13
    mov   rbx , rcx
    mov   rcx , rdx
    mov   rdx , 0xffffff
    shl   rbx , 32
    shl   rcx , 32
    add   rbx , 80*6
    add   rcx , 11
    int   0x60
    pop   rdx rcx rbx rax
    int   0x60
  nocschange:
    add   rdx , 12
    add   rbx , 81
    add   r10 , 1
    cmp   r10 , lines
    jb    newline

    ;
    ; Cursor
    ;
    mov   rbx , [textxp]
    mov   rcx , [textyp]
    mov   r9  , 0xffffff
    call  draw_cursor
    mov   rbx , [textx]
    mov   rcx , [texty]
    sub   rcx , [startline]
    mov   r9  , 0x000000
    call  draw_cursor
    mov   rax , [textx]
    mov   [textxp],rax
    mov   rax , [texty]
    sub   rax , [startline]
    mov   [textyp],rax

    ret



draw_cursor:

    ; Cursor

    mov   rax , 38
    imul  rbx , 6
    sub   rbx , 1
    imul  rcx , 12
    add   rbx , 8
    add   rcx , 42-2
    mov   rdx , rbx
    mov   r8  , rcx
    add   r8  , 12
    int   0x60

    ret


checksum:

    push  rax rsi rdi

    mov   rsi , rbx
    mov   rdi , rsi
    add   rdi , 80
    mov   rax , 0
  newcs:
    add   rax , [rsi]
    add   rsi , 8
    cmp   rsi , rdi
    jb    newcs

    mov   r15 , 0
    cmp   [checksums+r10*8],rax
    je    nocsc
    mov   r15 , 1
    mov   [checksums+r10*8],rax
  nocsc:

    pop   rdi rsi rax
    ret



;
; Data area
;

window_label:

    db    'EXAMPLE',0      ; Window label

menu_struct:               ; Menu Struct

    dq   0                 ; Version
    dq   0x100             ; Start value of ID to return ( ID + Line )
                           ; Returned when menu closes and
                           ; user made no selections.
    db   0,'FILE',0        ; ID = 0x100 + 1
    db   1,'New',0         ; ID = 0x100 + 2
    db   1,'Open..',0      ; ID = 0x100 + 3
    db   1,'Save..',0      ; ID = 0x100 + 4
    db   1,'-',0           ; ID = 0x100 + 5
    db   1,'Quit',0        ; ID = 0x100 + 6
    db   0,'HELP',0        ; ID = 0x100 + 7
    db   1,'Contents..',0  ; ID = 0x100 + 8
    db   1,'About..',0     ; ID = 0x100 + 9
    db   255               ; End of Menu Struct


clear_start:

startline: dq  ?
textx:     dq  ?
texty:     dq  ?
textxp:    dq  ?
textyp:    dq  ?

checksums:  times  100          dq  ? ; checksums for line redraw
text:       times  81*lines*100 db  ? ;

clear_end:

image_end:

