https://github.com/akkartik/mu/blob/main/linux/ex8.subx

     1 # Example reading commandline arguments: compute length of first arg.
     2 #
     3 # To run:
     4 #   $ bootstrap/bootstrap translate ex8.subx -o ex8
     5 #   $ bootstrap/bootstrap run ex8 abc de fghi
     6 # Expected result:
     7 #   $ echo $?
     8 #   3  # length of 'abc'
     9 #
    10 # At the start of a SubX program:
    11 #   argc: *esp
    12 #   argv[0]: *(esp+4)
    13 #   argv[1]: *(esp+8)
    14 #   ...
    15 # Locals start from esp-4 downwards.
    16
    17 == code
    18 #   instruction                     effective address                                                   register    displacement    immediate
    19 # . op          subop               mod             rm32          base        index         scale       r32
    20 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
    21
    22 Entry:
    23     # . prologue
    24     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    25     # eax = ascii-length(argv[1])
    26     # . . push args
    27     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    28     # . . call
    29     e8/call  ascii-length/disp32
    30     # . . discard args
    31     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    32
    33     # exit(eax)
    34     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
    35     e8/call  syscall_exit/disp32
    36
    37 ascii-length:  # s: (addr array byte) -> n/eax
    38     # edx = s
    39     8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/esp  4/index/none  .           2/r32/edx   4/disp8         .                 # copy *(esp+4) to edx
    40     # var result/eax = 0
    41     b8/copy-to-eax  0/imm32
    42 $ascii-length:loop:
    43     # var c/ecx = *s
    44     8a/copy-byte                    0/mod/*         2/rm32/edx    .           .             .           1/r32/CL    .               .                 # copy byte at *edx to CL
    45     # if (c == '\0') break
    46     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0/imm32/null      # compare ecx
    47     74/jump-if-=  $ascii-length:end/disp8
    48     # ++s
    49     42/increment-edx
    50     # ++result
    51     40/increment-eax
    52     # loop
    53     eb/jump  $ascii-length:loop/disp8
    54 $ascii-length:end:
    55     # return eax
    56     c3/return
    57
    58 == data
    59
    60 # . . vim:nowrap:textwidth=0