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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
section .data
newline_char: db 0xA
digits: db '0123456789'
minus: db '-'
notation: dq 0xA
space_symbols: db 0x20, 0x9, 0xA
section .text
global exit
global string_length
global print_string
global print_char
global print_newline
global print_uint
global print_int
global string_equals
global read_char
global read_word
global read_line
global parse_uint
global parse_int
global string_copy
; Принимает код возврата и завершает текущий процесс
exit:
mov rax, 60
syscall
; Принимает указатель на нуль-терминированную строку, возвращает её длину
string_length:
xor rax, rax
.loop:
cmp byte [rdi+rax], 0
je .end
inc rax
jmp .loop
.end:
ret
; Принимает указатель на нуль-терминированную строку, выводит её в stdout
print_string:
push rdi
call string_length ; count str length (in rax)
pop rsi
mov rdx, rax
mov rax, 1
mov rdi, 1
syscall
ret
; Принимает указатель на нуль-терминированную строку, её длину и выводит её в stdout
print_string_with_lenght:
mov rdx, rsi
mov rsi, rdi
mov rax, 1
mov rdi, 1
syscall
ret
; Принимает код символа и выводит его в stdout
print_char:
push rdi
mov rsi, rsp
mov rax, 1
mov rdi, 1
mov rdx, 1
syscall
pop rdi
ret
; Переводит строку (выводит символ с кодом 0xA)
print_newline:
mov rdi, [newline_char]
jmp print_char
; Выводит беззнаковое 8-байтовое число в десятичном формате
; Совет: выделите место в стеке и храните там результаты деления
; Не забудьте перевести цифры в их ASCII коды.
print_uint:
mov rax, rdi
xor rcx, rcx ; обнуляем rcx, в нём будет храниться количество цифр в числе
.loop:
xor rdx, rdx ; обнуляем rdx, в нём будет храниться остаток от деления
inc rcx
div qword [notation]
dec rsp
mov dl, [digits + rdx]
mov [rsp], dl
cmp rax, 0
jnz .loop
; print string
mov rdi, rsp
mov rsi, rcx
add rsp, rcx ; возвращаем значение rsp
jmp print_string_with_lenght
; Выводит знаковое 8-байтовое число в десятичном формате
print_int:
cmp rdi, 0
jge .print
neg rdi
push rdi
mov rdi, [minus]
call print_char
pop rdi
.print:
call print_uint
ret
; Принимает два указателя на нуль-терминированные строки, возвращает 1 если они равны, 0 иначе
string_equals:
xor rax, rax ; counter
xor rdx, rdx
xor rcx, rcx
.loop:
mov dl, [rdi + rax]
mov cl, [rsi + rax]
cmp rdx, rcx
jne .error
cmp rdx, 0
je .success
inc rax
jmp .loop
.error:
xor rax, rax
ret
.success:
mov rax, 0x1
ret
; Читает один символ из stdin и возвращает его. Возвращает 0 если достигнут конец потока
read_char:
xor rax, rax
xor rdi, rdi
dec rsp
mov rsi, rsp
mov rdx, 1
syscall
test rax, rax
jz .EOF
mov al, [rsp]
jmp .END
.EOF:
xor rax, rax
.END:
inc rsp
ret
; Принимает: адрес начала буфера, размер буфера
; Читает в буфер слово из stdin, пропуская пробельные символы в начале, .
; Пробельные символы это пробел 0x20, табуляция 0x9 и перевод строки 0xA.
; Останавливается и возвращает 0 если слово слишком большое для буфера
; При успехе возвращает адрес буфера в rax, длину слова в rdx.
; При неудаче возвращает 0 в rax
; Эта функция должна дописывать к слову нуль-терминатор
read_word:
xor rdx, rdx
cmp rsi, 0
je .error
.loop:
push rdi
push rsi
push rdx
call read_char
pop rdx
pop rsi
pop rdi
cmp al, byte [space_symbols]
je .skip_or_end
cmp al, byte [space_symbols + 1]
je .skip_or_end
cmp al, byte [space_symbols + 2]
je .skip_or_end
jmp .save_char
.skip_or_end:
cmp rdx, 0
je .loop
jmp .success
.save_char:
cmp rsi, rdx
je .error
mov [rdi + rdx], al
cmp rax, 0
je .success
inc rdx
jmp .loop
.error:
xor rax, rax
xor rdx, rdx
ret
.success:
mov rax, rdi
ret
; Принимает: адрес начала буфера, размер буфера
; Читает в буфер строку из stdin, пропуская пробельные символы в начале.
; Строка заканчивается после символа перевода строки или при закрытии потока ввода
; Пробельные символы это пробел 0x20, табуляция 0x9 и перевод строки 0xA.
; Останавливается и возвращает 0 если слово слишком большое для буфера
; При успехе возвращает адрес буфера в rax, длину слова в rdx.
; При неудаче возвращает 0 в rax
; Эта функция дописывает к строке нуль-терминатор
read_line:
xor rdx, rdx
cmp rsi, 0
je .error
.loop:
push rdi
push rsi
push rdx
call read_char
pop rdx
pop rsi
pop rdi
cmp rdx, 0
jne .process_char
cmp al, byte [space_symbols]
je .loop
cmp al, byte [space_symbols + 1]
je .loop
cmp al, byte [space_symbols + 2]
je .loop
.process_char:
cmp rsi, rdx
je .error
cmp al, byte [newline_char]
jne .save
xor rax, rax
.save:
mov [rdi + rdx], al
cmp rax, 0
je .success
inc rdx
jmp .loop
.error:
xor rax, rax
xor rdx, rdx
ret
.success:
mov rax, rdi
ret
; Принимает указатель на строку, пытается
; прочитать из её начала беззнаковое число.
; Возвращает в rax: число, rdx : его длину в символах
; rdx = 0 если число прочитать не удалось
parse_uint:
xor rax, rax
push rcx
xor rcx, rcx
push rbx
xor rbx, rbx
.read:
mov bl, [rdi + rcx]
cmp bl, '0'
jb .end
cmp bl, '9'
ja .end
sub rbx, '0'
mul qword [notation]
add rax, rbx
inc rcx
jmp .read
.end:
mov rdx, rcx
pop rbx
pop rcx
ret
; Принимает указатель на строку, пытается
; прочитать из её начала знаковое число.
; Если есть знак, пробелы между ним и числом не разрешены.
; Возвращает в rax: число, rdx : его длину в символах (включая знак, если он был)
; rdx = 0 если число прочитать не удалось
parse_int:
xor rcx, rcx ; Boolean flag, 1 if number is negative, 0 otherwise
mov rax, [rdi] ; Check first symbol
cmp al, '+'
jne .check_neg
jmp .process
.check_neg:
cmp al, '-'
jne .just_uint
inc rcx
jmp .process
.just_uint:
jmp parse_uint
.process:
inc rdi ; Send string from next symbol
call parse_uint
cmp rdx, 0 ; If error, go to ret
je .end
inc rdx ; Else add sign to number lenght
cmp rcx, 0
je .end
neg rax ; For negative number
.end:
ret
; Принимает указатель на строку, указатель на буфер и длину буфера
; Копирует строку в буфер
; Возвращает длину строки если она умещается в буфер, иначе 0
string_copy:
xor rax, rax
.loop:
cmp rdx, rax
je .error
mov cl, [rdi + rax]
mov [rsi + rax], cl
inc rax
cmp cl, 0
je .end
jmp .loop
.error:
xor rax, rax
.end:
ret