https://github.com/akkartik/mu/blob/main/linux/factorial2.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 #
      6 # To run:
      7 #   $ ./translate_subx init.linux [01]*.subx factorial.subx -o factorial
      8 #   $ bootstrap/bootstrap run factorial
      9 # Expected result:
     10 #   $ echo $?
     11 #   120
     12 #
     13 # You can also run the automated test suite:
     14 #   $ bootstrap/bootstrap run factorial test
     15 # Expected output:
     16 #   ........
     17 # Every '.' indicates a passing test. Failing tests get a 'F'.
     18 #
     19 # Compare factorial.subx
     20
     21 == code
     22
     23 factorial:  # n: int -> _/eax: int
     24     # . prologue
     25     55/push-ebp
     26     89/<- %ebp 4/r32/esp
     27     # save registers
     28     51/push-ecx
     29     # if (n <= 1) return 1
     30     b8/copy-to-eax 1/imm32
     31     81 7/subop/compare *(ebp+8) 1/imm32
     32     7e/jump-if-<= $factorial:end/disp8
     33     # n > 1; return n * factorial(n-1)
     34     8b/-> *(ebp+8) 1/r32/ecx
     35     49/decrement-ecx
     36     # var tmp/eax: int = factorial(n-1)
     37     # . . push args
     38     51/push-ecx
     39     # . . call
     40     e8/call factorial/disp32
     41     # . . discard args
     42     81 0/subop/add %esp 4/imm32
     43     # return n * tmp
     44     f7 4/subop/multiply-into-eax *(ebp+8)
     45     # TODO: check for overflow
     46 $factorial:end:
     47     # restore registers
     48     59/pop-to-ecx
     49     # . epilogue
     50     89/<- %esp 5/r32/ebp
     51     5d/pop-to-ebp
     52     c3/return
     53
     54 test-factorial:
     55     # factorial(5)
     56     # . . push args
     57     68/push 5/imm32
     58     # . . call
     59     e8/call factorial/disp32
     60     # . . discard args
     61     81 0/subop/add %esp 4/imm32
     62     # check-ints-equal(eax, 120, msg)
     63     # . . push args
     64     68/push "F - test-factorial"/imm32
     65     68/push 0x78/imm32/expected-120
     66     50/push-eax
     67     # . . call
     68     e8/call check-ints-equal/disp32
     69     # . . discard args
     70     81 0/subop/add %esp 0xc/imm32
     71     # end
     72     c3/return
     73
     74 Entry:  # run tests if necessary, compute `factorial(5)` if not
     75     # . prologue
     76     89/<- %ebp 4/r32/esp
     77
     78     # initialize heap (needed by tests elsewhere)
     79     # . Heap = new-segment(Heap-size)
     80     # . . push args
     81     68/push Heap/imm32
     82     ff 6/subop/push *Heap-size
     83     # . . call
     84     e8/call new-segment/disp32
     85     # . . discard args
     86     81 0/subop/add %esp 8/imm32
     87
     88     # if (argc <= 1) return factorial(5)
     89     81 7/subop/compare *ebp 1/imm32
     90     7f/jump-if-> $main:run-tests/disp8
     91     # . . push args
     92     68/push 5/imm32
     93     # . . call
     94     e8/call factorial/disp32
     95     # . . discard args
     96     81 0/subop/add %esp 4/imm32
     97     # .
     98     89/<- %ebx 0/r32/eax
     99     eb/jump $main:end/disp8
    100 $main:run-tests:
    101     # otherwise if first arg is "test", then return run_tests()
    102     # if (!kernel-string-equal?(argv[1], "test")) goto do-nothing
    103     # . eax = kernel-string-equal?(argv[1], "test")
    104     # . . push args
    105     68/push "test"/imm32
    106     ff 6/subop/push *(ebp+8)
    107     # . . call
    108     e8/call kernel-string-equal?/disp32
    109     # . . discard args
    110     81 0/subop/add %esp 8/imm32
    111     # . if (eax == false) goto do-nothing
    112     3d/compare-eax-and 0/imm32/false
    113     74/jump-if-= $main:do-nothing/disp8
    114     # run-tests()
    115     e8/call run-tests/disp32
    116     # exit(*Num-test-failures)
    117     8b/-> *Num-test-failures 3/r32/ebx
    118     eb/jump $main:end/disp8
    119 $main:do-nothing:
    120     bb/copy-to-ebx 0/imm32
    121 $main:end:
    122     e8/call  syscall_exit/disp32