x86-64 machine code function, 19 bytes
18 bytes for x86-32 with a non-standard calling convention, inc eax
is only 1B vs. 2B.
Callable from C (with the x86-64 System V calling convention) as:
void alphabet_staircase(char output[377]);
// or
struct string_result { char buf[377]; } \
alphabet_staircase(void); // return-value pointer passed as implicit first arg
i.e. it stores a flat string (including '\n'
newlines) into a buffer pointed to by rdi
.
0000000000400080 <alphabet_staircase>:
400080: 6a 61 push 0x61
400082: 58 pop rax ; eax = 'a'
0000000000400083 <alphabet_staircase.loop>:
;;; do {
400083: 8d 48 a1 lea ecx,[rax-0x5f] ; rax-'a'+1 + 1
400086: f3 aa rep stos BYTE PTR es:[rdi],al
400088: c6 47 ff 0a mov BYTE PTR [rdi-0x1],0xa
40008c: ff c0 inc eax
40008e: 3c 7a cmp al,0x7a
400090: 76 f1 jbe 400083 <alphabet_staircase.loop>
;;; } while(al <= 'z')
400092: c3 ret
400093 end of function. 0x93 - 0x80 = 0x13 = 19 bytes
Try it online! (including a _start
caller that prints the result with sys_write()
)
In each loop iteration, we store one extra copy of current letter, then overwrite it with a newline. rep stosb = memset(rdi, al, rcx)
. It advances rdi
to point one-past-the-end of the bytes it stored.
eax
holds the current letter, and we use it as the loop counter. do {} while(eax <= 'z');
. Setting rcx
from it with LEA requires that the upper bytes of eax
be zeroed, otherwise we could use 2-byte mov al, 'a'
to start.
The x86-64 SysV ABI requires that DF is cleared on function entry/exit, so we don't need cld
. If we wanted to use std
to store backwards, it wouldn't solve the problem of needing an offset of one in an addressing mode to store the newline (or needing a separate inc
/dec
, or swapping eax
with a reg holding 0x10
and using stosb
to store a newline); stosb
always overwrites the memory rdi
is pointing to.