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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  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, dq 0
  37. at TIMESPEC_STRUC.tv_nsec, dq 0
  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. buf: resb 1
  64. section .data
  65. align 8
  66. TIMESPEC ts_start
  67. TIMESPEC ts_cur
  68. TIMESPEC ts_sleep
  69. hours: times 8 db '0' ; 8 digits is the max for hours
  70. timestr: db ":00:00."
  71. times 8 db '0' ; 8 digits for nanoseconds
  72. times 0xE db ' '
  73. db 0
  74. timestrend: align 8
  75. timestrlen: equ timestrend - timestr
  76. %define HOURSLEN 8
  77. time_res: dq 2 ; 2 digits bellow seconds can grow to 8
  78. fcntl_flag: dq 0
  79. SIGACTION sigaction
  80. lapsmsg: db "Lap : "
  81. lapsmsglen: equ $ - lapsmsg
  82. lapcount: times 20 db '0' ; allows storing decimal repr of 1<<64
  83. lapcountlen: equ $ - lapcount
  84. laplen: dq 2
  85. startmsg: db "Press Enter or ctrl+d to exit and ctrl+c for new lap."
  86. db 0xA
  87. startmsglen: equ $ - startmsg
  88. usage_pre: db "Usage : "
  89. usage_prelen: equ $ - usage_pre
  90. usage_post: db " [-h] [-r NDIGITS]", 0xA
  91. db "Options :", 0xA
  92. db 0x9, "-h print this help and exit", 0xA
  93. db 0x9, "-r Number of digits bellow seconds, between [1..8]"
  94. db 0xA
  95. db "Interactions :", 0xA
  96. db 0x9, "Press enter or close stdin to exit", 0xA
  97. db 0x9, "Send SIGINT (with kill -2 or ctrl + c) for a new lap"
  98. db 0xA
  99. db 0x9, "Elapsed time is sent on stderr and laps infos on stdout"
  100. db 0xA
  101. usage_postlen: equ $ - usage_post
  102. badarg_msg: db "Unexpected argument : "
  103. badarg_msglen: equ $ - badarg_msg
  104. badval_msg: db "Value for -r should be in [1..8] but got "
  105. badval_msglen: equ $ - badval_msg
  106. faultmsg: db "Fault !", 0xA
  107. faultmsglen: equ $ - faultmsg
  108. nl: db 0x0A
  109. cr: db 0xd
  110. section .text
  111. global _start
  112. _start:
  113. ; parse arguments and set time_res value
  114. jmp arg_parse
  115. arg_ok:
  116. ; set stdin reads non blocking
  117. xor rdx, rdx
  118. xor rdi, rdi
  119. mov rax, 72 ; fcntl
  120. mov rsi, 3 ; F_GETFL
  121. syscall
  122. mov [fcntl_flag], rax
  123. mov rdx, rax
  124. or rdx, O_NONBLOCK
  125. mov rax, 72 ; fcntl
  126. mov rsi, 4 ; F_SETFL
  127. syscall
  128. cmp rax, 0
  129. jne fault
  130. ; preparing SIGINT catch
  131. mov rax, proc_lap_handler
  132. mov qword [sigaction.sa_handler], rax
  133. mov eax, 0x14000000 ; SA_RESTART | SA_RESTORER
  134. mov dword [sigaction.sa_flags], eax
  135. mov rax, sig_restorer
  136. mov qword [sigaction.sa_restorer], rax
  137. mov rax, 13 ; sys_rt_sigaction
  138. mov rdi, 2 ; SIGINT
  139. mov rsi, sigaction
  140. mov rdx, 0 ; NULL
  141. mov r10, 8 ; sig_size
  142. syscall
  143. cmp rax, 0
  144. jne fault
  145. mov rax, 228 ; clock_gettime
  146. mov rdi, 0 ; CLOCK_REALTIME
  147. mov rsi, ts_start
  148. syscall
  149. mov rax, 1 ; write
  150. mov rdi, 2 ; stderr
  151. mov rsi, startmsg
  152. mov rdx, startmsglen
  153. syscall
  154. ; set value for ts_sleep.tv_nsec given time_res
  155. ; div sleep time by 10 for each digits added bellow seconds
  156. mov rax, 100000000
  157. mov r8, [time_res]
  158. mov r9, 10
  159. xor rdx, rdx
  160. setsleep_loop:
  161. cmp r8, 1
  162. jle setsleep_endloop
  163. div r9
  164. sub r8, 1
  165. jmp setsleep_loop
  166. setsleep_endloop:
  167. mov [ts_sleep.tv_nsec], rax
  168. std ; set DF for string operations
  169. main_loop:
  170. push 2 ; stderr
  171. push 0xD ; \r
  172. call proc_print_time
  173. ; Attempt to read from stdin
  174. ; if something read, enter has been pressed
  175. mov rax, 0
  176. mov rdi, 0
  177. mov rsi, buf
  178. mov rdx, 1
  179. syscall
  180. cmp rax, 0
  181. jge flush_stdin ; flush stdin and exit
  182. mov rax, 35 ; nanosleep
  183. mov rdi, ts_sleep
  184. mov rsi, 0
  185. syscall
  186. jmp main_loop ; main_loop
  187. flush_stdin:
  188. mov rax, 0
  189. mov rdi, 0
  190. mov rsi, buf
  191. mov rdx, 1
  192. syscall
  193. cmp rax, 0
  194. je newline_exit
  195. jg flush_stdin
  196. mov rdi, 0 ; EXIT OK
  197. ;
  198. ; Expect rdi to be the return code
  199. ;
  200. exit:
  201. push rdi
  202. ; restoring stdin state
  203. mov rax, 72 ; fcntl
  204. xor rdi, rdi
  205. mov rsi, 4 ; F_SETFL
  206. mov rdx, [fcntl_flag]
  207. syscall
  208. cmp rax, 0
  209. je exit_end
  210. pop rdi ; failed to restore
  211. push 1 ; exit FAIL
  212. exit_end:
  213. mov rax, 60 ; sys_exit
  214. pop rdi ; return code
  215. syscall
  216. fault:
  217. mov rdi, 2 ; stderr
  218. call proc_nl
  219. mov rax, 1
  220. mov rsi, faultmsg
  221. mov rdx, faultmsglen
  222. syscall
  223. mov rdi, 1 ; failure
  224. jmp exit
  225. newline_exit:
  226. mov rdi, 1
  227. call proc_nl
  228. mov rdi, 0 ; exit OK
  229. jmp exit
  230. align 32
  231. ;
  232. ; Print current time on FD and add a leading char CHR
  233. ; push FD & push CHR to set arguments
  234. ; Warning : DF must be set
  235. ;
  236. proc_print_time:
  237. mov rax, 228 ; clock_gettime
  238. mov rdi, 0 ; CLOCK_REALTIME
  239. mov rsi, ts_cur
  240. syscall ; updating ts_cur time
  241. mov rax, [ts_cur.tv_nsec]
  242. sub rax, [ts_start.tv_nsec]
  243. cmp rax, 0 ; Calculating elapsed ns
  244. jge print_time_us_cont
  245. add rax, 1000000000 ; negativ result
  246. sub qword [ts_cur.tv_sec], 1
  247. print_time_us_cont:
  248. xor rdx, rdx
  249. div qword [ts_sleep.tv_nsec] ; Divide result given time_res
  250. mov r9, [rsp + 8]
  251. mov byte [timestr+timestrlen-1], r9b ; set last chr from 1st argument
  252. ; set the nanosec chars (time_res chars) in timestr
  253. mov rbx, 0x2020202020202020
  254. mov rcx, [time_res]
  255. mov r8, 10
  256. procpt_loopns:
  257. xor rdx, rdx
  258. div r8
  259. or dl, 0x30 ; '0'
  260. shl rbx, 8
  261. mov bl, dl
  262. loop procpt_loopns
  263. mov [timestr+7], rbx
  264. ; filling timestr with seconds & minutes chars
  265. mov rax, [ts_cur.tv_sec]
  266. sub rax, [ts_start.tv_sec] ; rax now contain elapsed seconds
  267. mov r8, 10
  268. mov r9, 6
  269. xor rdx, rdx
  270. div r8
  271. mov bh, dl
  272. xor dl, dl
  273. div r9
  274. mov bl, dl
  275. or bx, 0x3030
  276. mov word [timestr + 4], bx
  277. xor dl, dl
  278. div r8
  279. mov bh, dl
  280. xor dl, dl
  281. div r9
  282. mov bl, dl
  283. or bx, 0x3030
  284. mov word [timestr + 1], bx
  285. ; using rbx to stores the hours string
  286. xor rbx, rbx
  287. mov rcx, HOURSLEN
  288. mov r8, 10
  289. procpt_looph:
  290. xor dl, dl
  291. div r8
  292. shl rbx, 8
  293. mov bl, dl
  294. cmp rax, 0
  295. loopne procpt_looph
  296. procpt_looph_end:
  297. mov r8, HOURSLEN - 2
  298. cmp rcx, HOURSLEN - 2
  299. cmovl r8, rcx ; r8 stores hours digit count (at least 2)
  300. cmp rcx, 0
  301. jz procpt_looph2_end
  302. procpt_looph2:
  303. shl rbx, 8
  304. loopne procpt_looph2
  305. procpt_looph2_end:
  306. mov r9, 0x3030303030303030 ; ASCII convertion
  307. or rbx, r9
  308. mov [hours], rbx
  309. mov rax, 1 ; write
  310. mov rdi, [rsp + 16] ; fd as 2nd argument
  311. lea rsi, [hours + r8] ; hours start pointer
  312. mov rdx, timestrlen + HOURSLEN
  313. sub rdx, r8 ; timestr + hours len
  314. syscall
  315. ret 16
  316. ;
  317. ; sig handler for SIGINT displaying lap count and time on stdout
  318. ;
  319. proc_lap_handler:
  320. mov rax, 1
  321. mov rdi, 2
  322. mov rsi, cr
  323. mov rdx, 1
  324. syscall ; \r on stderr
  325. mov rax, 1
  326. mov rdi, 1
  327. mov rsi, lapsmsg
  328. mov rdx, 4 ; "Lap "
  329. syscall
  330. ; increment the lapcount str directly
  331. mov rcx, lapcountlen - 1; digit counter starting by the right most
  332. proclap_loop:
  333. cmp byte [lapcount + rcx], '9'
  334. jl proclap_loopend
  335. mov byte [lapcount + rcx], '0' ; set current digit to '0'
  336. loop proclap_loop
  337. proclap_loopend:
  338. add byte [lapcount + rcx], 1 ; increment current digit
  339. mov rdx, lapcountlen
  340. sub rdx, rcx
  341. cmp rdx, [laplen] ; update laplen if needed
  342. cmovl rdx, [laplen]
  343. mov [laplen], rdx
  344. mov rsi, lapcount + lapcountlen
  345. sub rsi, rdx
  346. mov rax, 1
  347. mov rdi, 1 ; stdout
  348. syscall
  349. mov rax, 1 ; write
  350. mov rsi, lapsmsg + 3
  351. mov rdx, 3 ; " : "
  352. syscall
  353. std ; set DF for string operations
  354. push 1 ; stdout
  355. push 0xA ; \n
  356. call proc_print_time
  357. ret
  358. sig_restorer:
  359. mov rax, 15 ; sys_rt_sigreturn
  360. xor rdi, rdi
  361. syscall
  362. ;
  363. ; Argument parsing
  364. ;
  365. arg_parse:
  366. ; Checking argument count
  367. mov rax, [rsp]
  368. cmp rax, 1
  369. je arg_ok
  370. cmp rax, 3
  371. jle parse_r
  372. arg_err: ; badval & badarg jmp here too
  373. mov rax, [rsp+8] ; argv[0] program name
  374. mov rdi, -1 ; return status
  375. jmp usage
  376. parse_r:
  377. mov rax, [rsp+16] ; 1st arg should be "-r"
  378. mov bl, [rax]
  379. cmp bl, '-'
  380. jne badarg
  381. mov bl, [rax+1]
  382. cmp bl, 'h' ; -h
  383. je arg_err
  384. cmp bl, 'r'
  385. jne badarg
  386. mov bl, [rax+2]
  387. cmp bl, 0
  388. jne arg_nxt ; the value seems to be just after the -r like -r2
  389. nxt_arg:
  390. ; the 1st argument is -r the second must be the time_res
  391. ; check that the arg exists
  392. mov rax, [rsp]
  393. cmp rax, 3
  394. jne arg_err
  395. mov rax, [rsp+24]
  396. jmp arg_cont
  397. arg_nxt:
  398. ; check that there is no more args
  399. mov rbx, [rsp]
  400. cmp rbx, 2
  401. jne arg_err
  402. add rax, 2
  403. arg_cont:
  404. ; rax should point on the value
  405. mov bl, [rax+1]
  406. cmp bl, 0
  407. jne badval
  408. xor rbx, rbx
  409. mov bl, [rax]
  410. cmp bl, '1'
  411. jl badval
  412. cmp bl, '8'
  413. jg badval
  414. sub bl, '0'
  415. mov [time_res], rbx
  416. jmp arg_ok
  417. ; print an error message, usage and exit with rax pointing the value
  418. badval:
  419. mov rsi, badval_msg
  420. mov rdx, badval_msglen
  421. jmp arg_strerr
  422. ; print an error message, usage and exit
  423. badarg:
  424. mov rsi, badarg_msg
  425. mov rdx, badarg_msglen
  426. mov rax, [rsp+16]
  427. jmp arg_strerr
  428. ; rsi msg ptr, rdx msg len, rax arg ptr
  429. arg_strerr:
  430. mov r8, rax
  431. mov rax, 1
  432. mov rdi, 1
  433. syscall
  434. mov rsi, r8
  435. mov rax, r8
  436. call proc_strlen
  437. mov rax, 1
  438. syscall
  439. call proc_nl
  440. call proc_nl
  441. jmp arg_err
  442. ;
  443. ; Print usage and exit
  444. ; Except rax to point on programm name and rdi to be the return code
  445. ;
  446. usage:
  447. push rdi
  448. push rax
  449. mov rax, 1 ; write
  450. mov rdi, 1 ; stdout
  451. mov rsi, usage_pre
  452. mov rdx, usage_prelen
  453. syscall
  454. pop rsi
  455. mov rax, rsi
  456. call proc_strlen
  457. mov rax, 1
  458. syscall
  459. mov rax, 1
  460. mov rsi, usage_post
  461. mov rdx, usage_postlen
  462. syscall
  463. mov rax, 60
  464. pop rdi
  465. syscall
  466. ; With rax pointing on the string, the result will be in rdx
  467. proc_strlen:
  468. mov r8, rax
  469. mov r9, r8
  470. dec r9
  471. strlen_loop:
  472. inc r9
  473. mov al, [r9]
  474. cmp al, 0
  475. jne strlen_loop
  476. sub r9, r8
  477. mov rdx, r9
  478. ret
  479. ; with rdi the fd
  480. proc_nl:
  481. mov rax, 1
  482. mov rsi, nl
  483. mov rdx, 1
  484. syscall
  485. ret
  486. .end: