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 10KB

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