https://github.com/akkartik/mu/blob/master/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     #   if (s->length != benchmark->length) return false
 19     #   currs = s->data
 20     #   currb = benchmark->data
 21     #   maxs = s->data + s->length
 22     #   while currs < maxs
 23     #     c1 = *currs
 24     #     c2 = *currb
 25     #     if (c1 != c2) return false
 26     #     ++currs, ++currb
 27     #   return true
 28     #
 29     # registers:
 30     #   currs: esi
 31     #   maxs: ecx
 32     #   currb: edi
 33     #   c1: eax
 34     #   c2: ebx
 35     #
 36     # . prologue
 37     55/push-ebp
 38     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 39     # . save registers
 40     51/push-ecx
 41     52/push-edx
 42     56/push-esi
 43     57/push-edi
 44     # esi = s
 45     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 46     # edi = benchmark
 47     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
 48     # ecx = s->length
 49     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
 50 $string-equal?:lengths:
 51     # if (ecx != benchmark->length) return false
 52     39/compare                      0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # compare *edi and ecx
 53     75/jump-if-not-equal  $string-equal?:false/disp8
 54     # currs/esi = s->data
 55     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
 56     # maxs/ecx = s->data + s->length
 57     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           6/r32/esi   .               .                 # add esi to ecx
 58     # currb/edi = benchmark->data
 59     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
 60     # c1/eax = c2/edx = 0
 61     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 62     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
 63 $string-equal?:loop:
 64     # if (currs >= maxs) return true
 65     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi with ecx
 66     73/jump-if-greater-or-equal-unsigned  $string-equal?:true/disp8
 67     # c1 = *currs
 68     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
 69     # c2 = *currb
 70     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           2/r32/DL    .               .                 # copy byte at *edi to DL
 71     # if (c1 != c2) return false
 72     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax and edx
 73     75/jump-if-not-equal  $string-equal?:false/disp8
 74     # ++currs
 75     46/increment-esi
 76     # ++currb
 77     47/increment-edi
 78     eb/jump  $string-equal?:loop/disp8
 79 $string-equal?:true:
 80     b8/copy-to-eax  1/imm32
 81     eb/jump  $string-equal?:end/disp8
 82 $string-equal?:false:
 83     b8/copy-to-eax  0/imm32
 84 $string-equal?:end:
 85     # . restore registers
 86     5f/pop-to-edi
 87     5e/pop-to-esi
 88     5a/pop-to-edx
 89     59/pop-to-ecx
 90     # . epilogue
 91     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 92     5d/pop-to-ebp
 93     c3/return
 94 
 95 # - tests
 96 
 97 test-compare-empty-with-empty-string:
 98     # eax = string-equal?("", "")
 99     # . . push args
100     68/push  ""/imm32
101     68/push  ""/imm32
102     # . . call
103     e8/call  string-equal?/disp32
104     # . . discard args
105     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
106     # check-ints-equal(eax, 1, msg)
107     # . . push args
108     68/push  "F - test-compare-empty-with-empty-string"/imm32
109     68/push  1/imm32/true
110     50/push-eax
111     # . . call
112     e8/call  check-ints-equal/disp32
113     # . . discard args
114     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
115     c3/return
116 
117 test-compare-empty-with-non-empty-string:  # also checks length-mismatch code path
118     # eax = string-equal?("", "Abc")
119     # . . push args
120     68/push  "Abc"/imm32
121     68/push  ""/imm32
122     # . . call
123     e8/call  string-equal?/disp32
124     # . . discard args
125     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
126     # check-ints-equal(eax, 0, msg)
127     # . . push args
128     68/push  "F - test-compare-empty-with-non-empty-string"/imm32
129     68/push  0/imm32/false
130     50/push-eax
131     # . . call
132     e8/call  check-ints-equal/disp32
133     # . . discard args
134     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
135     c3/return
136 
137 test-compare-equal-strings:
138     # eax = string-equal?("Abc", "Abc")
139     # . . push args
140     68/push  "Abc"/imm32
141     68/push  "Abc"/imm32
142     # . . call
143     e8/call  string-equal?/disp32
144     # . . discard args
145     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
146     # check-ints-equal(eax, 1, msg)
147     # . . push args
148     68/push  "F - test-compare-equal-strings"/imm32
149     68/push  1/imm32/true
150     50/push-eax
151     # . . call
152     e8/call  check-ints-equal/disp32
153     # . . discard args
154     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
155     c3/return
156 
157 test-compare-inequal-strings-equal-lengths:
158     # eax = string-equal?("Abc", "Adc")
159     # . . push args
160     68/push  "Adc"/imm32
161     68/push  "Abc"/imm32
162     # . . call
163     e8/call  string-equal?/disp32
164     # . . discard args
165     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
166     # check-ints-equal(eax, 0, msg)
167     # . . push args
168     68/push  "F - test-compare-inequal-strings-equal-lengths"/imm32
169     68/push  0/imm32/false
170     50/push-eax
171     # . . call
172     e8/call  check-ints-equal/disp32
173     # . . discard args
174     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
175     c3/return
176 
177 # helper for later tests
178 check-string-equal:  # s : (address string), expected : (address string), msg : (address string)
179     # . prologue
180     55/push-ebp
181     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
182     # . save registers
183     50/push-eax
184     # eax = string-equal?(s, expected)
185     # . . push args
186     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
187     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
188     # . . call
189     e8/call  string-equal?/disp32
190     # . . discard args
191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
192     # check-ints-equal(eax, 1, msg)
193     # . . push args
194     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
195     68/push  1/imm32
196     50/push-eax
197     # . . call
198     e8/call  check-ints-equal/disp32
199     # . . discard args
200     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
201 $check-string-equal:end:
202     # . restore registers
203     58/pop-to-eax
204     # . epilogue
205     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
206     5d/pop-to-ebp
207     c3/return
208 
209 # test the helper
210 test-check-string-equal:
211     # check-string-equal?("Abc", "Abc")
212     # . . push args
213     68/push  "Abc"/imm32
214     68/push  "Abc"/imm32
215     # . . call
216     e8/call  check-string-equal/disp32
217     # . . discard args
218     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
219     # check-ints-equal(eax, 1, msg)
220     # . . push args
221     68/push  "F - test-check-string-equal"/imm32
222     68/push  0/imm32/false
223     50/push-eax
224     # . . call
225     e8/call  check-ints-equal/disp32
226     # . . discard args
227     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
228     c3/return
229 
230 # . . vim:nowrap:textwidth=0