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

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