https://github.com/akkartik/mu/blob/master/052kernel-string-equal.subx
  1 # Checking null-terminated ascii strings.
  2 #
  3 # By default we create strings as arrays of bytes, and all arrays have a 4-byte
  4 # length prefix.
  5 #
  6 # However, we sometimes need to deal with null-prefixed strings when interacting
  7 # with the Linux kernel. This layer implements a function for comparing a
  8 # null-terminated 'kernel string' with a length-prefixed 'SubX string'.
  9 #
 10 # To run (from the subx directory):
 11 #   $ ./bootstrap translate 05[0-2]*.subx -o /tmp/tmp52
 12 #   $ ./bootstrap run /tmp/tmp52  # runs a series of tests
 13 #   ......  # all tests pass
 14 #
 15 # (We can't yet run the tests when given a "test" commandline argument,
 16 # because checking for it would require the function being tested! Breakage
 17 # would cause tests to not run, rather than to fail as we'd like.)
 18 
 19 == code
 20 #   instruction                     effective address                                                   register    displacement    immediate
 21 # . op          subop               mod             rm32          base        index         scale       r32
 22 # . 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
 23 
 24 Entry:  # run all tests
 25     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 26     # syscall(exit, Num-test-failures)
 27     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
 28     b8/copy-to-eax  1/imm32/exit
 29     cd/syscall  0x80/imm8
 30 
 31 # compare a null-terminated ascii string with a more idiomatic length-prefixed byte array
 32 # reason for the name: the only place we should have null-terminated ascii strings is from commandline args
 33 kernel-string-equal?:  # s : (addr kernel-string), benchmark : (addr array byte) -> eax : boolean
 34     # pseudocode:
 35     #   n = benchmark->length
 36     #   s1 = s
 37     #   s2 = benchmark->data
 38     #   i = 0
 39     #   while (i < n)
 40     #     c1 = *s1
 41     #     c2 = *s2
 42     #     if (c1 == 0) return false
 43     #     if (c1 != c2) return false
 44     #     ++s1, ++s2, ++i
 45     #   return *s1 == 0
 46     #
 47     # registers:
 48     #   i: ecx
 49     #   n: edx
 50     #   s1: edi
 51     #   s2: esi
 52     #   c1: eax
 53     #   c2: ebx
 54     #
 55     # . prologue
 56     55/push-ebp
 57     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 58     # . save registers
 59     51/push-ecx
 60     52/push-edx
 61     53/push-ebx
 62     56/push-esi
 63     57/push-edi
 64     # var s1/edi : (addr byte) = s
 65     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
 66     # var n/edx : int = benchmark->length
 67     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
 68     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           2/r32/edx   .               .                 # copy *edx to edx
 69     # var s2/esi : (addr byte) = benchmark->data
 70     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
 71     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
 72     # var i/ecx : int = 0
 73     b9/copy-to-ecx  0/imm32/exit
 74     # var c1/eax : byte = 0
 75     b8/copy-to-eax  0/imm32
 76     # var c2/ebx : byte = 0
 77     bb/copy-to-ebx  0/imm32
 78 $kernel-string-equal?:loop:
 79     # if (i >= n) break
 80     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 81     7d/jump-if-greater-or-equal  $kernel-string-equal?:break/disp8
 82     # c1 = *s1
 83     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           0/r32/AL    .               .                 # copy byte at *edi to AL
 84     # c2 = *s2
 85     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           3/r32/BL    .               .                 # copy byte at *esi to BL
 86     # if (c1 == 0) return false
 87     3d/compare-eax-and  0/imm32
 88     74/jump-if-equal  $kernel-string-equal?:false/disp8
 89     # if (c1 != c2) return false
 90     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
 91     75/jump-if-not-equal  $kernel-string-equal?:false/disp8
 92     # ++i
 93     41/increment-ecx
 94     # ++s1
 95     47/increment-edi
 96     # ++s2
 97     46/increment-esi
 98     eb/jump  $kernel-string-equal?:loop/disp8
 99 $kernel-string-equal?:break:
