https://github.com/akkartik/mu/blob/master/095stack.subx
  1 # A stack looks like this:
  2 #   top: int
  3 #   data: (array byte)  # prefixed by length as usual
  4 
  5 == code
  6 #   instruction                     effective address                                                   register    displacement    immediate
  7 # . op          subop               mod             rm32          base        index         scale       r32
  8 # . 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
  9 
 10 clear-stack:  # s : (address stack)
 11     # . prologue
 12     55/push-ebp
 13     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 14     # . save registers
 15     50/push-eax
 16     51/push-ecx
 17     # eax = s
 18     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
 19     # ecx = &s->data[s->length]
 20     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(eax+4) to eax
 21     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   8/disp8         .                 # copy eax+ecx+8 to ecx
 22     # s->top = 0
 23     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
 24     # eax = s->data
 25     81          0/subop/add         3/mod/direct    0/rm32/eax    .           .             .           .           .               8/imm32           # add to eax
 26 $clear-stack:loop:
 27     # if (eax >= ecx) break
 28     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
 29     73/jump-if-greater-or-equal-unsigned  $clear-stack:end/disp8
 30     # *eax = 0
 31     c6          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm8            # copy byte to *eax
 32     # ++eax
 33     40/increment-eax
 34     eb/jump $clear-stack:loop/disp8
 35 $clear-stack:end:
 36     # . restore registers
 37     59/pop-to-ecx
 38     58/pop-to-eax
 39     # . epilogue
 40     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 41     5d/pop-to-ebp
 42     c3/return
 43 
 44 test-clear-stack:
 45     # . prologue
 46     55/push-ebp
 47     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 48     # var stack/ecx = stack of size 8 with random data in it
 49     68/push 34/imm32
 50     68/push 35/imm32
 51     68/push 8/imm32/length
 52     68/push 14/imm32/top
 53     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 54     # clear(stack)
 55     # . . push args
 56     51/push-ecx
 57     # . . call
 58     e8/call  clear-stack/disp32
 59     # . . discard args
 60     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 61     # top should be 0
 62     58/pop-to-eax
 63     # . check-ints-equal(eax, 0, msg)
 64     # . . push args
 65     68/push  "F - test-clear-stack: top"/imm32
 66     68/push  0/imm32
 67     50/push-eax
 68     # . . call
 69     e8/call  check-ints-equal/disp32
 70     # . . discard args
 71     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 72     # length should remain 8
 73     58/pop-to-eax
 74     # . check-ints-equal(eax, 8, msg)
 75     # . . push args
 76     68/push  "F - test-clear-stack: length"/imm32
 77     68/push  8/imm32
 78     50/push-eax
 79     # . . call
 80     e8/call  check-ints-equal/disp32
 81     # . . discard args
 82     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 83     # first word is 0
 84     58/pop-to-eax
 85     # . check-ints-equal(eax, 0, msg)
 86     # . . push args
 87     68/push  "F - test-clear-stack: data[0..3]"/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     # second word is 0
 95     58/pop-to-eax
 96     # . check-ints-equal(eax, 0, msg)
 97     # . . push args
 98     68/push  "F - test-clear-stack: data[4..7]"/imm32
 99     68/push  0/imm32
