-
Notifications
You must be signed in to change notification settings - Fork 0
/
ttyper.asm
446 lines (372 loc) · 9.09 KB
/
ttyper.asm
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
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
format ELF64
public _start
extrn initscr
extrn noecho
extrn endwin
extrn getch
extrn printw
extrn mvprintw
extrn move
extrn refresh
extrn use_default_colors
extrn start_color
extrn init_pair
extrn wattr_on
extrn wattr_off
extrn stdscr
extrn curs_set
; SYS_CALLS
define SYS_EXIT 0x3C
define SYS_TIME 0xC9
; Keys
define KEY_ENTER 0x0A
define KEY_ESC 0x1B
define KEY_SPACE 0x20
define KEY_DEL 0x7E
define KEY_BACKSPACE 0x7F
; Default colors
define COLOR_DEFAULT 0xFFFF
define COLOR_BLACK 0x00
define COLOR_RED 0x01
define COLOR_GREEN 0x02
; Color index
define CORRECT_COLOR 0x01
define WRONG_COLOR 0x02
define SPACE_WRONG_COLOR 0x03
define MIN_TERM 70
;
; Data
;
section '.data' writable
ctype db '%c', 0
stype db '%s', 0
lutype db '%lu', 0
stattype db 'Time: %lus - %lu mistakes - %.2f cpm / %.2f wpm - Accuracity: %.2f%%', 0
sstattype db '%.2f cpm / %.2f wpm', 0
default_text db 'A large rose-tree stood near the entrance of the garden: the roses growing on it were white, but there were three gardeners at it, busily painting them red.', 0 ; Text the user has to type
default_text_size = $-default_text ; Text length
user_mistakes dd 0 ; Number of total mistyped letters
user_input dd 0 ; The key that user has pressed
user_char_writen dd 0 ; Count of user written characters (considered per line)
user_time_start_typing dq 0 ; When the user started typing the first character
; Terminal size
termx dw ?
termy dw ?
;
; Macros
;
macro INIT_NCURSES {
; Initialize the screen
call initscr
; Get terminal size
mov bx, word [rax+4] ; y
mov cx, word [rax+6] ; x
mov [termy], bx
mov [termx], cx
call noecho
; Initialize color
call use_default_colors
call start_color
INIT_PAIR CORRECT_COLOR, COLOR_DEFAULT, COLOR_GREEN
INIT_PAIR WRONG_COLOR, COLOR_DEFAULT, COLOR_RED
INIT_PAIR SPACE_WRONG_COLOR, COLOR_RED, COLOR_DEFAULT
}
macro CLOSE_NCURSES {
call endwin
}
macro INIT_PAIR pair*, fg*, bg* {
mov rdi, pair
mov rsi, bg
mov rdx, fg
call init_pair
}
macro PRINT_CHAR chr {
mov rdi, ctype
mov esi, chr
call printw
}
macro COLOR_PAIR pair* {
shl pair, 8
}
macro COLOR_ON pair* {
mov rdi, [stdscr]
mov rsi, pair
shl rsi, 8
xor rdx, rdx
call wattr_on
}
macro COLOR_OFF pair* {
mov rdi, [stdscr]
mov rsi, pair
shl rsi, 8
xor rdx, rdx
call wattr_off
}
macro PRINT_TEXT_AT y*, x*, text* {
mov rdi, y
mov rsi, x
mov rdx, stype
mov rcx, text
call mvprintw
}
macro PRINT_CENTERED_TEXT text* {
mov rdi, default_text_size ; get the center position of the text
mov rsi, text
call _get_center_position
mov rdx, stype
mov rcx, default_text
call mvprintw
; PRINT_TEXT_AT rdi, rsi, default_text ; print the text
}
macro MOVE_CURSOR_TEXT_BEGIN {
mov rdi, default_text_size
call _get_center_position
call move
}
macro MOVE_CURSOR_TEXT_USER_CURRENT {
mov rdi, default_text_size
call _get_center_position
add esi, [user_char_writen]
call move
}
macro CURSES_HIDE_CURSOR {
xor rdi, rdi
call curs_set
}
macro CURSES_SHOW_CURSOR {
xor rdi, rdi
inc rdi
call curs_set
}
;
; Code
;
section '.text' executable
_start:
INIT_NCURSES
; Print the text and move the cursor to the beginning
PRINT_CENTERED_TEXT default_text
MOVE_CURSOR_TEXT_BEGIN
; Handle user input
_while_loop:
; Test for the end of a string
xor rax, rax
mov eax, [user_char_writen]
mov rbx, default_text_size
dec rbx ; remove the null terminator
cmp eax, ebx
jge _text_typed
; Get time when the user starts typing
call getch
mov [user_input], eax
cmp [user_char_writen], 0
jne _pass
call _set_user_type_time_start
_pass:
; If ESC, exit
cmp [user_input], KEY_ESC
je _exit
; Remove a character at backspace
cmp [user_input], KEY_BACKSPACE
jne _backspace_pass
; Don't decrement if below 0
cmp [user_char_writen], 0
jle _user_len_neg_pass
dec [user_char_writen]
_user_len_neg_pass:
; Move the cursor at the correct position
MOVE_CURSOR_TEXT_USER_CURRENT
; Print back the character from the text
lea rsi, [default_text]
movzx rdi, word [user_char_writen]
call _get_char_at_offset
; and rdi, 0xFF
PRINT_CHAR esi
; Move the cursor at the correct position
MOVE_CURSOR_TEXT_USER_CURRENT
jmp _while_loop
_backspace_pass:
; Check if key is printable
; Try again if it's not
cmp [user_input], KEY_SPACE
jl _while_loop
cmp [user_input], KEY_DEL
jge _while_loop
; Print single character
; Check if the character is correct or not
xor eax, eax
mov eax, [user_input]
lea rdi, [default_text]
movzx rsi, word [user_char_writen]
call _get_char_at_offset
cmp rsi, rax
jne _char_typed_wrong
; the character is correctly typed
COLOR_ON CORRECT_COLOR
PRINT_CHAR [user_input]
COLOR_OFF CORRECT_COLOR
jmp _user_input_wrapup
; The character is not correctly typed
_char_typed_wrong:
inc [user_mistakes]
cmp rax, KEY_SPACE
je _wrong_input_space_skip
; Wrong character typed
COLOR_ON WRONG_COLOR
PRINT_CHAR [user_input]
COLOR_OFF WRONG_COLOR
jmp _user_input_wrapup
_wrong_input_space_skip:
; Wrong typed space, print the character instead of the space
COLOR_ON SPACE_WRONG_COLOR
mov rsi, default_text
mov edi, [user_char_writen]
call _get_char_at_offset
PRINT_CHAR esi
COLOR_OFF SPACE_WRONG_COLOR
_user_input_wrapup:
inc [user_char_writen]
jmp _while_loop
; Print the time
_text_typed:
; Get the end time
xor rdi, rdi
mov rax, SYS_TIME
syscall
; Calculate the difference
mov rbx, [user_time_start_typing]
sub rax, rbx
; When the terminal is less than 70 character wide
; The stats are printed in a more concise format
cmp [termx], MIN_TERM
jg _minify_skip
mov di, [termy]
xor rsi, rsi
mov rdx, sstattype
; Calculate the cpm
cvtsi2sd xmm1, rax
mov rax, default_text_size
imul rax, 60 ; size * 60
cvtsi2sd xmm0, rax ; size
divsd xmm0, xmm1 ; cpm = size * 60 / time
; Calculate the wpm
; wpm = cpm / 5
movsd xmm1, xmm0
mov rax, 5 ; 5 characters per word
cvtsi2sd xmm2, rax
divsd xmm1, xmm2
mov rax, 2
call mvprintw
jmp _stat_wrapup
_minify_skip:
; Print the time
mov di, [termy] ; y
xor rsi, rsi ; x
mov rdx, stattype ; template
mov rcx, rax ; seconds
mov r8d, [user_mistakes] ; mistakes
; Calculate the cpm
cvtsi2sd xmm1, rax
mov rax, default_text_size
imul rax, 60 ; size * 60
cvtsi2sd xmm0, rax ; size
divsd xmm0, xmm1 ; cpm = size * 60 / time
; Calculate the wpm
; wpm = cpm / 5
movsd xmm1, xmm0
mov rax, 5 ; 5 characters per word
cvtsi2sd xmm2, rax
divsd xmm1, xmm2
; Calculate the accuracity percentage
xor rbx, rbx ; (size - mistakes) / size * 100
mov rax, default_text_size
dec rax ; minus null terminator
mov ebx, [user_mistakes]
sub rax, rbx ; (size - mistakes)
imul rax, 100 ; ^^^^^^^^^^^^^^^^^ * 100
cvtsi2sd xmm2, rax
mov rax, default_text_size
dec rax ; minus null terminator
cvtsi2sd xmm3, rax
divsd xmm2, xmm3 ; (mistakes * 100) / size
mov rax, 3 ; number of xmm args
_stat_wrapup:
; Print the stats
call mvprintw
CURSES_HIDE_CURSOR
call getch
_exit:
CLOSE_NCURSES
; Exit successfully
xor rdi, rdi
mov rax, SYS_EXIT
syscall
;
; Input:
; rdi - text len
; Output:
; rdi - y
; rsi - x
;
_get_center_position:
; Set x = (termy - text_len)/2
movzx rsi, word [termx]
sub rsi, rdi
shr rsi, 1
; Set y = termx / 2
movzx rdi, word [termy]
shr rdi, 1
ret
;
; Input:
; rdi - string address
; rsi - offset
; Output:
; rsi - character at offset
;
_get_char_at_offset:
add rdi, rsi
movzx rsi, byte [rdi]
ret
;
; Sets the current time into user_time_start_typing
;
; Input: none
; Output: none
;
_set_user_type_time_start:
push rax
xor rdi, rdi
mov rax, SYS_TIME
syscall
mov [user_time_start_typing], rax
pop rax
ret
;
; Returns how many lines should the text be devided into
; given the text length and the max size of one chunk
;
; It's a rough approximation of the line count but never will be less than
; the actual line count
;
; Is supposed to be used when allocating memory for an array to get the
; array size
;
; Input:
; rdi - text length
; rsi - chunk length (terminal width)
;
; Output:
; rdi - number of lines
;
_get_text_max_line_count:
push rax
push rdx
shr rsi, 1
mov rax, rdi
xor rdx, rdx
idiv rsi
mov rdi, rax
pop rdx
pop rax
ret