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

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