;name: palindrome.asm
;description: The program checks if a given string is a palindrome. If no string is passed on
;             the commandline a brief message is displayed on how to use the program.
;             The string can be any charactersequence and is case sensitive thus Aa is a
;             palindrome of aA but not of aa.
;build: nasm -felf64 palindrome.asm -o palindrome.o
;       ld -melf_x86_64 palindrome.o -o palindrome 
;usage: ./palindrome string1 string2 .... stringn

bits 64

%include "unistd.inc"

global _start

section .bss

section .data
    .start:     db  "Palindrome by Agguro.",10
                db  "usage: palindrome string1 string2 ...",10
    .length:    equ $-usage.start
    .is:        db  " is "
    .islength:  equ $-txt.is
    .no:        db  "not "
    .nolength:  equ $-txt.no
    .yes:       db  "a palindrome.",10
    .yeslength: equ $-txt.yes
section .text

    pop	    rcx                    ;argc in RCX
    cmp     rcx,2                  ;is there an argument?
    jl      .noArguments
    pop     rax                    ;pointer to command      
    dec     rcx                    ;argc - 1 because of command
    pop     rsi                    ;get pointer to string
    call    String.length          ;get length of string
    mov     rdx,rax                ;lenght in rdx
    call    String.write
    push    rsi
    mov	    rsi,txt.is
    mov	    rdx,txt.islength
    call    String.write
    pop	    rsi
    call    Palindrome.check
    jnc     .isPalindrome
    mov	    rsi,txt.no
    mov	    rdx,txt.nolength
    call    String.write

    mov     rsi,txt.yes
    mov     rdx,txt.yeslength
    call    String.write
    loop    .repeat
    jmp     Exit
    mov     rsi,usage
    mov     rdx,usage.length
    call    String.write
    jmp     Exit
    syscall exit, 0

    ;RSI has the pointer to a zero terminated string. If the string is a palindrome then the
    ;subroutine returns with the carryflag cleared, otherwise the carryflag is set.

    push    rsi
    push    rdi
    push    rax
    push    rcx  
    ;get length of stringz pointed by RSI
    call    String.length
    ;pointer to last character of string
    mov     rdi,rsi
    add     rdi,rax                 ;calculate pointer to last character in string
    dec     rdi
    ;calculate middle of string
    shr     rax,1                   ;divide rax by 2
    mov     rcx,rax                 ;integer part of division in rcx
    mov     al,byte[rsi]            ;read byte RCX from begin of string
    mov     ah,byte[rdi]            ;read byte RCX from end of string
    cmp     al,ah      
    jne     .noPalindrome
    inc     rsi                     ;move pointer to next byte position
    dec     rdi                     ;move pointer to previous byte position
    dec     rcx                     ;adjust counter
    jnz     .repeat
    clc                             ;string is palindrome
    jmp     .done
    pop     rcx
    pop     rax
    pop     rdi
    pop     rsi

    ; RSI has the pointer to a zero terminated string
    ; the length will be returned in RAX, all registers except RAX are restored
    push    rsi                     ;store used registers
    push    rcx     
    xor     rcx,rcx                 ;bytecounter
    lodsb                           ;byte in AL
    cmp     al,0                    ;if 0 then end of string
    je      .done
    inc     rcx                     ;else increment byte counter
    jmp     .repeat
    mov     rax,rcx                 ;string length in RAX
    pop     rcx                     ;restore used registers
    pop     rsi

    ; show a string pointed by RSI and length RDX on STDOUT
    ; all registers are restored, rcx will be destroyed after the syscall
    push    rcx
    syscall write, stdout
    pop     rcx