https://github.com/akkartik/mu/blob/main/linux/203stack.subx

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