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

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