; The MIT License (MIT) ; ; Copyright (c) 2026 pacman64 ; ; Permission is hereby granted, free of charge, to any person obtaining a copy ; of this software and associated documentation files (the "Software"), to deal ; in the Software without restriction, including without limitation the rights ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ; copies of the Software, and to permit persons to whom the Software is ; furnished to do so, subject to the following conditions: ; ; The above copyright notice and this permission notice shall be included in ; all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ; SOFTWARE. ; To compile this app for linux/amd64, use the commands ; ; nasm -f elf64 -o ruler-linux-amd64.o ruler-linux-amd64.asm ; ld -n -s ruler-linux-amd64.o -o ruler ; rm ruler-linux-amd64.o ; ruler [width...] ; ; Emit a line with a ruler-like pattern, which helps check the width of output ; lines right above it. If given a valid number, it's used as the ruler width, ; or 80 is used as the default width. stdout equ 1 sys_write equ 1 sys_exit equ 60 lf equ 10 ; line feed section .rodata one_length equ 2 ; each middle-dot is 2 bytes ten: db 0xc2, 0xb7, 0xc2, 0xb7, 0xc2, 0xb7, 0xc2, 0xb7 ; 4 middle-dots db 0xe2, 0x95, 0xb5 ; 1 high half vertical bar five_length equ $ - ten db 0xc2, 0xb7, 0xc2, 0xb7, 0xc2, 0xb7, 0xc2, 0xb7 ; 4 middle-dots db 0xe2, 0x94, 0x82 ; 1 fancy vertical bar, not the ASCII one ten_length equ $ - ten eol: db lf ; end of line section .text global _start _start: ; rcx = argc pop rcx ; just use the default ruler-width, if no arguments were given cmp rcx, 1 mov rdx, 1 je bad_width ; skip argv[0] pop rbx ; get argv[1] pop rbx ; an empty string fails to parse: if so, use the default ruler-width mov al, [rbx] cmp al, 0 je bad_width mov rdx, 0 mov rax, 0 parse_width: mov al, [rbx] cmp al, 0 je good_width cmp al, '0' jl bad_width cmp al, '9' jg bad_width imul rdx, 10 sub al, '0' add rdx, rax inc rbx jmp parse_width bad_width: ; use the default ruler-width mov rdx, 80 good_width: ruler: ; the number-parser code stores the result in `rdx`, but the loop doing ; the writing uses `rcx` instead mov rcx, rdx ; empty rulers shouldn't even emit a single-byte line-feed cmp rcx, 1 jl quit write_tens: ; for (; rcx >= 10; rcx -= 10) next_ten: cmp rcx, 10 jl write_trail push rcx ; write(stdout, ten, ten_length) mov rax, sys_write mov rdi, stdout mov rsi, ten mov rdx, ten_length syscall pop rcx sub rcx, 10 jmp next_ten write_trail: cmp rcx, 5 jl write_ones push rcx ; write(stdout, ten, five_length) mov rax, sys_write mov rdi, stdout mov rsi, ten mov rdx, five_length syscall pop rcx sub rcx, 5 write_ones: imul rcx, rcx, one_length ; write(stdout, ten, rdx = one_length * rcx) mov rax, sys_write mov rdi, stdout mov rsi, ten mov rdx, rcx syscall end_line: ; write(stdout, eol, 1) mov rax, sys_write mov rdi, stdout mov rsi, eol mov rdx, 1 syscall quit: ; exit(0) mov rax, sys_exit mov rdi, 0 syscall