File: ruler-linux-amd64.asm
   1 ; The MIT License (MIT)
   2 ;
   3 ; Copyright (c) 2026 pacman64
   4 ;
   5 ; Permission is hereby granted, free of charge, to any person obtaining a copy
   6 ; of this software and associated documentation files (the "Software"), to deal
   7 ; in the Software without restriction, including without limitation the rights
   8 ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   9 ; copies of the Software, and to permit persons to whom the Software is
  10 ; furnished to do so, subject to the following conditions:
  11 ;
  12 ; The above copyright notice and this permission notice shall be included in
  13 ; all copies or substantial portions of the Software.
  14 ;
  15 ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 ; SOFTWARE.
  22 
  23 
  24 ; To compile this app for linux/amd64, use the commands
  25 ;
  26 ; nasm -f elf64 -o ruler-linux-amd64.o ruler-linux-amd64.asm
  27 ; ld -n -s ruler-linux-amd64.o -o ruler
  28 ; rm ruler-linux-amd64.o
  29 
  30 
  31 ; ruler [width...]
  32 ;
  33 ; Emit a line with a ruler-like pattern, which helps check the width of output
  34 ; lines right above it. If given a valid number, it's used as the ruler width,
  35 ; or 80 is used as the default width.
  36 
  37 
  38 stdout equ 1
  39 sys_write equ 1
  40 sys_exit equ 60
  41 
  42 lf equ 10 ; line feed
  43 
  44 section .rodata
  45 
  46 one_length equ 2 ; each middle-dot is 2 bytes
  47 
  48 ten:
  49     db 0xc2, 0xb7, 0xc2, 0xb7, 0xc2, 0xb7, 0xc2, 0xb7 ; 4 middle-dots
  50     db 0xe2, 0x95, 0xb5 ; 1 high half vertical bar
  51 five_length equ $ - ten
  52     db 0xc2, 0xb7, 0xc2, 0xb7, 0xc2, 0xb7, 0xc2, 0xb7 ; 4 middle-dots
  53     db 0xe2, 0x94, 0x82 ; 1 fancy vertical bar, not the ASCII one
  54 ten_length equ $ - ten
  55 
  56 eol: db lf ; end of line
  57 
  58 section .text
  59 
  60 global _start
  61 
  62 _start:
  63 
  64 ; rcx = argc
  65 pop rcx
  66 
  67 ; just use the default ruler-width, if no arguments were given
  68 cmp rcx, 1
  69 mov rdx, 1
  70 je bad_width
  71 
  72 ; skip argv[0]
  73 pop rbx
  74 ; get argv[1]
  75 pop rbx
  76 
  77 ; an empty string fails to parse: if so, use the default ruler-width
  78 mov al, [rbx]
  79 cmp al, 0
  80 je bad_width
  81 
  82 mov rdx, 0
  83 mov rax, 0
  84 parse_width:
  85     mov al, [rbx]
  86     cmp al, 0
  87     je good_width
  88 
  89     cmp al, '0'
  90     jl bad_width
  91     cmp al, '9'
  92     jg bad_width
  93 
  94     imul rdx, 10
  95     sub al, '0'
  96     add rdx, rax
  97 
  98     inc rbx
  99     jmp parse_width
 100 
 101 bad_width:
 102     ; use the default ruler-width
 103     mov rdx, 80
 104 good_width:
 105 
 106 ruler:
 107     ; the number-parser code stores the result in `rdx`, but the loop doing
 108     ; the writing uses `rcx` instead
 109     mov rcx, rdx
 110 
 111     ; empty rulers shouldn't even emit a single-byte line-feed
 112     cmp rcx, 1
 113     jl quit
 114 
 115 write_tens:
 116     ; for (; rcx >= 10; rcx -= 10)
 117 next_ten:
 118     cmp rcx, 10
 119     jl write_trail
 120     push rcx
 121     ; write(stdout, ten, ten_length)
 122         mov rax, sys_write
 123         mov rdi, stdout
 124         mov rsi, ten
 125         mov rdx, ten_length
 126         syscall
 127     pop rcx
 128     sub rcx, 10
 129     jmp next_ten
 130 
 131 write_trail:
 132     cmp rcx, 5
 133     jl write_ones
 134     push rcx
 135     ; write(stdout, ten, five_length)
 136         mov rax, sys_write
 137         mov rdi, stdout
 138         mov rsi, ten
 139         mov rdx, five_length
 140         syscall
 141     pop rcx
 142     sub rcx, 5
 143 
 144 write_ones:
 145     imul rcx, rcx, one_length
 146     ; write(stdout, ten, rdx = one_length * rcx)
 147     mov rax, sys_write
 148     mov rdi, stdout
 149     mov rsi, ten
 150     mov rdx, rcx
 151     syscall
 152 
 153 end_line:
 154     ; write(stdout, eol, 1)
 155     mov rax, sys_write
 156     mov rdi, stdout
 157     mov rsi, eol
 158     mov rdx, 1
 159     syscall
 160 
 161 quit:
 162     ; exit(0)
 163     mov rax, sys_exit
 164     mov rdi, 0
 165     syscall