100     50/push-eax
101     # . . call
102     e8/call  check-ints-equal/disp32
103     # . . discard args
104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
105     # . epilogue
106     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
107     5d/pop-to-ebp
108     c3/return
109 
110 push:  # s : (address stack), n : int
111     # . prologue
112     55/push-ebp
113     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
114     # . save registers
115     50/push-eax
116     51/push-ecx
117     56/push-esi
118     # esi = s
119     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
120     # ecx = s->top
121     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
122     # if (s->top >= s->length) abort
123     39/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # compare *(esi+4) and ecx
124     7e/jump-if-lesser-or-equal  $push:abort/disp8
125     # s->data[s->top] = n
126     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
127     89/copy                         1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   8/disp8         .                 # copy eax to *(esi+ecx+8)
128     # s->top += 4
129     81          0/subop/add         0/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # subtract from *esi
130 $push:end:
131     # . restore registers
132     5e/pop-to-esi
133     59/pop-to-ecx
134     58/pop-to-eax
135     # . epilogue
136     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
137     5d/pop-to-ebp
138     c3/return
139 
140 $push:abort:
141     # print(stderr, "error: push: no space left")
142     # . write-buffered(Stderr, "error: push: no space left")
143     # . . push args
144     68/push  "error: push: no space left"/imm32
145     68/push  Stderr/imm32
146     # . . call
147     e8/call  write-buffered/disp32
148     # . . discard args
149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
150     # . flush(Stderr)
151     # . . push args
152     68/push  Stderr/imm32
153     # . . call
154     e8/call  flush/disp32
155     # . . discard args
156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
157     # . syscall(exit, 1)
158     bb/copy-to-ebx  1/imm32
159     b8/copy-to-eax  1/imm32/exit
160     cd/syscall  0x80/imm8
161     # never gets here
162 
163 test-push:
164     # . prologue
165     55/push-ebp
166     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
167     # var stack/ecx = empty stack of size 8
168     68/push 0/imm32
169     68/push 0/imm32
170     68/push 8/imm32/length
171     68/push 0/imm32/top
172     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
173     # push(stack, 0x42)
174     # . . push args
175     68/push  0x42/imm32
176     51/push-ecx
177     # . . call
178     e8/call  push/disp32
179     # . . discard args
180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
181     # check top
182     58/pop-to-eax
183     # . check-ints-equal(eax, 4, msg)
184     # . . push args
185     68/push  "F - test-push: top"/imm32
186     68/push  4/imm32
187     50/push-eax
188     # . . call
189     e8/call  check-ints-equal/disp32
190     # . . discard args
191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
192     # check length
193     58/pop-to-eax
194     # . check-ints-equal(eax, 8, msg)
195     # . . push args
196     68/push  "F - test-push: length"/imm32
197     68/push  8/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     # first word is 0x42
204     58/pop-to-eax
205     # . check-ints-equal(eax, 0x42, msg)
206     # . . push args
207     68/push  "F - test-push: data[0..3]"/imm32
208     68/push  0x42/imm32
209     50/push-eax
210     # . . call
211     e8/call  check-ints-equal/disp32
212     # . . discard args
213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
214     # second word is 0
215     58/pop-to-eax
216     # . check-ints-equal(eax, 0, msg)
217     # . . push args
218     68/push  "F - test-push: data[4..7]"/imm32
219     68/push  0/imm32
220     50/push-eax
221     # . . call
222     e8/call  check-ints-equal/disp32
223     # . . discard args
224     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
225     # . epilogue
226     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
227     5d/pop-to-ebp
228     c3/return
229 
230 pop:  # s : (address stack) -> n/eax : int
231     # . prologue
232     55/push-ebp
233     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
234     # . save registers
235     51/push-ecx
236     56/push-esi
237     # esi = s
238     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
239     # if (s->top <= 0) abort
240     81          7/subop/compare     0/mod/indirect  6/rm32/esi    .           .             .           .           .               0/imm32           # compare *esi
241     7e/jump-if-lesser-or-equal  $pop:abort/disp8
242     # s->top -= 4
243     81          5/subop/subtract    0/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # subtract from *esi
244     # eax = s->data[s->top]
245     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
246     8b/copy                         1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   8/disp8         .                 # copy *(esi+ecx+8) to eax
247     # s->data[s->top] = 0
248     c7          0/subop/copy        1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   8/disp8         0/imm32           # copy to *(esi+ecx+8)
249 $pop:end:
250     # . restore registers
251     5e/pop-to-esi
252     59/pop-to-ecx
253     # . epilogue
254     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
255     5d/pop-to-ebp
256     c3/return
257 
258 $pop:abort:
259     # print(stderr, "error: pop: nothing left in stack")
260     # . write-buffered(Stderr, "error: pop: nothing left in stack")
261     # . . push args
262     68/push  "error: pop: nothing left in stack"/imm32
263     68/push  Stderr/imm32
264     # . . call
265     e8/call  write-buffered/disp32
266     # . . discard args
267     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
268     # . flush(Stderr)
269     # . . push args
270     68/push  Stderr/imm32
271     # . . call
272     e8/call  flush/disp32
273     # . . discard args
274     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
275     # . syscall(exit, 1)
276     bb/copy-to-ebx  1/imm32
277     b8/copy-to-eax  1/imm32/exit
278     cd/syscall  0x80/imm8
279     # never gets here
280 
281 test-pop:
282     # . prologue
283     55/push-ebp
284     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
285     # var stack/ecx = stack of size 8 containing just 0x42
286     68/push 0/imm32
287     68/push 0x42/imm32
288     68/push 8/imm32/length
289     68/push 4/imm32/top
290     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
291     # eax = pop(stack)
292     # . . push args
293     51/push-ecx
294     # . . call
295     e8/call  pop/disp32
296     # . . discard args
297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
298     # check-ints-equal(eax, 0x42, msg)
299     # . . push args
300     68/push  "F - test-pop: result"/imm32
301     68/push  0x42/imm32
302     50/push-eax
303     # . . call
304     e8/call  check-ints-equal/disp32
305     # . . discard args
306     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
307     # check top
308     58/pop-to-eax
309     # . check-ints-equal(eax, 0, msg)
310     # . . push args
311     68/push  "F - test-pop: top"/imm32
312     68/push  0/imm32
313     50/push-eax
314     # . . call
315     e8/call  check-ints-equal/disp32
316     # . . discard args
317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
318     # check length
319     58/pop-to-eax
320     # . check-ints-equal(eax, 8, msg)
321     # . . push args
322     68/push  "F - test-pop: length"/imm32
323     68/push  8/imm32
324     50/push-eax
325     # . . call
326     e8/call  check-ints-equal/disp32
327     # . . discard args
328     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
329     # . epilogue
330     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
331     5d/pop-to-ebp
332     c3/return
333 
334 top:  # s : (address stack) -> n/eax : int
335     # . prologue
336     55/push-ebp
337     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
338     # . save registers
339     51/push-ecx
340     56/push-esi
341     # esi = s
342     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
343     # if (s->top <= 0) abort
344     81          7/subop/compare     0/mod/indirect  6/rm32/esi    .           .             .           .           .               0/imm32           # compare *esi
345     7e/jump-if-lesser-or-equal  $top:abort/disp8
346     # eax = s->data[s->top - 4]
347     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
348     81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # subtract from ecx
349     8b/copy                         1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   8/disp8         .                 # copy *(esi+ecx+8) to eax
350 $top:end:
351     # . restore registers
352     5e/pop-to-esi
353     59/pop-to-ecx
354     # . epilogue
355     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
356     5d/pop-to-ebp
357     c3/return
358 
359 $top:abort:
360     # print(stderr, "error: top: nothing left in stack")
361     # . write-buffered(Stderr, "error: top: nothing left in stack")
362     # . . push args
363     68/push  "error: top: nothing left in stack"/imm32
364     68/push  Stderr/imm32
365     # . . call
366     e8/call  write-buffered/disp32
367     # . . discard args
368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
369     # . flush(Stderr)
370     # . . push args
371     68/push  Stderr/imm32
372     # . . call
373     e8/call  flush/disp32
374     # . . discard args
375     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
376     # . syscall(exit, 1)
377     bb/copy-to-ebx  1/imm32
378     b8/copy-to-eax  1/imm32/exit
379     cd/syscall  0x80/imm8
380     # never gets here
381 
382 test-top:
383     # . prologue
384     55/push-ebp
385     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
386     # var stack/ecx = stack of size 8 containing just 0x42
387     68/push  0/imm32
388     68/push  0x42/imm32
389     68/push  8/imm32/length
390     68/push  4/imm32/top
391     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
392     # eax = top(stack)
393     # . . push args
394     51/push-ecx
395     # . . call
396     e8/call  top/disp32
397     # . . discard args
398     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
399     # check-ints-equal(eax, 42, msg")
400     # . . push args
401     68/push  "F - test-top: result"/imm32
402     68/push  0x42/imm32
403     50/push-eax
404     # . . call
405     e8/call  check-ints-equal/disp32
406     # . . discard args
407     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
408     # . epilogue
409     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
410     5d/pop-to-ebp
411     c3/return
412 
413 # . . vim:nowrap:textwidth=0