https://github.com/akkartik/mu/blob/master/subx/054string-equal.subx

      1 # Comparing 'regular' length-prefixed strings.
      2
      3 == code
      4 #   instruction                     effective address                                                   register    displacement    immediate
      5 # . op          subop               mod             rm32          base        index         scale       r32
      6 # . 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
      7
      8 Entry:  # run all tests
      9 #?     e8/call test-compare-equal-strings/disp32
     10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
     11     # syscall(exit, Num-test-failures)
     12     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
     13     b8/copy-to-EAX  1/imm32/exit
     14     cd/syscall  0x80/imm8
     15
     16 string-equal?:  # s : (address string), benchmark : (address string) -> EAX : boolean
     17     # pseudocode:
     18     #   lens = s->length
     19     #   if (lens != benchmark->length) return false
     20     #   i = 0
     21     #   currs = s->data
     22     #   currb = benchmark->data
     23     #   while i < s->length
     24     #     c1 = *currs
     25     #     c2 = *currb
     26     #     if (c1 != c2) return false
     27     #     ++i, ++currs, ++currb
     28     #   return true
     29     #
     30     # registers:
     31     #   i: ECX
     32     #   lens: EDX
     33     #   currs: ESI
     34     #   currb: EDI
     35     #   c1: EAX
     36     #   c2: EBX
     37     #
     38     # . prolog
     39     55/push-EBP
     40     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     41     # . save registers
     42     51/push-ECX
     43     52/push-EDX
     44     53/push-EBX
     45     56/push-ESI
     46     57/push-EDI
     47     # ESI = s
     48     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
     49     # EDI = benchmark
     50     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
     51     # lens/EDX = s->length
     52     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
     53 $string-equal?:lengths:
     54     # if (lens != benchmark->length) return false
     55     39/compare                      0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare *EDI and EDX
     56     75/jump-if-not-equal  $string-equal?:false/disp8
     57     # currs/ESI = s->data
     58     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               4/imm32           # add to ESI
     59     # currb/EDI = benchmark->data
     60     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm32           # add to EDI
     61     # i/ECX = c1/EAX = c2/EBX = 0
     62     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
     63     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
     64     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
     65 $string-equal?:loop:
     66     # if (i >= lens) return true
     67     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
     68     7d/jump-if-greater-or-equal  $string-equal?:true/disp8
     69     # c1 = *currs
     70     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
     71     # c2 = *currb
     72     8a/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at *EDI to BL
     73     # if (c1 != c2) return false
     74     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX and EBX
     75     75/jump-if-not-equal  $string-equal?:false/disp8
     76     # ++i
     77     41/increment-ECX
     78     # ++currs
     79     46/increment-ESI
     80     # ++currb
     81     47/increment-EDI
     82     eb/jump  $string-equal?:loop/disp8
     83 $string-equal?:true:
     84     b8/copy-to-EAX  1/imm32
     85     eb/jump  $string-equal?:end/disp8
     86 $string-equal?:false:
     87     b8/copy-to-EAX  0/imm32
     88 $string-equal?:end:
     89     # . restore registers
     90     5f/pop-to-EDI
     91     5e/pop-to-ESI
     92     5b/pop-to-EBX
     93     5a/pop-to-EDX
     94     59/pop-to-ECX
     95     # . epilog
     96     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     97     5d/pop-to-EBP
     98     c3/return
     99
    100 # - tests
    101
    102 test-compare-empty-with-empty-string:
    103     # EAX = string-equal?("", "")
    104     # . . push args
    105     68/push  ""/imm32
    106     68/push  ""/imm32
    107     # . . call
    108     e8/call  string-equal?/disp32
    109     # . . discard args
    110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    111     # check-ints-equal(EAX, 1, msg)
    112     # . . push args
    113     68/push  "F - test-compare-empty-with-empty-string"/imm32
    114     68/push  1/imm32/true
    115     50/push-EAX
    116     # . . call
    117     e8/call  check-ints-equal/disp32
    118     # . . discard args
    119     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    120     c3/return
    121
    122 test-compare-empty-with-non-empty-string:  # also checks length-mismatch code path
    123     # EAX = string-equal?("", "Abc")
    124     # . . push args
    125     68/push  "Abc"/imm32
    126     68/push  ""/imm32
    127     # . . call
    128     e8/call  string-equal?/disp32
    129     # . . discard args
    130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    131     # check-ints-equal(EAX, 0, msg)
    132     # . . push args
    133     68/push  "F - test-compare-empty-with-non-empty-string"/imm32
    134     68/push  0/imm32/false
    135     50/push-EAX
    136     # . . call
    137     e8/call  check-ints-equal/disp32
    138     # . . discard args
    139     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    140     c3/return
    141
    142 test-compare-equal-strings:
    143     # EAX = string-equal?("Abc", "Abc")
    144     # . . push args
    145     68/push  "Abc"/imm32
    146     68/push  "Abc"/imm32
    147     # . . call
    148     e8/call  string-equal?/disp32
    149     # . . discard args
    150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    151     # check-ints-equal(EAX, 1, msg)
    152     # . . push args
    153     68/push  "F - test-compare-equal-strings"/imm32
    154     68/push  1/imm32/true
    155     50/push-EAX
    156     # . . call
    157     e8/call  check-ints-equal/disp32
    158     # . . discard args
    159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    160     c3/return
    161
    162 test-compare-inequal-strings-equal-lengths:
    163     # EAX = string-equal?("Abc", "Adc")
    164     # . . push args
    165     68/push  "Adc"/imm32
    166     68/push  "Abc"/imm32
    167     # . . call
    168     e8/call  string-equal?/disp32
    169     # . . discard args
    170     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    171     # check-ints-equal(EAX, 0, msg)
    172     # . . push args
    173     68/push  "F - test-compare-inequal-strings-equal-lengths"/imm32
    174     68/push  0/imm32/false
    175     50/push-EAX
    176     # . . call
    177     e8/call  check-ints-equal/disp32
    178     # . . discard args
    179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    180     c3/return
    181
    182 # . . vim:nowrap:textwidth=0