; Version : 2.2 ; Updated : 25/5/2020 ; Copyright (C) : 2020 Gentoo-libre Install ; License : GPLv3+ ; Description : Simple character counter program. ; Counts the amount of 'a' characters recieved in stdin until EOF is reached. Single byte reads are used for simplicity. ; Input EOF to get your result. ;; using static memory should be faster than the stack, and it's only one byte... section .bss Buff resb 1 ; A single byte for the ASCII interpretation of the number read section .text global _start _start: xor r12, r12 ; Set r12 to 0. r12 is used to count the number of characters detected mov edx,1 ;read 1 byte mov rsi,Buff ;Address of buffer to read into xor rdi,rdi ;stdin Read: xor rax, rax ;specify sys_read (0) syscall ;sys_read test eax,eax ; Check if the return value was 0 je printnum ; Jump to printing the total if read value was 0 (EOF) cmp byte[Buff],'a' ;Test input char against 0x61 jne Read ;If not equal, 'a' was not read, so jump back to reading the next character inc r12 ;Increment r12 since an 'a' was read. jmp Read ;Read the next char printnum: ALIGN 16 ;Align the stack to 16 bytes ;;even pushing a whole 8 bytes to stack appears to be faster than copying 1 byte to the stack (check?) mov ecx,0xA ;base 10 push rcx ;newline = 0xA = base mov rsi,rsp ;move the stack pointer to the source register ;; rsi is pointing at '\n' on the stack. Below that is 128 bytes of red zone. mov rax,r12 ;rax = counter mov rbx,0xcccccccd ;used in division by 10 below ;; ecx=remainder = low digit = 0..9. eax/=10 toascii_digit: mov ecx,eax ;copy eax to ecx for later usage in modulus. imul rax,rbx ;sign multiply rax by 0xcccccccd to complete the first step of division by 10 shr rax,0x23 ;shift rax right by 35 to finish off rax/=10 mov edx,eax ;move eax to edx so modulus can be done on it without clobbering eax lea edx,[rdx+rdx*4] ;multiply edx by 5 as the first step of modulus add edx,edx ;multiply edx by 2 as the second step of modulus sub ecx,edx ;subtract edx from the original number (before /=10) to finish off modulus 10 ;; the remainder(ecx) is the next calculated digit add ecx,'0' ;add 48 to ecx to turn it into the ASCII representation of the number dec rsi ;store digits in MSD-first printing order, working backwards from the end of the string mov [rsi],cl ;shove cl(lower 8 bits in rcx) onto the stack test eax,eax jnz toascii_digit ;if eax does not equal zero, there are more ASCII chars to generate and shove onto the stack ;; rsi points to the first digit mov eax,1 ;sys_write mov edi,1 ;fd = STDOUT_FILENO lea edx,[rsp+1] ;yes, it's safe to truncate pointers before subtracting to find length. sub edx,esi ;calculate length, including the \n syscall ;print the chars on the stack Exit: mov eax,60 ;code for sys_exit xor edi,edi ;Return value of 0 syscall ;Do sys_exit