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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
; Version : 2.11
; Last update : 30/7/2020
; Description : FizzBuzz, but in assembly. Left at a good tradeoff between speed and complexity.
; Licence : GPLv3
section .data
fizz db 'Fizz',0xA
fizzlen equ $-fizz
buzz db 'Buzz',0xA
buzzlen equ $-buzz
fizzbuzz db 'FizzBuzz',0xA
fizzbuzzlen equ $-fizzbuzz
section .text
global _start
_start:
xor r12,r12 ;counter register, set to count 0 to 100
xor r9,r9 ;r9 is used as a boolean to record if modulus 3 is achieved
jmp skip ;skip incrementing the first time round so 0 is kept
increment: inc r12 ;increment the counter
skip: cmp r12,101 ;check if 101 has been reached
;skip: cmp r12,1000001 ;check if 101 has been reached
je exit ; Exit if numbers 0 to 100 have been calculated
check3: imul r8d,r12d,0xaaaaaaab;calculate modulus 3 quickly by multiplying the counter by 0xaaaaaaab and truncuating the result into r8d.
cmp r8,0x55555555
ja check5 ;if r8 is larger than 0x55555555; (r12 % 3 !=0) and so modulus 5 is directly jumped to.
;falls through if (r12 % 3 == 0)
mov r9,1 ; set the fizz boolean to true
check5: imul r8d,r12d,0xcccccccd ;calculate modulus 5 quickly by multiplying the counter by 0xcccccccd and truncuating the result into r8d.
cmp r8,0x33333333
ja checkfizz ;if r8 is larger than 0x33333333; (r12 % 5 !=0). The state of fizz needs to be check to determine if anything needs printing.
;; fall through if (r12 % 5 == 0)
test r9,r9 ;r9 is the fizz boolean
je buzzonly ;if there is no fizz, only print buzz
;; fall through if both fizz and buzz are achieved; it's fizzbuzz time
printfizzbuzz: mov edi,1 ;file descriptor 1=stdout
mov rsi,fizzbuzz ;mov memory address of text to print into rsi.
mov rdx,fizzbuzzlen ;mov amount of bytes to write into rdx.
mov eax,1 ;1=sys_write syscall.
syscall ;do the syscall (sys_write in this case)
xor r9,r9 ;zero out the fizz boolean
jmp increment
checkfizz: test r9,r9 ;check if fizz is achieved
je printnum ;if not, print current number
;; fall through if fizz is achieved
fizzonly:
mov edi,1 ; File descriptor 1=stdout
mov rsi,fizz ;mov memory address of text to print into rsi.
mov rdx,fizzlen ;mov amount of bytes to write into rdx.
mov eax,1 ;1=sys_write syscall.
syscall ;do the syscall (sys_write in this case)
xor r9,r9 ;zero out the fizz boolean
jmp increment
buzzonly:
mov edi,1 ;file descriptor 1=stdout
mov rsi,buzz ;mov memory address of text to print into rsi.
mov rdx,buzzlen ;mov amount of bytes to write into rdx.
mov eax,1 ;1=sys_write syscall. 1 must be moved into rax after syscall is used, since rax is overwritten with the return value
syscall ;do the syscall (sys_write in this case)
xor r9,r9 ;zero out the fizz boolean
jmp increment
printnum: ALIGN 16 ;align the stack to 16 bytes.
mov ecx, 0xA ;base 10
dec rsp
mov [rsp],cl ;newline = 0xa = base
mov rsi,rsp ;move the stack pointer to the source register
;sub rsp,16 ;the red-zone is big enough on 64 bit linux to allow printing ~8 or even more digits without modifying the stack pointer. Change the LEA below from "[rsp+16+1]" to "lea edx, [rsp+1]" if removed.
;; rsi is pointing at '\n' on the stack, with 16B of "allocated" space below that. (if the stack pointer wasn't modified, the red-zone is written into instead)
mov rax,r12 ;rax = counter
mov r10,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,r10 ;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
;add rsp, 24 ;undo the push and the buffer reservation (32-bit: add esp,20)
jmp increment
exit:
mov eax, 60 ;code for sys_exit
xor edi, edi ;return value of 0
syscall ;do sys_exit
|