https://github.com/akkartik/mu/blob/master/subx/072slice.subx
  1 # new data structure: a slice is an open interval of addresses [start, end)
  2 # that includes 'start' but not 'end'
  3 
  4 == code
  5 #   instruction                     effective address                                                   register    displacement    immediate
  6 # . op          subop               mod             rm32          base        index         scale       r32
  7 # . 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
  8 
  9 # main:
 10 #?     e8/call test-slice-starts-with-fails/disp32
 11 #?     e8/call test-slice-starts-with-single-character/disp32
 12     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 13     # syscall(exit, Num-test-failures)
 14     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 15     b8/copy-to-EAX  1/imm32/exit
 16     cd/syscall  0x80/imm8
 17 
 18 slice-empty?:  # s : (address slice) -> EAX : boolean
 19     # . prolog
 20     55/push-EBP
 21     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 22     # . save registers
 23     51/push-ECX
 24     # ECX = s
 25     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
 26     # if (s->start == s->end) return true
 27     # . EAX = s->start
 28     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
 29     # . compare EAX with s->end
 30     39/compare                      1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # compare EAX and *(ECX+4)
 31     b8/copy-to-EAX  1/imm32/true
 32     74/jump-if-equal  $slice-empty?:end/disp8
 33     b8/copy-to-EAX  0/imm32/false
 34 $slice-empty?:end:
 35     # . restore registers
 36     59/pop-to-ECX
 37     # . epilog
 38     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 39     5d/pop-to-EBP
 40     c3/return
 41 
 42 test-slice-empty-true:
 43     # . prolog
 44     55/push-EBP
 45     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 46     # var slice/ECX = {34, 34}
 47     68/push  34/imm32/end
 48     68/push  34/imm32/start
 49     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 50     # slice-empty?(slice)
 51     # . . push args
 52     51/push-ECX
 53     # . . call
 54     e8/call  slice-empty?/disp32
 55     # . . discard args
 56     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 57     # check-ints-equal(EAX, 1, msg)
 58     # . . push args
 59     68/push  "F - test-slice-empty-true"/imm32
 60     68/push  1/imm32
 61     50/push-EAX
 62     # . . call
 63     e8/call  check-ints-equal/disp32
 64     # . . discard args
 65     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 66     # . epilog
 67     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 68     5d/pop-to-EBP
 69     c3/return
 70 
 71 test-slice-empty-false:
 72     # . prolog
 73     55/push-EBP
 74     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 75     # var slice/ECX = {34, 23}
 76     68/push  23/imm32/end
 77     68/push  34/imm32/start
 78     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 79     # slice-empty?(slice)
 80     # . . push args
 81     51/push-ECX
 82     # . . call
 83     e8/call  slice-empty?/disp32
 84     # . . discard args
 85     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 86     # check-ints-equal(EAX, 0, msg)
 87     # . . push args
 88     68/push  "F - test-slice-empty-false"/imm32
 89     68/push  0/imm32
 90     50/push-EAX
 91     # . . call
 92     e8/call  check-ints-equal/disp32
 93     # . . discard args
 94     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 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 slice-equal?:  # s : (address slice), p : (address string) -> EAX : boolean
