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 apps/factorial.subx -o apps/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 apps/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