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