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

      1 # Null-terminated vs size-prefixed ascii strings.
      2 #
      3 # By default we create strings with a 4-byte size prefix rather than a null suffix.
      4 # However we still need null-prefixed strings when interacting with the Linux
      5 # kernel in a few places. This layer implements a function for comparing
      6 # a null-terminated 'kernel string' with a size-prefixed 'SubX string'.
      7 #
      8 # To run:
      9 #   $ bootstrap/bootstrap translate ex11.subx -o ex11
     10 #   $ bootstrap/bootstrap run ex11  # runs a series of tests
     11 #   ......  # all tests pass
     12 #
     13 # (We can't yet run the tests when given a "test" commandline argument,
     14 # because checking for it would require the function being tested! Breakage
     15 # would cause tests to not run, rather than to fail as we'd like.)
     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:  # run all tests
     23     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
     24     # exit(eax)
     25     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
     26     e8/call  syscall_exit/disp32
     27
     28 # compare a null-terminated ascii string with a more idiomatic size-prefixed byte array
     29 # reason for the name: the only place we should have null-terminated ascii strings is from commandline args
     30 kernel-string-equal?:  # s: null-terminated ascii string, benchmark: size-prefixed ascii string -> eax: boolean
     31     # pseudocode:
     32     #   n = benchmark->size
     33     #   s1 = s
     34     #   s2 = benchmark->data
     35     #   i = 0
     36     #   while i < n
     37     #     c1 = *s1
     38     #     c2 = *s2
     39     #     if (c1 == 0) return false
     40     #     if (c1 != c2) return false
     41     #     ++s1, ++s2, ++i
     42     #   return *s1 == 0
     43     #
     44     # registers:
     45     #   i: ecx
     46     #   n: edx
     47     #   s1: edi
     48     #   s2: esi
     49     #   c1: eax
     50     #   c2: ebx
     51     #
     52     # . prologue
     53     55/push-ebp
     54     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     55     # . save registers
     56     51/push-ecx
     57     52/push-edx
     58     53/push-ebx
     59     56/push-esi
     60     57/push-edi
     61     # s1/edi = s
     62     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
     63     # n/edx = benchmark->size
     64     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
     65     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           2/r32/edx   .               .                 # copy *edx to edx
     66     # s2/esi = benchmark->data
     67     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
     68     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
     69     # i/ecx = c1/eax = c2/ebx = 0
     70     b9/copy-to-ecx  0/imm32/exit
     71     b8/copy-to-eax  0/imm32
     72     bb/copy-to-ebx  0/imm32
     73 $kernel-string-equal?:loop:
     74     # if (i >= n) break
     75     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     76     7d/jump-if->=  $kernel-string-equal?:break/disp8
     77     # c1 = *s1
     78     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           0/r32/AL    .               .                 # copy byte at *edi to AL
     79     # c2 = *s2
     80     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           3/r32/BL    .               .                 # copy byte at *esi to BL
     81     # if (c1 == 0) return false
     82     3d/compare-eax-and  0/imm32/null
     83     74/jump-if-=  $kernel-string-equal?:false/disp8
     84     # if (c1 != c2) return false
     85     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
     86     75/jump-if-!=  $kernel-string-equal?:false/disp8
     87     # ++i
     88     41/increment-ecx
     89     # ++s1
     90     47/increment-edi
     91     # ++s2
     92     46/increment-esi
     93     eb/jump  $kernel-string-equal?:loop/disp8
     94 $kernel-string-equal?:break:
     95     # return *s1 == 0
     96     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           0/r32/AL    .               .                 # copy byte at *edi to AL
     97     3d/compare-eax-and  0/imm32/null
     98     75/jump-if-!=  $kernel-string-equal?:false/disp8
     99 $kernel-string-equal?:true:
    100     b8/copy-to-eax  1/imm32
    101     eb/jump  $kernel-string-equal?:end/disp8
    102 $kernel-string-equal?:false:
    103     b8/copy-to-eax  0/imm32
    104 $kernel-string-equal?:end:
    105     # . restore registers
    106     5f/pop-to-edi
    107     5e/pop-to-esi
    108     5b/pop-to-ebx
    109     5a/pop-to-edx
    110     59/pop-to-ecx
    111     # . epilogue
    112     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    113     5d/pop-to-ebp
    114     c3/return
    115
    116 # - tests
    117
    118 test-compare-null-kernel-string-with-empty-array:
    119     # eax = kernel-string-equal?(Null-kernel-string, "")
    120     # . . push args
    121     68/push  ""/imm32
    122     68/push  Null-kernel-string/imm32
    123     # . . call
    124     e8/call  kernel-string-equal?/disp32
    125     # . . discard args
    126     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    127     # check-ints-equal(eax, 1, msg)
    128     # . . push args
    129     68/push  "F - test-compare-null-kernel-string-with-empty-array"/imm32
    130     68/push  1/imm32/true
    131     50/push-eax
    132     # . . call
    133     e8/call  check-ints-equal/disp32
    134     # . . discard args
    135     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    136     c3/return
    137
    138 test-compare-null-kernel-string-with-non-empty-array:
    139     # eax = kernel-string-equal?(Null-kernel-string, "Abc")
    140     # . . push args
    141     68/push  "Abc"/imm32
    142     68/push  Null-kernel-string/imm32
    143     # . . call
    144     e8/call  kernel-string-equal?/disp32
    145     # . . discard args
    146     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    147     # check-ints-equal(eax, 0, msg)
    148     # . . push args
    149     68/push  "F - test-compare-null-kernel-string-with-non-empty-array"/imm32
    150     68/push  0/imm32/false
    151     50/push-eax
    152     # . . call
    153     e8/call  check-ints-equal/disp32
    154     # . . discard args
    155     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    156     c3/return
    157
    158 test-compare-kernel-string-with-equal-array:
    159     # eax = kernel-string-equal?(_test-Abc-kernel-string, "Abc")
    160     # . . push args
    161     68/push  "Abc"/imm32
    162     68/push  _test-Abc-kernel-string/imm32
    163     # . . call
    164     e8/call  kernel-string-equal?/disp32
    165     # . . discard args
    166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    167     # check-ints-equal(eax, 1, msg)
    168     # . . push args
    169     68/push  "F - test-compare-kernel-string-with-equal-array"/imm32
    170     68/push  1/imm32/true
    171     50/push-eax
    172     # . . call
    173     e8/call  check-ints-equal/disp32
    174     # . . discard args
    175     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    176     c3/return
    177
    178 test-compare-kernel-string-with-inequal-array:
    179     # eax = kernel-string-equal?(_test-Abc-kernel-string, "Adc")
    180     # . . push args
    181     68/push  "Adc"/imm32
    182     68/push  _test-Abc-kernel-string/imm32
    183     # . . call
    184     e8/call  kernel-string-equal?/disp32
    185     # . . discard args
    186     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    187     # check-ints-equal(eax, 0, msg)
    188     # . . push args
    189     68/push  "F - test-compare-kernel-string-with-equal-array"/imm32
    190     68/push  0/imm32/false
    191     50/push-eax
    192     # . . call
    193     e8/call  check-ints-equal/disp32
    194     # . . discard args
    195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    196     c3/return
    197
    198 test-compare-kernel-string-with-empty-array:
    199     # eax = kernel-string-equal?(_test-Abc-kernel-string, "")
    200     # . . push args
    201     68/push  ""/imm32
    202     68/push  _test-Abc-kernel-string/imm32
    203     # . . call
    204     e8/call  kernel-string-equal?/disp32
    205     # . . discard args
    206     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    207     # check-ints-equal(eax, 0, msg)
    208     # . . push args
    209     68/push  "F - test-compare-kernel-string-with-equal-array"/imm32
    210     68/push  0/imm32/false
    211     50/push-eax
    212     # . . call
    213     e8/call  check-ints-equal/disp32
    214     # . . discard args
    215     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    216     c3/return
    217
    218 test-compare-kernel-string-with-shorter-array:
    219     # eax = kernel-string-equal?(_test-Abc-kernel-string, "Ab")
    220     # . . push args
    221     68/push  "Ab"/imm32
    222     68/push  _test-Abc-kernel-string/imm32
    223     # . . call
    224     e8/call  kernel-string-equal?/disp32
    225     # . . discard args
    226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    227     # check-ints-equal(eax, 0, msg)
    228     # . . push args
    229     68/push  "F - test-compare-kernel-string-with-shorter-array"/imm32
    230     68/push  0/imm32/false
    231     50/push-eax
    232     # . . call
    233     e8/call  check-ints-equal/disp32
    234     # . . discard args
    235     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    236     c3/return
    237
    238 test-compare-kernel-string-with-longer-array:
    239     # eax = kernel-string-equal?(_test-Abc-kernel-string, "Abcd")
    240     # . . push args
    241     68/push  "Abcd"/imm32
    242     68/push  _test-Abc-kernel-string/imm32
    243     # . . call
    244     e8/call  kernel-string-equal?/disp32
    245     # . . discard args
    246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    247     # check-ints-equal(eax, 0, msg)
    248     # . . push args
    249     68/push  "F - test-compare-kernel-string-with-longer-array"/imm32
    250     68/push  0/imm32/false
    251     50/push-eax
    252     # . . call
    253     e8/call  check-ints-equal/disp32
    254     # . . discard args
    255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    256     c3/return
    257
    258 # - helpers
    259
    260 # print msg to stderr if a != b, otherwise print "."
    261 check-ints-equal:  # (a: int, b: int, msg: (addr array byte)) -> boolean
    262     # . prologue
    263     55/push-ebp
    264     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    265     # . save registers
    266     51/push-ecx
    267     53/push-ebx
    268     # load args into eax, ebx and ecx
    269     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
    270     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy *(ebp+12) to ebx
    271     # if (eax == b/ebx) print('.') and return
    272     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
    273     75/jump-if-unequal  $check-ints-equal:else/disp8
    274     # . write-stderr('.')
    275     # . . push args
    276     68/push  "."/imm32
    277     # . . call
    278     e8/call  write-stderr/disp32
    279     # . . discard args
    280     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    281     # . return
    282     eb/jump  $check-ints-equal:end/disp8
    283     # otherwise print(msg)
    284 $check-ints-equal:else:
    285     # copy msg into ecx
    286     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8       .                # copy *(ebp+16) to ecx
    287     # print(ecx)
    288     # . . push args
    289     51/push-ecx
    290     # . . call
    291     e8/call  write-stderr/disp32
    292     # . . discard args
    293     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    294     # print newline
    295     # . . push args
    296     68/push  Newline/imm32
    297     # . . call
    298     e8/call  write-stderr/disp32
    299     # . . discard args
    300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    301 $check-ints-equal:end:
    302     # . restore registers
    303     5b/pop-to-ebx
    304     59/pop-to-ecx
    305     # end
    306     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    307     5d/pop-to-ebp
    308     c3/return
    309
    310 write-stderr:  # s: (addr array byte) -> <void>
    311     # . prologue
    312     55/push-ebp
    313     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    314     # . save registers
    315     50/push-eax
    316     51/push-ecx
    317     52/push-edx
    318     53/push-ebx
    319     # syscall(write, 2/stderr, (data) s+4, (size) *s)
    320     # . . fd = 2 (stderr)
    321     bb/copy-to-ebx  2/imm32
    322     # . . x = s+4
    323     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
    324     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # add to ecx
    325     # . . size = *s
    326     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   8/disp8         .                 # copy *(ebp+8) to edx
    327     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           2/r32/edx   .               .                 # copy *edx to edx
    328     # . . syscall
    329     e8/call  syscall_write/disp32
    330     # . restore registers
    331     5b/pop-to-ebx
    332     5a/pop-to-edx
    333     59/pop-to-ecx
    334     58/pop-to-eax
    335     # . end
    336     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    337     5d/pop-to-ebp
    338     c3/return
    339
    340 == data
    341
    342 Newline:
    343     # size
    344     1/imm32
    345     # data
    346     0a/newline
    347
    348 # for kernel-string-equal tests
    349 Null-kernel-string:
    350     00/null
    351
    352 _test-Abc-kernel-string:
    353     41/A 62/b 63/c 00/null
    354
    355 # . . vim:nowrap:textwidth=0