https://github.com/akkartik/mu/blob/master/092stack.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 : (addr stack)
 11     # . prologue
 12     55/push-ebp
 13     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy es
MIT/X Consortium License

(C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the 
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software. 

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
DEALINGS IN THE SOFTWARE.
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 : (addr 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 : (addr 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 : (addr 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 # n = 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