summaryrefslogtreecommitdiffstats
path: root/count.asm
blob: 375da4a450adec6bc38c197a1286bc714e27ed3b (plain)
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