https://github.com/akkartik/mu/blob/main/linux/factorial4.subx
 1 ## compute the factorial of 5, and return the result in the exit code
 2 #
 3 # Uses syntax sugar for:
 4 #   rm32 operands
 5 #   function calls
 6 #   control flow
 7 #
 8 # To run:
 9 #   $ ./translate_subx init.linux [01]*.subx factorial.subx -o factorial
10 #   $ bootstrap/bootstrap run factorial
11 # Expected result:
12 #   $ echo $?
13 #   120
14 #
15 # You can also run the automated test suite:
16 #   $ bootstrap/bootstrap run factorial test
17 # Expected output:
18 #   ........
19 # Every '.' indicates a passing test. Failing tests get a 'F'.
20 #
21 # Compare factorial3.subx
22 
23 == code
24 
25 factorial:  # n: int -> _/eax: int
26     # . prologue
27     55/push-ebp
28     89/<- %ebp 4/r32/esp
29     # save registers
30     51/push-ecx
31     # if (n <= 1) return 1
32     81 7/subop/compare *(ebp+8) 1/imm32
33     {
34       7f/jump-if-> break/disp8
35       b8/copy-to-eax 1/imm32
36       eb/jump $factorial:end/disp8
37     }
38     # n > 1; return n * factorial(n-1)
39     8b/-> *(ebp+8) 1/r32/ecx
40     49/decrement-ecx
41     (factorial %ecx)  # => eax
42     f7 4/subop/multiply-into-eax *(ebp+8)
43     # TODO: check for overflow
44 $factorial:end:
45     # restore registers
46     59/pop-to-ecx
47     # . epilogue
48     89/<- %esp 5/r32/ebp
49     5d/pop-to-ebp
50     c3/return
51 
52 test-factorial:
53     (factorial 5)
54     (check-ints-equal %eax 0x78 "F - test-factorial")
55     c3/return
56 
57 Entry:  # run tests if necessary, compute `factorial(5)` if not
58     # . prologue
59     89/<- %ebp 4/r32/esp
60 
61     # initialize heap (needed by tests elsewhere)
62     (new-segment *Heap-size Heap)
63 
64     # if (argc <= 1) return factorial(5)
65     {
66       # if (argc > 1) break
67       81 7/subop/compare *ebp 1/imm32
68       7f/jump-if-> break/disp8
69       # ebx = factorial(5)
70       (factorial 5)  # => eax
71       89/<- %ebx 0/r32/eax
72       eb/jump $main:end/disp8
73     }
74     # otherwise if first arg is "test", then return run_tests()
75     {
76       # if (!kernel-string-equal?(argv[1], "test")) break
77       (kernel-string-equal? *(ebp+8) "test")  # => eax
78       3d/compare-eax-and 0/imm32/false
79       74/jump-if-= break/disp8
80       #
81       (run-tests)
82       # exit(*Num-test-failures)
83       8b/-> *Num-test-failures 3/r32/ebx
84       eb/jump $main:end/disp8
85     }
86     bb/copy-to-ebx 0/imm32
87 $main:end:
88     e8/call  syscall_exit/disp32