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