Arm Thumb (with libc calls), 34 bytes
Raw machine code
f7ff fffe
is for libc calls which haven't been linked yet.
Needs to be loaded at an address that is not a multiple of 4, due to alignment requirements. Yes, it is counterintuitive, but it will make sense later.
2400 a707 3401 2501 0038 0029 f7ff fffe
3501 42a5 d9f8 200a f7ff fffe e7f2 7525
0020
Uncommented Assembly
.thumb
.globl main
.thumb_func
main:
movs r4, #0
adr r7, str
.Lloop1:
adds r4, #1
movs r5, #1
.Lloop2:
movs r0, r7
movs r1, r5
bl printf
adds r5, #1
cmp r5, r4
bls .Lloop2
.Lloop2_end:
movs r0, #'\n'
bl putchar
b .Lloop1
str:
.asciz "%u "
Explanation
Equivalent C code:
#include <stdio.h>
#include <stdint.h>
int main(void)
{
uint32_t N = 0;
for (;;) {
++N;
for (uint32_t num = 1; num <= N; num++) {
printf("%u ", num);
}
putchar('\n');
}
// Unreachable
}
Since we are never returning from main
, I say, "to hell with calling convention" and just overwrite the callee-saved registers with our local variables. They won't know the difference. 😈
Specifically, I want r4 to be N (it is incremented before using), and r7 to be our printf string.
Unfortunately, we have an odd number of instructions, and adr
can only load addresses that are 4 byte aligned, so we need to make main
not be 4-byte aligned so the string is 4-byte aligned.
main:
movs r4, #0
adr r7, str
Begin printing the natural numbers. Increment N and start counting ℕ in r5.
.Lloop1:
adds r4, #1
movs r5, #1
printf("%u ", ℕ)
.Lloop2:
movs r0, r7
movs r1, r5
bl printf
Increment ℕ and loop while it is less than or equal to N.
adds r5, #1
cmp r5, r4
bls .Lloop2
After the second loop: putchar('\n')
.Lloop2_end:
movs r0, #'\n'
bl putchar
Jump back to the outer loop to increment N.
b .Lloop1
The printf string. This must be 4-byte aligned for adr
, which we do with how we load main
.
str:
.asciz "%u "
realmin
) \$\endgroup\$