WTFStopW : a simple stopwatch
nasm
x86-64
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 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  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 .data
  64. align 8
  65. TIMESPEC ts_start
  66. TIMESPEC ts_cur
  67. TIMESPEC ts_sleep
  68. time_res: dq 2 ; 2 digits bellow seconds can grow to 8
  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. buf: db 0
  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. call arg_parse
  115. mov rbx, ts_start
  116. %define RBXPTR(x) rbx + x - ts_start
  117. ; set stdin reads non blocking
  118. xor rdx, rdx
  119. xor rdi, rdi
  120. mov rax, 72 ; fcntl
  121. mov rsi, 3 ; F_GETFL
  122. syscall
  123. mov [RBXPTR(fcntl_flag)], rax
  124. mov rdx, rax
  125. or dx, O_NONBLOCK
  126. mov rax, 72 ; fcntl
  127. mov rsi, 4 ; F_SETFL
  128. syscall
  129. test al, al
  130. js fault
  131. ; preparing SIGINT catch
  132. mov rax, proc_lap_handler
  133. mov qword [RBXPTR(sigaction.sa_handler)], rax
  134. mov ecx, 0x14000000 ; SA_RESTART | SA_RESTORER
  135. mov dword [RBXPTR(sigaction.sa_flags)], ecx
  136. add rax, sig_restorer - proc_lap_handler
  137. mov qword [RBXPTR(sigaction.sa_restorer)], rax
  138. mov rax, 13 ; sys_rt_sigaction
  139. mov rdi, 2 ; SIGINT
  140. mov rsi, rbx
  141. add rsi, sigaction - ts_start
  142. ;mov rsi, sigaction
  143. mov rdx, 0 ; NULL
  144. mov r10, 8 ; sig_size
  145. syscall
  146. cmp rax, 0
  147. jne fault
  148. mov rax, 228 ; clock_gettime
  149. mov rdi, 0 ; CLOCK_REALTIME
  150. mov rsi, rbx
  151. syscall
  152. mov rax, 1 ; write
  153. mov rdi, 2 ; stderr
  154. mov rsi, rbx
  155. add rsi, startmsg - ts_start
  156. mov rdx, startmsglen
  157. syscall
  158. ; set value for ts_sleep.tv_nsec given time_res
  159. ; div sleep time by 10 for each digits added bellow seconds
  160. mov rax, 100000000
  161. xor rcx, rcx
  162. mov cl, [RBXPTR(time_res)]
  163. dec cl
  164. test cl, cl
  165. jz setsleep_loopend
  166. mov rsi, 10
  167. xor rdx, rdx
  168. setsleep_loop:
  169. div rsi
  170. loop setsleep_loop
  171. setsleep_loopend:
  172. mov [RBXPTR(ts_sleep.tv_nsec)], rax
  173. mov r12w, 0x0d02
  174. mov r13, proc_print_time
  175. mov r14, buf ; nanosleep
  176. mov r15, ts_sleep
  177. std ; set DF for string operations
  178. align 16
  179. main_loop:
  180. call r13 ; proc_print_time
  181. ; Attempt to read from stdin (syscall prepared by proc_print_time)
  182. ; if something read, enter has been pressed
  183. syscall
  184. test al, al
  185. jns flush_stdin ; flush & exit
  186. mov rax, 35 ; nanosleep
  187. mov rdi, r15
  188. xor rsi, rsi
  189. syscall
  190. jmp main_loop ; main_loop
  191. flush_stdin:
  192. jz newline_exit
  193. xor rax, rax
  194. ;xor rdi, rdi ; done in main_loop
  195. ;mov rsi, r14
  196. ;mov rdx, 1
  197. syscall
  198. test al, al
  199. jns flush_stdin
  200. xor rbx, rbx ; EXIT OK
  201. jmp exit
  202. newline_exit:
  203. mov rdi, 2 ; stderr
  204. call proc_nl
  205. xor rbx, rbx ; exit OK
  206. jmp exit
  207. ;
  208. ; Expect rbx to be the return code
  209. ;
  210. exit:
  211. ; restoring stdin state
  212. mov rax, 72 ; fcntl
  213. xor rdi, rdi
  214. mov rsi, 4 ; F_SETFL
  215. mov rdx, [fcntl_flag]
  216. syscall
  217. mov rdi, rbx ; return code
  218. test rax, rax
  219. cmovnz rdi, rax ; exit FAIL
  220. mov rax, 60 ; sys_exit
  221. syscall
  222. fault:
  223. mov rdi, 2 ; stderr
  224. call proc_nl
  225. mov rax, 1
  226. mov rsi, faultmsg
  227. mov rdx, faultmsglen
  228. syscall
  229. mov rbx, 1 ; failure
  230. jmp exit
  231. ;
  232. ; Print current time on FD and add a leading char CHR
  233. ; argument are in r12w r12b is FD and r12w(h) is CHR
  234. ; Warning : DF must be set
  235. ;
  236. align 16
  237. proc_print_time:
  238. mov rax, 228 ; clock_gettime
  239. xor rdi, rdi ; CLOCK_REALTIME
  240. mov rsi, ts_cur
  241. syscall ; updating ts_cur time
  242. mov rsi, ts_start.tv_sec
  243. %define RSIPTR(x) rsi + x - ts_start.tv_sec
  244. mov rax, [RSIPTR(ts_cur.tv_nsec)]
  245. sub rax, [ts_start.tv_nsec] ; allign nano sec loop
  246. jge print_time_us_cont
  247. add rax, 1000000000 ; negativ result
  248. sub qword [RSIPTR(ts_cur.tv_sec)], 1
  249. print_time_us_cont:
  250. xor rdx, rdx
  251. div qword [RSIPTR(ts_sleep.tv_nsec)] ; Divide result given time_res
  252. mov bx, r12w
  253. shr bx, 8
  254. mov byte [RSIPTR(timestrend - 1)], bl ; set last chr from 1st argument
  255. ; set the nanosec chars (time_res chars) in timestr
  256. mov rbx, 0x2020202020202020
  257. mov rcx, [RSIPTR(time_res)]
  258. mov rdi, 10
  259. xor rdx, rdx
  260. align 16 ; should be allready alligned
  261. procpt_loopns:
  262. div rdi
  263. or dl, 0x30 ; '0'
  264. shl rbx, 8
  265. xchg bl, dl
  266. loop procpt_loopns
  267. mov [timestr+7], rbx
  268. ; filling timestr with seconds & minutes chars
  269. mov rax, [RSIPTR(ts_cur.tv_sec)]
  270. sub rax, [RSIPTR(ts_start.tv_sec)] ; rax now contain elapsed seconds
  271. mov rcx, 10
  272. mov rdi, 6
  273. xor rdx, rdx
  274. div rcx
  275. mov bh, dl
  276. xor dl, dl
  277. div rdi
  278. mov bl, dl
  279. or bx, 0x3030
  280. mov word [timestr + 4], bx
  281. xor dl, dl
  282. div rcx
  283. mov bh, dl
  284. xor dl, dl
  285. div rdi
  286. mov bl, dl
  287. or bx, 0x3030
  288. mov word [timestr + 1], bx
  289. ; using rbx to stores the hours string
  290. xor rbx, rbx
  291. mov rcx, HOURSLEN
  292. mov rdi, 10
  293. xor dl, dl
  294. align 16
  295. procpt_looph:
  296. div rdi
  297. shl rbx, 8
  298. xchg bl, dl
  299. test rax, rax
  300. loopnz procpt_looph
  301. xor rax, rax
  302. mov al, HOURSLEN - 2
  303. cmp cx, HOURSLEN - 2
  304. cmovl ax, cx ; rax stores hours digit count (at least 2)
  305. test cl, cl
  306. jz procpt_looph2_end
  307. align 16
  308. procpt_looph2: ; end rbx left shift before sto
  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. xor rdi, rdi
  316. mov dil, r12b ; fd as 2nd argument
  317. mov rdx, timestrlen + HOURSLEN
  318. sub rdx, rax ; timestr + hours len
  319. lea rsi, [RSIPTR(hours + rax)] ; hours start pointer
  320. mov rax, 1 ; write
  321. syscall
  322. test rax, rax
  323. js fault
  324. ; ALLIGN TRICK : prepare read syscall for main_loop
  325. xor rax, rax
  326. xor rdi, rdi
  327. mov rsi, r14
  328. mov rdx, 1
  329. ret
  330. %undef RSIPTR
  331. ;
  332. ; sig handler for SIGINT displaying lap count and time on stdout
  333. ;
  334. proc_lap_handler:
  335. mov rax, 1
  336. mov rdi, 2
  337. mov rsi, cr
  338. mov rdx, 1
  339. syscall ; \r on stderr
  340. test rax, rax
  341. js fault
  342. mov rax, 1
  343. mov rdi, 1
  344. mov rsi, lapsmsg
  345. mov rdx, 4 ; "Lap "
  346. syscall
  347. test rax, rax
  348. js fault
  349. ; increment the lapcount str directly
  350. mov rcx, lapcountlen - 1; digit counter starting by the right most
  351. proclap_loop:
  352. cmp byte [lapcount + rcx], '9'
  353. jl proclap_loopend
  354. mov byte [lapcount + rcx], '0' ; set current digit to '0'
  355. loop proclap_loop
  356. proclap_loopend:
  357. add byte [lapcount + rcx], 1 ; increment current digit
  358. mov rdx, lapcountlen
  359. sub rdx, rcx
  360. cmp rdx, [laplen] ; update laplen if needed
  361. cmovl rdx, [laplen]
  362. mov [laplen], rdx
  363. mov rsi, lapcount + lapcountlen
  364. sub rsi, rdx
  365. mov rax, 1
  366. mov rdi, 1 ; stdout
  367. syscall
  368. test rax, rax
  369. js fault
  370. mov rax, 1 ; write
  371. mov rsi, lapsmsg + 3
  372. mov rdx, 3 ; " : "
  373. syscall
  374. test rax, rax
  375. js fault
  376. std ; set DF for string operations
  377. mov r12w, 0x0A01 ; \n & stdout
  378. call proc_print_time
  379. ret
  380. sig_restorer:
  381. mov rax, 15 ; sys_rt_sigreturn
  382. xor rdi, rdi
  383. syscall
  384. ;
  385. ; Argument parsing
  386. ;
  387. arg_parse:
  388. ; Checking argument count
  389. mov rcx, [rsp+8] ; argc
  390. mov rsi, [rsp+24] ; argv[1]
  391. mov rdi, badarg_msg
  392. cmp rcx, 1
  393. je arg_ok
  394. cmp rcx, 3
  395. jle parse_r
  396. arg_err: ; badval & badarg jmp here too
  397. mov rax, [rsp+16] ; argv[0] program name
  398. mov rdi, -1 ; return status
  399. jmp usage
  400. parse_r:
  401. mov rax, rsi
  402. cmp byte [rsi], '-'
  403. jne badarg
  404. mov bl, [rsi+1]
  405. cmp bl, 'h' ; -h
  406. je arg_err
  407. cmp bl, 'r'
  408. jne badarg
  409. cmp byte [rsi+2], 0
  410. jne arg_nxt ; the value seems to be just after the -r like -r2
  411. nxt_arg:
  412. ; the 1st argument is -r the second must be the time_res
  413. ; check that the arg exists
  414. cmp cl, 3
  415. jne arg_err
  416. mov rsi, [rsp+32]
  417. jmp arg_cont
  418. arg_nxt:
  419. ; check that there is no more args
  420. cmp cl, 2
  421. jne arg_err
  422. add rsi, 2
  423. arg_cont:
  424. ; rsi should point on the ASCII digit counter
  425. cmp byte [rsi+1], 0
  426. jne badval
  427. xor rbx, rbx
  428. mov bl, [rsi]
  429. cmp bl, '1'
  430. jl badval
  431. cmp bl, '8'
  432. jg badval
  433. xor bl, 0x30 ; '0'
  434. mov [rdi + time_res - badarg_msg], rbx
  435. arg_ok:
  436. ret
  437. ; print an error message, usage and exit with rax pointing the value and
  438. ; rdi badarg_msg ptr
  439. badval:
  440. mov rbx, rsi
  441. lea rsi, [rdi + badval_msg - badarg_msg]
  442. mov rdx, badval_msglen
  443. jmp arg_strerr
  444. ; print an error message, usage and exit
  445. badarg:
  446. mov rbx, rsi
  447. mov rsi, rdi
  448. mov rdx, badarg_msglen
  449. jmp arg_strerr
  450. ; rsi msg ptr, rdx msg len, rbx arg ptr
  451. arg_strerr:
  452. mov rax, 1
  453. mov rdi, 1
  454. syscall
  455. mov rsi, rbx
  456. mov rax, rbx
  457. call proc_strlen
  458. mov rax, 1
  459. syscall
  460. call proc_nl
  461. call proc_nl
  462. jmp arg_err
  463. ;
  464. ; Print usage and exit
  465. ; Except rax to point on programm name and rdi to be the return code
  466. ;
  467. usage:
  468. push rdi
  469. push rax
  470. mov rax, 1 ; write
  471. mov rdi, 1 ; stdout
  472. mov rsi, usage_pre
  473. mov rdx, usage_prelen
  474. syscall
  475. pop rsi
  476. mov rax, rsi
  477. call proc_strlen
  478. mov rax, 1
  479. syscall
  480. mov rax, 1
  481. mov rsi, usage_post
  482. mov rdx, usage_postlen
  483. syscall
  484. mov rax, 60
  485. pop rdi
  486. syscall
  487. ; With rax pointing on the string, the result will be in rdx
  488. proc_strlen:
  489. mov r8, rax
  490. mov r9, r8
  491. dec r9
  492. strlen_loop:
  493. inc r9
  494. mov al, [r9]
  495. cmp al, 0
  496. jne strlen_loop
  497. sub r9, r8
  498. mov rdx, r9
  499. ret
  500. ; with rdi the fd
  501. proc_nl:
  502. mov rax, 1
  503. mov rsi, nl
  504. mov rdx, 1
  505. syscall
  506. ret
  507. .end: