An opinionated autoformatter for the GNU assembler, as.
cur is the result of my long-brewing frustration with the complete lack of open source GAS autoformatters.
git clone https://github.com/yousabmenissy/cur.git
cd cur
sudo make installIf gcc is not installed, install it first via MinGW or TDM-GCC. you can check with:
gcc --versionPress Win + S and type cmd, Right-click Command Prompt and select Run as administrator.
run these commands:
git clone https://github.com/yousabmenissy/cur.git
cd cur
gcc main.c lib/*.c -o cur.exe -Ilib -O3
if not exist "C:\Program Files\cur" mkdir "C:\Program Files\cur"
move cur.exe "C:\Program Files\cur"
powershell -Command "[System.Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\Program Files\cur', 'Machine')"Provide cur with 1 or more file names to format:
cur atoi.s putd.sBy default cur will use standard output. To format files in place use the -w flag:
cur atoi.s putd.s -wYou can use the -v flag to make cur print the processed files names:
cur *.s -w -vThe makefile include a perf section that will use the perf stat command to print performance stats:
make perfFor testing data, an unformatted disassembled file is generated from he source code of cur itself:
gcc main.c lib/*.c -S -fno-asynchronous-unwind-tables -Ilib && cat *.s > seed.sseed.s will contain approximately 2000 lines of assembly generated by the gcc compiler.
here's an example of the output make perf will produce:
gcc main.c lib/*.c -o cur -Ilib -O3
gcc main.c lib/*.c -S -fno-asynchronous-unwind-tables -Ilib
cat *.s > seed.s
sudo perf stat ./cur seed.s -w
Performance counter stats for './cur seed.s -w':
1.10 msec task-clock # 0.495 CPUs utilized
28 context-switches # 25.474 K/sec
0 cpu-migrations # 0.000 /sec
67 page-faults # 60.955 K/sec
3,376,957 cycles # 3.072 GHz
2,023,753 stalled-cycles-frontend # 59.93% frontend cycles idle
1,568,398 stalled-cycles-backend # 46.44% backend cycles idle
2,730,462 instructions # 0.81 insn per cycle
# 0.74 stalled cycles per insn
643,580 branches # 585.513 M/sec
21,647 branch-misses # 3.36% of all branches
0.002218476 seconds time elapsed
0.001623000 seconds user
0.000000000 seconds sys
rm *.s -f cur
The characters '\n', '\t' and '\s' are treated as blanks.
- tabs and spaces at the start of the line are ignored.
- tabs and spaces before the end of the line are ignored.
- multible lines of blank characters are all replaced with a single '\n' character.
- cur will make sure the last character in the file is a new line, '\n'.
Expressions that start with '.', does not end with ':' are treated as directives.
- they are always at indentaion level 0.
- their operands are aligned by 1 space.
Expressions that does not start with '.', does not compose of a signle digit, and end with ':' are treated as global labels.
- they are always at indentaion level 0.
- cur will allow either a space, tab or a new line to follow the ':'.
- if ':' is not followed by either a space, tab or a new line, cur will inject a new line after the colon and treats the rest as a different line
Expressions that start with '.', or compose of a signle digit, and end with ':' are treated as local labels.
- they are always at indentaion level 1.
- cur will allow either a space, tab or a new line to follow the ':'.
- if ':' is not followed by either a space, tab or a new line, cur will inject a new line after the colon and treats the rest as a different line
- a new line will be inserted before it if the previous expression was an instruction.
lines that start with '#' or '//' are treated as single line comments.
- they follow the indentaion of the previous line
- if '//' is used, cur will replace it with '#' followed by a space.
- blank lines at the start of the comment are ignored.
- comments that come after other expressions in the line are not formated
lines that start with /* are treated as multi-line comment untill */ or end of the file is reached.
- blank lines at the start and end of the comment are ignored, other blanks are uneffected.
- everything inside the comment is always at indentation level 1.
- the /* and */ are set on their own lines with no indentaion.
lines that is not recognized as directives, labels, or comments are treated as instructions.
- they are always at indentaion level 1.
- their operands are aligned with 2 spaces after the longest instruction in the block.
- each block has it's own alignment.
- a block start at the first instruction and end at the next blank line.
- instruction without operands are not cosidered when finding the alignment.
- a space is injected after each ',' in the operands list.
- macro calls are treated exactly like instructions.
.section .text
.global basename
.type basename, @function
// basename(path)
basename:
movq %rdi, %rsi
xor %rax, %rax
.LPBN0:lodsb
cmpb $'/',%al
cmove %rsi,%rdi
cmpq $0, %rax
jne .LPBN0
movq %rdi,%rax
ret
/*## Altered Registers ##
- rdi
- rsi*/.section .text
.global basename
.type basename, @function
# basename(path)
basename:
movq %rdi, %rsi
xor %rax, %rax
.LPBN0:
lodsb
cmpb $'/', %al
cmove %rsi, %rdi
cmpq $0, %rax
jne .LPBN0
movq %rdi, %rax
ret
/*
## Altered Registers ##
- rdi
- rsi
*/
.section .text
.global itoa
.type itoa, @function
itoa: # itoa(int, buff)
movq %rdi, %rax
testq %rax, %rax
jns 1f
imul $-1, %rax
1:
xor %rcx, %rcx
xor %rdx, %rdx
movq $10, %r8
.LPITOA0:
incq %rcx
xor %rdx, %rdx
div %r8
addq $'0', %rdx
movb %dl, -1(%rsi, %rcx)
cmpq $0, %rax
jne .LPITOA0
testq %rdi, %rdi
jns 1f
incq %rcx
movb $'-', -1(%rsi, %rcx)
1:
movb $0, (%rsi, %rcx) # Null terminate the string
// Reverse the string
movq %rsi, %rax
addq %rcx, %rax
decq %rax
movq %rax, %rdi
.LPITOA1:
cmpq %rsi, %rdi
jb 1f
movb (%rsi), %al
movb (%rdi), %dl
movb %al, (%rdi)
movb %dl, (%rsi)
incq %rsi
decq %rdi
jmp .LPITOA1
1:
movq %rcx, %rax
ret.section .text
.global itoa
.type itoa, @function
itoa: # itoa(int, buff)
movq %rdi, %rax
testq %rax, %rax
jns 1f
imul $-1, %rax
1:
xor %rcx, %rcx
xor %rdx, %rdx
movq $10, %r8
.LPITOA0:
incq %rcx
xor %rdx, %rdx
div %r8
addq $'0', %rdx
movb %dl, -1(%rsi, %rcx)
cmpq $0, %rax
jne .LPITOA0
testq %rdi, %rdi
jns 1f
incq %rcx
movb $'-', -1(%rsi, %rcx)
1:
movb $0, (%rsi, %rcx) # Null terminate the string
# Reverse the string
movq %rsi, %rax
addq %rcx, %rax
decq %rax
movq %rax, %rdi
.LPITOA1:
cmpq %rsi, %rdi
jb 1f
movb (%rsi), %al
movb (%rdi), %dl
movb %al, (%rdi)
movb %dl, (%rsi)
incq %rsi
decq %rdi
jmp .LPITOA1
1:
movq %rcx, %rax
ret
You can see more formatting examples at aslib and aslib-examples. Containing a combined total of over 8000 lines of assembly formatted with cur.
Copyright (c) 2025-present Yousab Menissy
Licensed under MIT License. See the LICENSE file for details.