1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
; 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
|