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 : (addr array byte), benchmark : (addr array byte) -> 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     # var currs/esi : (addr byte) = s->data
 55     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
 56     # var maxs/ecx : (addr byte) = &s->data[s->length]
 57     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           6/r32/esi   .               .                 # add esi to ecx
 58     # var currb/edi : (addr byte) = benchmark->data
 59     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
 60     # var c1/eax : byte = 0
 61     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 62     # var c2/edx : byte = 0
 63     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
 64 $string-equal?:loop:
 65     # if (currs >= maxs) return true
 66     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi with ecx
 67     73/jump-if-greater-or-equal-unsigned  $string-equal?:true/disp8
 68     # c1 = *currs
 69     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
 70     # c2 = *currb
 71     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           2/r32/DL    .               .                 # copy byte at *edi to DL
 72     # if (c1 != c2) return false
 73     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax and edx
 74     75/jump-if-not-equal  $string-equal?:false/disp8
 75     # ++currs
 76     46/increment-esi
 77     # ++currb
 78     47/increment-edi
 79     eb/jump  $string-equal?:loop/disp8
 80 $string-equal?:true:
 81     b8/copy-to-eax  1/imm32
 82     eb/jump  $string-equal?:end/disp8
 83 $string-equal?:false:
 84     b8/copy-to-eax  0/imm32
 85 $string-equal?:end:
 86     # . restore registers
 87     5f/pop-to-edi
 88     5e/pop-to-esi
 89     5a/pop-to-edx
 90     59/pop-to-ecx
 91     # . epilogue
 92     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 93     5d/pop-to-ebp
 94     c3/return
 95 
 96 # - tests
 97 
 98 test-compare-empty-with-empty-string:
 99     # eax = string-equal?("", "")
100     # . . push args
101     68/push  ""/imm32
102     68/push  ""/imm32
103     # . . call
104     e8/call  string-equal?/disp32
105     # . . discard args
106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
107     # check-ints-equal(eax, 1, msg)
108     # . . push args
109     68/push  "F - test-compare-empty-with-empty-string"/imm32
110     68/push  1/imm32/true
111     50/push-eax
112     # . . call
113     e8/call  check-ints-equal/disp32
114     # . . discard args
115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
116     c3/return
117 
118 test-compare-empty-with-non-empty-string:  # also checks length-mismatch code path
119     # eax = string-equal?("", "Abc")
120     # . . push args
121     68/push  "Abc"/imm32
122     68/push  ""/imm32
123     # . . call
124     e8/call  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, 0, msg)
128     # . . push args
129     68/push  "F - test-compare-empty-with-non-empty-string"/imm32
130     68/push  0/imm32/false
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-equal-strings:
139     # eax = string-equal?("Abc", "Abc")
140     # . . push args
141     68/push  "Abc"/imm32
142     68/push  "Abc"/imm32
143     # . . call
144     e8/call  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, 1, msg)
148     # . . push args
149     68/push  "F - test-compare-equal-strings"/imm32
150     68/push  1/imm32/true
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-inequal-strings-equal-lengths:
159     # eax = string-equal?("Abc", "Adc")
160     # . . push args
161     68/push  "Adc"/imm32
162     68/push  "Abc"/imm32
163     # . . call
164     e8/call  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, 0, msg)
168     # . . push args
169     68/push  "F - test-compare-inequal-strings-equal-lengths"/imm32
170     68/push  0/imm32/false
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 # helper for later tests
179 check-strings-equal:  # s : (addr array byte), expected : (addr array byte), msg : (addr array byte)
180     # . prologue
181     55/push-ebp
182     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
183     # . save registers
184     50/push-eax
185     # var eax : boolean = string-equal?(s, expected)
186     # . . push args
187     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
188     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
189     # . . call
190     e8/call  string-equal?/disp32
191     # . . discard args
192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
193     # check-ints-equal(eax, 1, msg)
194     # . . push args
195     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
196     68/push  1/imm32
197     50/push-eax
198     # . . call
199     e8/call  check-ints-equal/disp32
200     # . . discard args
201     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
202 $check-strings-equal:end:
203     # . restore registers
204     58/pop-to-eax
205     # . epilogue
206     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
207     5d/pop-to-ebp
208     c3/return
209 
210 # test the helper
211 test-check-strings-equal:
212     # check-strings-equal?("Abc", "Abc")
213     # . . push args
214     68/push  "Abc"/imm32
215     68/push  "Abc"/imm32
216     # . . call
217     e8/call  check-strings-equal/disp32
218     # . . discard args
219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
220     c3/return
221 
222 # . . vim:nowrap:textwidth=0