100     # return *s1 == 0
101     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           0/r32/AL    .               .                 # copy byte at *edi to AL
102     3d/compare-eax-and  0/imm32
103     75/jump-if-not-equal  $kernel-string-equal?:false/disp8
104 $kernel-string-equal?:true:
105     b8/copy-to-eax  1/imm32
106     eb/jump  $kernel-string-equal?:end/disp8
107 $kernel-string-equal?:false:
108     b8/copy-to-eax  0/imm32
109 $kernel-string-equal?:end:
110     # . restore registers
111     5f/pop-to-edi
112     5e/pop-to-esi
113     5b/pop-to-ebx
114     5a/pop-to-edx
115     59/pop-to-ecx
116     # . epilogue
117     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
118     5d/pop-to-ebp
119     c3/return
120 
121 # - tests
122 
123 test-compare-null-kernel-string-with-empty-array:
124     # eax = kernel-string-equal?(Null-kernel-string, "")
125     # . . push args
126     68/push  ""/imm32
127     68/push  Null-kernel-string/imm32
128     # . . call
129     e8/call  kernel-string-equal?/disp32
130     # . . discard args
131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
132     # check-ints-equal(eax, 1, msg)
133     # . . push args
134     68/push  "F - test-compare-null-kernel-string-with-empty-array"/imm32
135     68/push  1/imm32/true
136     50/push-eax
137     # . . call
138     e8/call  check-ints-equal/disp32
139     # . . discard args
140     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
141     c3/return
142 
143 test-compare-null-kernel-string-with-non-empty-array:
144     # eax = kernel-string-equal?(Null-kernel-string, "Abc")
145     # . . push args
146     68/push  "Abc"/imm32
147     68/push  Null-kernel-string/imm32
148     # . . call
149     e8/call  kernel-string-equal?/disp32
150     # . . discard args
151     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
152     # check-ints-equal(eax, 0, msg)
153     # . . push args
154     68/push  "F - test-compare-null-kernel-string-with-non-empty-array"/imm32
155     68/push  0/imm32/false
156     50/push-eax
157     # . . call
158     e8/call  check-ints-equal/disp32
159     # . . discard args
160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
161     c3/return
162 
163 test-compare-kernel-string-with-equal-array:
164     # eax = kernel-string-equal?(_test-Abc-kernel-string, "Abc")
165     # . . push args
166     68/push  "Abc"/imm32
167     68/push  _test-Abc-kernel-string/imm32
168     # . . call
169     e8/call  kernel-string-equal?/disp32
170     # . . discard args
171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
172     # check-ints-equal(eax, 1, msg)
173     # . . push args
174     68/push  "F - test-compare-kernel-string-with-equal-array"/imm32
175     68/push  1/imm32/true
176     50/push-eax
177     # . . call
178     e8/call  check-ints-equal/disp32
179     # . . discard args
180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
181     c3/return
182 
183 test-compare-kernel-string-with-inequal-array:
184     # eax = kernel-string-equal?(_test-Abc-kernel-string, "Adc")
185     # . . push args
186     68/push  "Adc"/imm32
187     68/push  _test-Abc-kernel-string/imm32
188     # . . call
189     e8/call  kernel-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, 0, msg)
193     # . . push args
194     68/push  "F - test-compare-kernel-string-with-equal-array"/imm32
195     68/push  0/imm32/false
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     c3/return
202 
203 test-compare-kernel-string-with-empty-array:
204     # eax = kernel-string-equal?(_test-Abc-kernel-string, "")
205     # . . push args
206     68/push  ""/imm32
207     68/push  _test-Abc-kernel-string/imm32
208     # . . call
209     e8/call  kernel-string-equal?/disp32
210     # . . discard args
211     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
212     # check-ints-equal(eax, 0, msg)
213     # . . push args
214     68/push  "F - test-compare-kernel-string-with-equal-array"/imm32
215     68/push  0/imm32/false
216     50/push-eax
217     # . . call
218     e8/call  check-ints-equal/disp32
219     # . . discard args
220     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
221     c3/return
222 
223 test-compare-kernel-string-with-shorter-array:
224     # eax = kernel-string-equal?(_test-Abc-kernel-string, "Ab")
225     # . . push args
226     68/push  "Ab"/imm32
227     68/push  _test-Abc-kernel-string/imm32
228     # . . call
229     e8/call  kernel-string-equal?/disp32
230     # . . discard args
231     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
232     # check-ints-equal(eax, 0, msg)
233     # . . push args
234     68/push  "F - test-compare-kernel-string-with-shorter-array"/imm32
235     68/push  0/imm32/false
236     50/push-eax
237     # . . call
238     e8/call  check-ints-equal/disp32
239     # . . discard args
240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
241     c3/return
242 
243 test-compare-kernel-string-with-longer-array:
244     # eax = kernel-string-equal?(_test-Abc-kernel-string, "Abcd")
245     # . . push args
246     68/push  "Abcd"/imm32
247     68/push  _test-Abc-kernel-string/imm32
248     # . . call
249     e8/call  kernel-string-equal?/disp32
250     # . . discard args
251     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
252     # check-ints-equal(eax, 0, msg)
253     # . . push args
254     68/push  "F - test-compare-kernel-string-with-longer-array"/imm32
255     68/push  0/imm32/false
256     50/push-eax
257     # . . call
258     e8/call  check-ints-equal/disp32
259     # . . discard args
260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
261     c3/return
262 
263 == data
264 
265 Null-kernel-string:  # (addr kernel-string)
266     00/null
267 
268 _test-Abc-kernel-string:  # (addr kernel-string)
269     41/A 62/b 63/c 00/null
270 
271 # . . vim:nowrap:textwidth=0