101     # pseudocode:
102     #   currs = s->start
103     #   maxs = s->end
104     #   if (maxs - currs != p->length) return false
105     #   currp = p->data
106     #   while (currs < maxs)
107     #     if (*currs != *currp) return false
108     #     ++currs
109     #     ++currp
110     #   return true
111     #
112     # registers:
113     #   currs: EDX
114     #   maxs: ESI
115     #   currp: EBX
116     #   *currs: EAX
117     #   *currp: ECX
118     #
119     # . prolog
120     55/push-EBP
121     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
122     # . save registers
123     51/push-ECX
124     52/push-EDX
125     53/push-EBX
126     56/push-ESI
127     # ESI = s
128     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
129     # currs/EDX = s->start
130     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
131     # maxs/ESI = s->end
132     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
133     # EBX = p
134     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
135     # EAX = maxs - currs
136     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy ESI to EAX
137     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # subtract EDX from EAX
138     # if (EAX != p->length) return false
139     39/compare                      0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # compare *EBX and EAX
140     75/jump-if-not-equal  $slice-equal?:false/disp8
141     # currp/EBX = p->data
142     81          0/subop/add         3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm32           # add to EBX
143     # EAX = ECX = 0
144     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
145     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
146 $slice-equal?:loop:
147     # if (currs >= maxs) return true
148     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX and ESI
149     7d/jump-if-greater-or-equal  $slice-equal?:true/disp8
150     # AL = *currp
151     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
152     # CL = *currs
153     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
154     # if (EAX != ECX) return false
155     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX and ECX
156     75/jump-if-not-equal  $slice-equal?:false/disp8
157     # ++currp
158     43/increment-EBX
159     # ++currs
160     42/increment-EDX
161     eb/jump $slice-equal?:loop/disp8
162 $slice-equal?:false:
163     b8/copy-to-EAX  0/imm32
164     eb/jump  $slice-equal?:end/disp8
165 $slice-equal?:true:
166     b8/copy-to-EAX  1/imm32
167 $slice-equal?:end:
168     # . restore registers
169     5e/pop-to-ESI
170     5b/pop-to-EBX
171     5a/pop-to-EDX
172     59/pop-to-ECX
173     # . epilog
174     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
175     5d/pop-to-EBP
176     c3/return
177 
178 test-slice-equal:
179     # - slice-equal?(slice("Abc"), "Abc") == 1
180     # . prolog
181     55/push-EBP
182     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
183     # var slice/ECX
184     68/push  _test-slice-data-3/imm32/end
185     68/push  _test-slice-data-0/imm32/start
186     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
187     # EAX = slice-equal?(ECX, "Abc")
188     # . . push args
189     68/push  "Abc"/imm32
190     51/push-ECX
191     # . . call
192     e8/call  slice-equal?/disp32
193     # . . discard args
194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
195     # check-ints-equal(EAX, 1, msg)
196     # . . push args
197     68/push  "F - test-slice-equal"/imm32
198     68/push  1/imm32
199     50/push-EAX
200     # . . call
201     e8/call  check-ints-equal/disp32
202     # . . discard args
203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
204     # . epilog
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-slice-equal-false:
210     # - slice-equal?(slice("bcd"), "Abc") == 0
211     # . prolog
212     55/push-EBP
213     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
214     # var slice/ECX
215     68/push  _test-slice-data-4/imm32/end
216     68/push  _test-slice-data-1/imm32/start
217     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
218     # EAX = slice-equal?(ECX, "Abc")
219     # . . push args
220     68/push  "Abc"/imm32
221     51/push-ECX
222     # . . call
223     e8/call  slice-equal?/disp32
224     # . . discard args
225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
226     # check-ints-equal(EAX, 0, msg)
227     # . . push args
228     68/push  "F - test-slice-equal-false"/imm32
229     68/push  0/imm32
230     50/push-EAX
231     # . . call
232     e8/call  check-ints-equal/disp32
233     # . . discard args
234     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
235     # . epilog
236     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
237     5d/pop-to-EBP
238     c3/return
239 
240 test-slice-equal-too-long:
241     # - slice-equal?(slice("Abcd"), "Abc") == 0
242     # . prolog
243     55/push-EBP
244     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
245     # var slice/ECX
246     68/push  _test-slice-data-4/imm32/end
247     68/push  _test-slice-data-0/imm32/start
248     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
249     # EAX = slice-equal?(ECX, "Abc")
250     # . . push args
251     68/push  "Abc"/imm32
252     51/push-ECX
253     # . . call
254     e8/call  slice-equal?/disp32
255     # . . discard args
256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
257     # check-ints-equal(EAX, 0, msg)
258     # . . push args
259     68/push  "F - test-slice-equal-too-long"/imm32
260     68/push  0/imm32
261     50/push-EAX
262     # . . call
263     e8/call  check-ints-equal/disp32
264     # . . discard args
265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
266     # . epilog
267     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
268     5d/pop-to-EBP
269     c3/return
270 
271 test-slice-equal-too-short:
272     # - slice-equal?(slice("A"), "Abc") == 0
273     # . prolog
274     55/push-EBP
275     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
276     # var slice/ECX
277     68/push  _test-slice-data-1/imm32/end
278     68/push  _test-slice-data-0/imm32/start
279     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
280     # EAX = slice-equal?(ECX, "Abc")
281     # . . push args
282     68/push  "Abc"/imm32
283     51/push-ECX
284     # . . call
285     e8/call  slice-equal?/disp32
286     # . . discard args
287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
288     # check-ints-equal(EAX, 0, msg)
289     # . . push args
290     68/push  "F - test-slice-equal-too-short"/imm32
291     68/push  0/imm32
292     50/push-EAX
293     # . . call
294     e8/call  check-ints-equal/disp32
295     # . . discard args
296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
297     # . epilog
298     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
299     5d/pop-to-EBP
300     c3/return
301 
302 test-slice-equal-empty:
303     # - slice-equal?(slice(""), "Abc") == 0
304     # . prolog
305     55/push-EBP
306     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
307     # var slice/ECX
308     68/push  _test-slice-data-0/imm32/end
309     68/push  _test-slice-data-0/imm32/start
310     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
311     # EAX = slice-equal?(ECX, "Abc")
312     # . . push args
313     68/push  "Abc"/imm32
314     51/push-ECX
315     # . . call
316     e8/call  slice-equal?/disp32
317     # . . discard args
318     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
319     # check-ints-equal(EAX, 0, msg)
320     # . . push args
321     68/push  "F - test-slice-equal-empty"/imm32
322     68/push  0/imm32
323     50/push-EAX
324     # . . call
325     e8/call  check-ints-equal/disp32
326     # . . discard args
327     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
328     # . epilog
329     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
330     5d/pop-to-EBP
331     c3/return
332 
333 test-slice-equal-with-empty:
334     # - slice-equal?(slice("Ab"), "") == 0
335     # . prolog
336     55/push-EBP
337     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
338     # var slice/ECX
339     68/push  _test-slice-data-2/imm32/end
340     68/push  _test-slice-data-0/imm32/start
341     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
342     # EAX = slice-equal?(ECX, "")
343     # . . push args
344     68/push  ""/imm32
345     51/push-ECX
346     # . . call
347     e8/call  slice-equal?/disp32
348     # . . discard args
349     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
350     # check-ints-equal(EAX, 0, msg)
351     # . . push args
352     68/push  "F - test-slice-equal-with-empty"/imm32
353     68/push  0/imm32
354     50/push-EAX
355     # . . call
356     e8/call  check-ints-equal/disp32
357     # . . discard args
358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
359     # . epilog
360     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
361     5d/pop-to-EBP
362     c3/return
363 
364 test-slice-equal-empty-with-empty:
365     # - slice-equal?(slice(""), "") == 1
366     # . prolog
367     55/push-EBP
368     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
369     # var slice/ECX
370     68/push  _test-slice-data-0/imm32/end
371     68/push  _test-slice-data-0/imm32/start
372     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
373     # EAX = slice-equal?(ECX, "")
374     # . . push args
375     68/push  ""/imm32
376     51/push-ECX
377     # . . call
378     e8/call  slice-equal?/disp32
379     # . . discard args
380     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
381     # check-ints-equal(EAX, 1, msg)
382     # . . push args
383     68/push  "F - test-slice-equal-empty-with-empty"/imm32
384     68/push  1/imm32
385     50/push-EAX
386     # . . call
387     e8/call  check-ints-equal/disp32
388     # . . discard args
389     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
390     # . epilog
391     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
392     5d/pop-to-EBP
393     c3/return
394 
395 slice-starts-with?:  # s : (address slice), head : (address string) -> EAX : boolean
396     # pseudocode
397     #   lenh = head->length
398     #   if (lenh > s->end - s->start) return false
399     #   i = 0
400     #   currs = s->start
401     #   currp = head->data
402     #   while i < lenh
403     #     if (*currs != *currh) return false
404     #     ++i
405     #     ++currs
406     #     ++currh
407     #   return true
408     #
409     # registers:
410     #   currs: ESI
411     #   currh: EDI
412     #   *currs: EAX
413     #   *currh: EBX
414     #   i: ECX
415     #   lenh: EDX
416     #
417     # . prolog
418     55/push-EBP
419     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
420     # . save registers
421     51/push-ECX
422     52/push-EDX
423     53/push-EBX
424     56/push-ESI
425     57/push-EDI
426     # ESI = s
427     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
428     # ECX = s->end - s->start
429     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
430     2b/subtract                     0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # subtract *ESI from ECX
431     # EDI = head
432     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
433     # lenh/EDX = head->length
434     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy *EDI to EDX
435     # if (lenh > s->end - s->start) return false
436     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           1/r32/ECX   .               .                 # compare EDX with ECX
437     7f/jump-if-greater  $slice-starts-with?:false/disp8
438     # currs/ESI = s->start
439     8b/subtract                     0/mod/indirect  6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # copy *ESI to ESI
440     # currh/EDI = head->data
441     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm32           # add to EDI
442     # i/ECX = 0
443     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
444     # EAX = EBX = 0
445     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
446     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
447 $slice-starts-with?:loop:
448     # if (i >= lenh) return true
449     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
450     7d/jump-if-greater-or-equal  $slice-starts-with?:true/disp8
451     # AL = *currs
452     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
453     # BL = *currh
454     8a/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at *EDI to BL
455     # if (*currs != *currh) return false
456     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX and EBX
457     75/jump-if-not-equal  $slice-starts-with?:false/disp8
458     # ++i
459     41/increment-ECX
460     # ++currs
461     46/increment-ESI
462     # ++currh
463     47/increment-EDI
464     eb/jump $slice-starts-with?:loop/disp8
465 $slice-starts-with?:true:
466     b8/copy-to-EAX  1/imm32
467     eb/jump  $slice-starts-with?:end/disp8
468 $slice-starts-with?:false:
469     b8/copy-to-EAX  0/imm32
470 $slice-starts-with?:end:
471     # . restore registers
472     5f/pop-to-EDI
473     5e/pop-to-ESI
474     5b/pop-to-EBX
475     5a/pop-to-EDX
476     59/pop-to-ECX
477     # . epilog
478     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
479     5d/pop-to-EBP
480     c3/return
481 
482 test-slice-starts-with-single-character:
483     # - slice-starts-with?(slice("Abc"), "A") == 1
484     # . prolog
485     55/push-EBP
486     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
487     # var slice/ECX
488     68/push  _test-slice-data-3/imm32/end
489     68/push  _test-slice-data-0/imm32/start
490     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
491     # EAX = slice-starts-with?(ECX, "A")
492     # . . push args
493     68/push  "A"/imm32
494     51/push-ECX
495     # . . call
496     e8/call  slice-starts-with?/disp32
497     # . . discard args
498     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
499     # check-ints-equal(EAX, 1, msg)
500     # . . push args
501     68/push  "F - test-slice-starts-with-single-character"/imm32
502     68/push  1/imm32
503     50/push-EAX
504     # . . call
505     e8/call  check-ints-equal/disp32
506     # . . discard args
507     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
508     # . epilog
509     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
510     5d/pop-to-EBP
511     c3/return
512 
513 test-slice-starts-with-empty-string:
514     # - slice-starts-with?(slice("Abc"), "") == 1
515     # . prolog
516     55/push-EBP
517     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
518     # var slice/ECX
519     68/push  _test-slice-data-3/imm32/end
520     68/push  _test-slice-data-0/imm32/start
521     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
522     # EAX = slice-starts-with?(ECX, "")
523     # . . push args
524     68/push  ""/imm32
525     51/push-ECX
526     # . . call
527     e8/call  slice-starts-with?/disp32
528     # . . discard args
529     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
530     # check-ints-equal(EAX, 1, msg)
531     # . . push args
532     68/push  "F - test-slice-starts-with-empty-string"/imm32
533     68/push  1/imm32
534     50/push-EAX
535     # . . call
536     e8/call  check-ints-equal/disp32
537     # . . discard args
538     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
539     # . epilog
540     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
541     5d/pop-to-EBP
542     c3/return
543 
544 test-slice-starts-with-multiple-characters:
545     # - slice-starts-with?(slice("Abc"), "Ab") == 1
546     # . prolog
547     55/push-EBP
548     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
549     # var slice/ECX
550     68/push  _test-slice-data-3/imm32/end
551     68/push  _test-slice-data-0/imm32/start
552     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
553     # EAX = slice-starts-with?(ECX, "Ab")
554     # . . push args
555     68/push  "Ab"/imm32
556     51/push-ECX
557     # . . call
558     e8/call  slice-starts-with?/disp32
559     # . . discard args
560     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
561     # check-ints-equal(EAX, 1, msg)
562     # . . push args
563     68/push  "F - test-slice-starts-with-multiple-characters"/imm32
564     68/push  1/imm32
565     50/push-EAX
566     # . . call
567     e8/call  check-ints-equal/disp32
568     # . . discard args
569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
570     # . epilog
571     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
572     5d/pop-to-EBP
573     c3/return
574 
575 test-slice-starts-with-entire-string:
576     # - slice-starts-with?(slice("Abc"), "Abc") == 1
577     # . prolog
578     55/push-EBP
579     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
580     # var slice/ECX
581     68/push  _test-slice-data-3/imm32/end
582     68/push  _test-slice-data-0/imm32/start
583     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
584     # EAX = slice-starts-with?(ECX, "Abc")
585     # . . push args
586     68/push  "Abc"/imm32
587     51/push-ECX
588     # . . call
589     e8/call  slice-starts-with?/disp32
590     # . . discard args
591     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
592     # check-ints-equal(EAX, 1, msg)
593     # . . push args
594     68/push  "F - test-slice-starts-with-entire-string"/imm32
595     68/push  1/imm32
596     50/push-EAX
597     # . . call
598     e8/call  check-ints-equal/disp32
599     # . . discard args
600     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
601     # . epilog
602     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
603     5d/pop-to-EBP
604     c3/return
605 
606 test-slice-starts-with-fails:
607     # - slice-starts-with?(slice("Abc"), "Abd") == 1
608     # . prolog
609     55/push-EBP
610     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
611     # var slice/ECX
612     68/push  _test-slice-data-3/imm32/end
613     68/push  _test-slice-data-0/imm32/start
614     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
615     # EAX = slice-starts-with?(ECX, "Abd")
616     # . . push args
617     68/push  "Abd"/imm32
618     51/push-ECX
619     # . . call
620     e8/call  slice-starts-with?/disp32
621     # . . discard args
622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
623     # check-ints-equal(EAX, 0, msg)
624     # . . push args
625     68/push  "F - test-slice-starts-with-fails"/imm32
626     68/push  0/imm32
627     50/push-EAX
628     # . . call
629     e8/call  check-ints-equal/disp32
630     # . . discard args
631     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
632     # . epilog
633     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
634     5d/pop-to-EBP
635     c3/return
636 
637 test-slice-starts-with-fails-2:
638     # - slice-starts-with?(slice("Abc"), "Ac") == 1
639     # . prolog
640     55/push-EBP
641     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
642     # var slice/ECX
643     68/push  _test-slice-data-3/imm32/end
644     68/push  _test-slice-data-0/imm32/start
645     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
646     # EAX = slice-starts-with?(ECX, "Ac")
647     # . . push args
648     68/push  "Ac"/imm32
649     51/push-ECX
650     # . . call
651     e8/call  slice-starts-with?/disp32
652     # . . discard args
653     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
654     # check-ints-equal(EAX, 0, msg)
655     # . . push args
656     68/push  "F - test-slice-starts-with-fails-2"/imm32
657     68/push  0/imm32
658     50/push-EAX
659     # . . call
660     e8/call  check-ints-equal/disp32
661     # . . discard args
662     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
663     # . epilog
664     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
665     5d/pop-to-EBP
666     c3/return
667 
668 write-slice:  # out : (address buffered-file), s : (address slice)
669     # . prolog
670     55/push-EBP
671     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
672     # . save registers
673     50/push-EAX
674     51/push-ECX
675     52/push-EDX
676     53/push-EBX
677     56/push-ESI
678     57/push-EDI
679     # ESI = s
680     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
681     # curr/ECX = s->start
682     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
683     # max/ESI = s->end
684     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
685     # EDI = f
686     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
687     # EDX = f->length
688     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EDI+12) to EDX
689     # EBX = f->write
690     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(EDI+4) to EBX
691 $write-slice:loop:
692     # if (curr >= max) break
693     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # compare ECX with ESI
694     7d/jump-if-greater-or-equal  $write-slice:loop-end/disp8
695     # if (f->write >= f->length) flush and clear f's stream
696     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX with EDX
697     7c/jump-if-lesser  $write-slice:to-stream/disp8
698     # . persist f->write
699     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
700     # . flush(f)
701     # . . push args
702     57/push-EDI
703     # . . call
704     e8/call  flush/disp32
705     # . . discard args
706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
707     # . clear-stream(stream = f+4)
708     # . . push args
709     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
710     50/push-EAX
711     # . . call
712     e8/call  clear-stream/disp32
713     # . . discard args
714     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
715     # . f->write must now be 0; update its cache at EBX
716     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
717 $write-slice:to-stream:
718     # f->data[f->write] = *in
719     # . AL = *in
720     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
721     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
722     # . f->data[f->write] = AL
723     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  3/index/EBX   .           0/r32/AL    0x10/disp8      .                 # copy AL to *(EDI+EBX+16)
724     # ++f->write
725     43/increment-EBX
726     # ++in
727     41/increment-ECX
728     eb/jump  $write-slice:loop/disp8
729 $write-slice:loop-end:
730     # persist necessary variables from registers
731     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
732 $write-slice:end:
733     # . restore registers
734     5f/pop-to-EDI
735     5e/pop-to-ESI
736     5b/pop-to-EBX
737     5a/pop-to-EDX
738     59/pop-to-ECX
739     58/pop-to-EAX
740     # . epilog
741     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
742     5d/pop-to-EBP
743     c3/return
744 
745 test-write-slice:
746     # . prolog
747     55/push-EBP
748     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
749     # setup
750     # . clear-stream(_test-stream)
751     # . . push args
752     68/push  _test-stream/imm32
753     # . . call
754     e8/call  clear-stream/disp32
755     # . . discard args
756     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
757     # . clear-stream(_test-buffered-file+4)
758     # . . push args
759     b8/copy-to-EAX  _test-buffered-file/imm32
760     05/add-to-EAX  4/imm32
761     50/push-EAX
762     # . . call
763     e8/call  clear-stream/disp32
764     # . . discard args
765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
766     # var slice/ECX = "Abc"
767     68/push  _test-slice-data-3/imm32/end
768     68/push  _test-slice-data-0/imm32/start
769     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
770     # write-slice(_test-buffered-file, slice)
771     # . . push args
772     51/push-ECX
773     68/push  _test-buffered-file/imm32
774     # . . call
775     e8/call  write-slice/disp32
776     # . . discard args
777     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
778     # flush(_test-buffered-file)
779     # . . push args
780     68/push  _test-buffered-file/imm32
781     # . . call
782     e8/call  flush/disp32
783     # . . discard args
784     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
785     # check-stream-equal(_test-stream, "Abc", msg)
786     # . . push args
787     68/push  "F - test-write-slice"/imm32
788     68/push  "Abc"/imm32
789     68/push  _test-stream/imm32
790     # . . call
791     e8/call  check-stream-equal/disp32
792     # . . discard args
793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
794     # . epilog
795     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
796     5d/pop-to-EBP
797     c3/return
798 
799 # write an entire stream's contents to a buffered-file
800 # ways to do this:
801 #   - construct a 'maximal slice' and pass it to write-slice
802 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
803 # we'll go with the first way for now
804 write-stream-buffered:  # f : (address buffered-file), s : (address stream)
805     # . prolog
806     55/push-EBP
807     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
808     # . save registers
809     50/push-EAX
810     56/push-ESI
811     # ESI = s
812     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
813     # var slice/ECX = {s->data, s->data + s->write}
814     # . push s->data + s->write
815     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
816     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
817     50/push-EAX
818     # . push s->data
819     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
820     50/push-EAX
821     # . ECX = ESP
822     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
823     # write-slice(f, slice)
824     # . . push args
825     51/push-ECX
826     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
827     # . . call
828     e8/call  write-slice/disp32
829     # . . discard args
830     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
831 $write-stream-buffered:end:
832     # . restore registers
833     5e/pop-to-ESI
834     58/pop-to-EAX
835     # . epilog
836     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
837     5d/pop-to-EBP
838     c3/return
839 
840 test-write-stream-buffered:
841     # . prolog
842     55/push-EBP
843     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
844     # setup
845     # . clear-stream(_test-stream)
846     # . . push args
847     68/push  _test-stream/imm32
848     # . . call
849     e8/call  clear-stream/disp32
850     # . . discard args
851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
852     # . clear-stream(_test-buffered-file+4)
853     # . . push args
854     b8/copy-to-EAX  _test-buffered-file/imm32
855     05/add-to-EAX  4/imm32
856     50/push-EAX
857     # . . call
858     e8/call  clear-stream/disp32
859     # . . discard args
860     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
861     # . clear-stream(_test-tmp-stream)
862     # . . push args
863     68/push  _test-tmp-stream/imm32
864     # . . call
865     e8/call  clear-stream/disp32
866     # . . discard args
867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
868     # initialize input
869     # . write(_test-tmp-stream, "abcd")
870     # . . push args
871     68/push  "abcd"/imm32
872     68/push  _test-tmp-stream/imm32
873     # . . call
874     e8/call  write/disp32
875     # . . discard args
876     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
877     # perform the write-stream-buffered
878     # . write-stream-buffered(_test-buffered-file, _test-tmp-stream)
879     # . . push args
880     68/push  _test-tmp-stream/imm32
881     68/push  _test-buffered-file/imm32
882     # . . call
883     e8/call  write-stream-buffered/disp32
884     # . . discard args
885     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
886     # check that the write happened as expected
887     # . flush(_test-buffered-file)
888     # . . push args
889     68/push  _test-buffered-file/imm32
890     # . . call
891     e8/call  flush/disp32
892     # . . discard args
893     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
894     # . check-stream-equal(_test-stream, "abcd", msg)
895     # . . push args
896     68/push  "F - test-write-stream-buffered"/imm32
897     68/push  "abcd"/imm32
898     68/push  _test-stream/imm32
899     # . . call
900     e8/call  check-stream-equal/disp32
901     # . . discard args
902     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
903     # . epilog
904     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
905     5d/pop-to-EBP
906     c3/return
907 
908 == data
909 
910 _test-slice-data-0:
911     41/A
912 _test-slice-data-1:
913     62/b
914 _test-slice-data-2:
915     63/c
916 _test-slice-data-3:
917     64/d
918 _test-slice-data-4:
919 
920 # . _. vim:nowrap:textwidth=0