WTFStopW : a simple stopwatch
x86-64
nasm
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

wtfstopw.asm 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. ; WTFStopW : a simple stopwatch
  2. ; Copyright (C) 2018 Weber Yann
  3. ;
  4. ; This program is free software; you can redistribute it and/or modify
  5. ; it under the terms of the GNU General Public License as published by
  6. ; the Free Software Foundation; either version 3 of the License, or
  7. ; any later version.
  8. ;
  9. ; This program is distributed in the hope that it will be useful,
  10. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ; GNU General Public License for more details.
  13. ;
  14. ; You should have received a copy of the GNU General Public License
  15. ; along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. ;
  17. ; A simple precise stopwatch
  18. ; Build : nasm -felf64 wtfstopw.asm && ld -s -melf_x86_64 wtfstopw.o -o wtfstopw
  19. ; Build Debug : nasm -g -F dwarf -felf64 -l wtfstopw.lst wtfstopw.asm && ld -melf_x86_64 wtfstopw.o -o wtfstopw
  20. ;
  21. ; ./wtfstopw [-h] [-r NDIGITS]
  22. ; Options :
  23. ; -h print this help and exit
  24. ; -r number of digits bellow seconds to display, default is 2
  25. ; Interact :
  26. ; press enter to exit
  27. ; send SIGINT (with kill -2 ir ctrl + c) for a new lap
  28. [bits 64]
  29. %define O_NONBLOCK 0x800
  30. STRUC TIMESPEC_STRUC
  31. .tv_sec: resq 1
  32. .tv_nsec: resq 1
  33. ENDSTRUC
  34. %macro TIMESPEC 1
  35. %1: ISTRUC TIMESPEC_STRUC
  36. at TIMESPEC_STRUC.tv_sec, resq 1
  37. at TIMESPEC_STRUC.tv_nsec, resq 1
  38. IEND
  39. %define %1.tv_sec %1+TIMESPEC_STRUC.tv_sec
  40. %define %1.tv_nsec %1+TIMESPEC_STRUC.tv_nsec
  41. %endmacro
  42. STRUC SIGACTION_STRUC
  43. .sa_handler: resq 1
  44. .sa_flags: resq 1
  45. .sa_restorer: resq 1
  46. .sa_mask: resb 128
  47. ENDSTRUC
  48. ; from https://www.linuxnasm.be/home/library/lib-includes/signals-inc
  49. %macro SIGACTION 1
  50. %1: ISTRUC SIGACTION_STRUC
  51. at SIGACTION_STRUC.sa_handler, dq 0
  52. at SIGACTION_STRUC.sa_flags, dq 0
  53. at SIGACTION_STRUC.sa_restorer, dq 0
  54. at SIGACTION_STRUC.sa_mask, times 128 db 0
  55. IEND
  56. %define sigaction.sa_handler sigaction+SIGACTION_STRUC.sa_handler
  57. %define sigaction.sa_flags sigaction+SIGACTION_STRUC.sa_flags
  58. %define sigaction.sa_restorer sigaction+SIGACTION_STRUC.sa_restorer
  59. %define sigaction.sa_mask sigaction+SIGACTION_STRUC.sa_mask
  60. %endmacro
  61. section .bss
  62. align 8
  63. TIMESPEC ts_start
  64. TIMESPEC ts_cur
  65. TIMESPEC ts_sleep
  66. buf: resb 1
  67. section .data
  68. time_res: dq 2 ; 2 digits bellow seconds can grow to 8
  69. fcntl_flag: dq 0
  70. SIGACTION sigaction
  71. align 8
  72. hours: times 8 db '0' ; allows storing max hours in 1<<64 secs
  73. timestr: db ":00:00.0 ", 0
  74. align 8
  75. timestrlen: equ $ - timestr
  76. hourslen: equ timestr - hours ; hours len -> the max number of digits
  77. lapsmsg: db "Lap : "
  78. lapsmsglen: equ $ - lapsmsg
  79. lapcount: times 20 db '0' ; allows storing decimal repr of 1<<64
  80. lapcountlen: equ $ - lapcount
  81. laplen: dq 2
  82. startmsg: db "Press Enter or ctrl+d to exit and ctrl+c for new lap."
  83. db 0xA
  84. startmsglen: equ $ - startmsg
  85. usage_pre: db "Usage : "
  86. usage_prelen: equ $ - usage_pre
  87. usage_post: db " [-h] [-r NDIGITS]", 0xA
  88. db "Options :", 0xA
  89. db 0x9, "-h print this help and exit", 0xA
  90. db 0x9, "-r Number of digits bellow seconds, between [1..8]"
  91. db 0xA
  92. db "Interactions :", 0xA
  93. db 0x9, "Press enter or close stdin to exit", 0xA
  94. db 0x9, "Send SIGINT (with kill -2 or ctrl + c) for a new lap"
  95. db 0xA
  96. db 0x9, "Elapsed time is sent on stderr and laps infos on stdout"
  97. db 0xA
  98. usage_postlen: equ $ - usage_post
  99. badarg_msg: db "Unexpected argument : "
  100. badarg_msglen: equ $ - badarg_msg
  101. badval_msg: db "Value for -r should be in [1..8] but got "
  102. badval_msglen: equ $ - badval_msg
  103. faultmsg: db "Fault !", 0xA
  104. faultmsglen: equ $ - faultmsg
  105. nl: db 0x0A
  106. cr: db 0xd
  107. section .text
  108. global _start
  109. _start:
  110. ; parse arguments and set time_res value
  111. jmp arg_parse
  112. arg_ok:
  113. ; set stdin reads non blocking
  114. xor rdx, rdx
  115. xor rdi, rdi
  116. mov rax, 72 ; fcntl
  117. mov rsi, 3 ; F_GETFL
  118. syscall
  119. mov [fcntl_flag], rax
  120. mov rdx, rax
  121. or rdx, O_NONBLOCK
  122. mov rax, 72 ; fcntl
  123. mov rsi, 4 ; F_SETFL
  124. syscall
  125. cmp rax, 0
  126. jne fault
  127. ; preparing SIGINT catch
  128. mov rax, proc_lap_handler
  129. mov qword [sigaction.sa_handler], rax
  130. mov eax, 0x14000000 ; SA_RESTART | SA_RESTORER
  131. mov dword [sigaction.sa_flags], eax
  132. mov rax, sig_restorer
  133. mov qword [sigaction.sa_restorer], rax
  134. mov rax, 13 ; sys_rt_sigaction
  135. mov rdi, 2 ; SIGINT
  136. mov rsi, sigaction
  137. mov rdx, 0 ; NULL
  138. mov r10, 8 ; sig_size
  139. syscall
  140. cmp rax, 0
  141. jne fault
  142. mov rax, 228 ; clock_gettime
  143. mov rdi, 0 ; CLOCK_REALTIME
  144. mov rsi, ts_start
  145. syscall
  146. mov rax, 1 ; write
  147. mov rdi, 2 ; stderr
  148. mov rsi, startmsg
  149. mov rdx, startmsglen
  150. syscall
  151. ; set value for ts_sleep.tv_nsec given time_res
  152. ; div sleep time by 10 for each digits added bellow seconds
  153. mov qword [ts_sleep.tv_sec], 0
  154. mov rax, 100000000
  155. mov r8, [time_res]
  156. mov r9, 10
  157. xor rdx, rdx
  158. setsleep_loop:
  159. cmp r8, 1
  160. jle setsleep_endloop
  161. div r9
  162. sub r8, 1
  163. jmp setsleep_loop
  164. setsleep_endloop:
  165. mov [ts_sleep.tv_nsec], rax
  166. std ; set DF for string operations
  167. main_loop:
  168. push 2 ; stderr
  169. push 0xD ; \r
  170. call proc_print_time
  171. ; Attempt to read from stdin
  172. ; if something read, enter has been pressed
  173. mov rax, 0
  174. mov rdi, 0
  175. mov rsi, buf
  176. mov rdx, 1
  177. syscall
  178. cmp rax, 0
  179. jge flush_stdin ; flush stdin and exit
  180. mov rax, 35 ; nanosleep
  181. mov rdi, ts_sleep
  182. mov rsi, 0
  183. syscall
  184. jmp main_loop ; main_loop
  185. flush_stdin:
  186. mov rax, 0
  187. mov rdi, 0
  188. mov rsi, buf
  189. mov rdx, 1
  190. syscall
  191. cmp rax, 0
  192. je newline_exit
  193. jg flush_stdin
  194. mov rdi, 0 ; EXIT OK
  195. ;
  196. ; Expect rdi to be the return code
  197. ;
  198. exit:
  199. push rdi
  200. ; restoring stdin state
  201. mov rax, 72 ; fcntl
  202. xor rdi, rdi
  203. mov rsi, 4 ; F_SETFL
  204. mov rdx, [fcntl_flag]
  205. syscall
  206. cmp rax, 0
  207. je exit_end
  208. pop rdi ; failed to restore
  209. push 1 ; exit FAIL
  210. exit_end:
  211. mov rax, 60 ; sys_exit
  212. pop rdi ; return code
  213. syscall
  214. fault:
  215. mov rdi, 2 ; stderr
  216. call proc_nl
  217. mov rax, 1
  218. mov rsi, faultmsg
  219. mov rdx, faultmsglen
  220. syscall
  221. mov rdi, 1 ; failure
  222. jmp exit
  223. newline_exit:
  224. mov rdi, 1
  225. call proc_nl
  226. mov rdi, 0 ; exit OK
  227. jmp exit
  228. ;
  229. ; Print current time on FD and add a leading char CHR
  230. ; push FD & push CHR to set arguments
  231. ; Warning : DF must be set
  232. ;
  233. proc_print_time:
  234. mov rax, 228 ; clock_gettime
  235. mov rdi, 0 ; CLOCK_REALTIME
  236. mov rsi, ts_cur
  237. syscall ; updating ts_cur time
  238. mov rax, [ts_cur.tv_nsec]
  239. sub rax, [ts_start.tv_nsec]
  240. cmp rax, 0 ; Calculating elapsed ns
  241. jge print_time_us_cont
  242. add rax, 1000000000 ; negativ result
  243. sub qword [ts_cur.tv_sec], 1
  244. print_time_us_cont:
  245. ; set the nanosec chars (time_res chars) in timestr
  246. xor rdx, rdx
  247. div qword [ts_sleep.tv_nsec] ; Divide result given time_res
  248. mov rdi, timestr + 6
  249. mov rsi, rdi ; rsi points on 1st nanosec digit
  250. add rdi, [time_res] ; rdi points on last nanosec digit
  251. mov r8, 10
  252. print_time_us_loop:
  253. xor rdx, rdx
  254. div r8
  255. xchg al, dl
  256. add al, 0x30
  257. stosb
  258. mov al, dl
  259. cmp rsi, rdi
  260. loopne print_time_us_loop
  261. ; filling timestr with seconds & minutes chars
  262. mov rax, [ts_cur.tv_sec]
  263. sub rax, [ts_start.tv_sec] ; rax now contain elapsed seconds
  264. mov r8, 10
  265. mov r9, 6
  266. xor rdx, rdx
  267. div r8
  268. add dl, 0x30
  269. mov byte [timestr + 5], dl
  270. xor dl, dl
  271. div r9
  272. add dl, 0x30
  273. mov byte [timestr + 4], dl
  274. xor dl, dl
  275. div r8
  276. add dl, 0x30
  277. mov byte [timestr + 2], dl
  278. xor dl, dl
  279. div r9
  280. add dl, 0x30
  281. mov byte[timestr + 1], dl
  282. ; filling the hours buffer
  283. ; rcx will contain max_digits - digits_count
  284. ; digits max is hourslen - 1
  285. mov rcx, hourslen - 1
  286. mov rdi, hours + hourslen - 1
  287. mov r10, 10
  288. procpt_loop:
  289. xor dl, dl
  290. div r10
  291. cmp rax, 0
  292. jne procpt_loopcont
  293. cmp dl, 0
  294. je procpt_loop_end
  295. procpt_loopcont:
  296. xchg al, dl
  297. add al, 0x30
  298. stosb
  299. mov al, dl
  300. cmp rcx, 0
  301. je fault
  302. cmp rax, 0
  303. loopne procpt_loop
  304. procpt_loop_end:
  305. ; set rcx for 2 digits at least for hours
  306. mov r9, hourslen - 2
  307. add rcx, 1
  308. cmp rcx, r9
  309. cmovnle rcx, r9
  310. mov r10, [rsp + 8] ; leading chr as 1st argument
  311. mov byte [timestr+timestrlen-1], r10b
  312. mov rax, 1 ; write
  313. mov rdi, [rsp + 16] ; fd as 2nd argument
  314. mov rdx, timestrlen + hourslen
  315. sub rdx, rcx ; timestr + hours len
  316. lea rsi, [hours + rcx] ; hours start pointer
  317. syscall
  318. ret 16
  319. ;
  320. ; sig handler for SIGINT displaying lap count and time on stdout
  321. ;
  322. proc_lap_handler:
  323. mov rax, 1
  324. mov rdi, 2
  325. mov rsi, cr
  326. mov rdx, 1
  327. syscall ; \r on stderr
  328. mov rax, 1
  329. mov rdi, 1
  330. mov rsi, lapsmsg
  331. mov rdx, 4 ; "Lap "
  332. syscall
  333. ; increment the lapcount str directly
  334. mov rbx, lapcount ; first digit ptr
  335. add rbx, lapcountlen ; rightmost digit ptr
  336. sub rbx, 1
  337. mov r8, 1 ; counter
  338. lap_handler_inc_lap:
  339. mov r10b, [rbx]
  340. cmp r10b, 0x39 ; '9'
  341. jl lap_handler_inc_end
  342. add r8, 1
  343. cmp r8, [laplen]
  344. jl lap_handler_laplen_noupd
  345. mov [laplen], r8 ; update laplen
  346. lap_handler_laplen_noupd:
  347. mov byte [rbx], 0x30 ; set current digit to '0'
  348. sub rbx, 1
  349. cmp rbx, lapcount
  350. jl fault
  351. jmp lap_handler_inc_lap
  352. lap_handler_inc_end:
  353. add r10b, 1
  354. mov [rbx], r10b
  355. mov rax, 1
  356. mov rdi, 1 ; stdout
  357. mov rdx, [laplen]
  358. mov rsi, lapcount
  359. add rsi, lapcountlen
  360. sub rsi, rdx ; leftmost digit ptr
  361. syscall
  362. mov rax, 1 ; write
  363. mov rsi, lapsmsg + 4
  364. mov rdx, 3 ; " : "
  365. syscall
  366. std ; set DF for string operations
  367. push 1 ; stdout
  368. push 0xA ; \n
  369. call proc_print_time
  370. ret
  371. sig_restorer:
  372. mov rax, 15 ; sys_rt_sigreturn
  373. xor rdi, rdi
  374. syscall
  375. ;
  376. ; Argument parsing
  377. ;
  378. arg_parse:
  379. ; Checking argument count
  380. mov rax, [rsp]
  381. cmp rax, 1
  382. je arg_ok
  383. cmp rax, 3
  384. jle parse_r
  385. arg_err: ; badval & badarg jmp here too
  386. mov rax, [rsp+8] ; argv[0] program name
  387. mov rdi, -1 ; return status
  388. jmp usage
  389. parse_r:
  390. mov rax, [rsp+16] ; 1st arg should be "-r"
  391. mov bl, [rax]
  392. cmp bl, '-'
  393. jne badarg
  394. mov bl, [rax+1]
  395. cmp bl, 'h' ; -h
  396. je arg_err
  397. cmp bl, 'r'
  398. jne badarg
  399. mov bl, [rax+2]
  400. cmp bl, 0
  401. jne arg_nxt ; the value seems to be just after the -r like -r2
  402. nxt_arg:
  403. ; the 1st argument is -r the second must be the time_res
  404. ; check that the arg exists
  405. mov rax, [rsp]
  406. cmp rax, 3
  407. jne arg_err
  408. mov rax, [rsp+24]
  409. jmp arg_cont
  410. arg_nxt:
  411. ; check that there is no more args
  412. mov rbx, [rsp]
  413. cmp rbx, 2
  414. jne arg_err
  415. add rax, 2
  416. arg_cont:
  417. ; rax should point on the value
  418. mov bl, [rax+1]
  419. cmp bl, 0
  420. jne badval
  421. xor rbx, rbx
  422. mov bl, [rax]
  423. cmp bl, '1'
  424. jl badval
  425. cmp bl, '8'
  426. jg badval
  427. sub bl, '0'
  428. mov [time_res], rbx
  429. jmp arg_ok
  430. ; print an error message, usage and exit with rax pointing the value
  431. badval:
  432. mov rsi, badval_msg
  433. mov rdx, badval_msglen
  434. jmp arg_strerr
  435. ; print an error message, usage and exit
  436. badarg:
  437. mov rsi, badarg_msg
  438. mov rdx, badarg_msglen
  439. mov rax, [rsp+16]
  440. jmp arg_strerr
  441. ; rsi msg ptr, rdx msg len, rax arg ptr
  442. arg_strerr:
  443. mov r8, rax
  444. mov rax, 1
  445. mov rdi, 1
  446. syscall
  447. mov rsi, r8
  448. mov rax, r8
  449. call proc_strlen
  450. mov rax, 1
  451. syscall
  452. call proc_nl
  453. call proc_nl
  454. jmp arg_err
  455. ;
  456. ; Print usage and exit
  457. ; Except rax to point on programm name and rdi to be the return code
  458. ;
  459. usage:
  460. push rdi
  461. push rax
  462. mov rax, 1 ; write
  463. mov rdi, 1 ; stdout
  464. mov rsi, usage_pre
  465. mov rdx, usage_prelen
  466. syscall
  467. pop rsi
  468. mov rax, rsi
  469. call proc_strlen
  470. mov rax, 1
  471. syscall
  472. mov rax, 1
  473. mov rsi, usage_post
  474. mov rdx, usage_postlen
  475. syscall
  476. mov rax, 60
  477. pop rdi
  478. syscall
  479. ; With rax pointing on the string, the result will be in rdx
  480. proc_strlen:
  481. mov r8, rax
  482. mov r9, r8
  483. sub r9, 1
  484. strlen_loop:
  485. add r9, 1
  486. mov al, [r9]
  487. cmp al, 0
  488. jne strlen_loop
  489. sub r9, r8
  490. mov rdx, r9
  491. ret
  492. ; with rdi the fd
  493. proc_nl:
  494. mov rax, 1
  495. mov rsi, nl
  496. mov rdx, 1
  497. syscall
  498. ret
  499. .end: