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