https://github.com/akkartik/mu/blob/main/317abort.subx

      1 # Dump a stack trace when you abort.
      2
      3 == code
      4
      5 abort:  # e: (addr array byte)
      6     # . prologue
      7     55/push-ebp
      8     89/<- %ebp 4/r32/esp
      9     #
     10     (set-cursor-position-on-real-screen 0 0)
     11     (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+8) 0xf 0xc)  # 0/real-screen, 0xf/fg=white, 0xc/bg=red
     12     (dump-call-stack)
     13     # crash
     14     {
     15       eb/jump loop/disp8
     16     }
     17
     18 # Helpers below this point are not intended to be reused; they assume the
     19 # program will soon crash. In particular, they destroy the heap.
     20
     21 dump-call-stack:
     22     # . prologue
     23     55/push-ebp
     24     89/<- %ebp 4/r32/esp
     25     # . save registers
     26     50/push-eax
     27     51/push-ecx
     28     52/push-edx
     29     53/push-ebx
     30     # var labels/edx: (addr stream {start-address, label-slice} 0x4000)
     31     # start addresses are in ascending order
     32     81 5/subop/subtract %esp 0x30000/imm32  # 0x4000 labels * 12 bytes per label
     33     68/push  0x30000/imm32
     34     68/push  0/imm32/read
     35     68/push  0/imm32/write
     36     89/<- %edx 4/r32/esp
     37     #
     38     (load-debug-symbols %edx)  # destroys the heap
     39     # traverse the linked list of ebp pointers: https://wiki.osdev.org/Stack_Trace
     40     8b/-> *ebp 3/r32/ebx
     41     {
     42       # loop termination check
     43       81 7/subop/compare %ebx 0/imm32
     44       0f 84/jump-if-= break/disp32
     45       # loop body
     46       (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "\n" 0 0xc)
     47       (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebx+4) 0xf 0xc)
     48       (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " " 0 0xc)
     49       (containing-function %edx *(ebx+4))  # => eax, ecx
     50       (draw-slice-wrapping-right-then-down-from-cursor-over-full-screen 0 %eax %ecx 0 0xc)
     51       # loop update
     52       8b/-> *ebx 3/r32/ebx
     53       #
     54       e9/jump loop/disp32
     55     }
     56 $dump-call-stack:end:
     57     # . reclaim locals
     58     81 0/subop/add %esp 0x100c/imm32
     59     # . restore registers
     60     5b/pop-to-ebx
     61     5a/pop-to-edx
     62     59/pop-to-ecx
     63     58/pop-to-eax
     64     # . epilogue
     65     89/<- %esp 5/r32/ebp
     66     5d/pop-to-ebp
     67     c3/return
     68
     69 load-debug-symbols:  # labels: (addr stream {start-address, label-slice})
     70     # . prologue
     71     55/push-ebp
     72     89/<- %ebp 4/r32/esp
     73     # . save registers
     74     50/push-eax
     75     51/push-ecx
     76     52/push-edx
     77     53/push-ebx
     78     # create space for a stream on the heap, clobbering any existing data
     79     # var s/ecx: (addr stream byte)
     80     b9/copy-to-ecx 0x03000000/imm32
     81     c7 0/subop/copy *ecx 0/imm32  # write index
     82     c7 0/subop/copy *(ecx+4) 0/imm32  # read index
     83     c7 0/subop/copy *(ecx+8) 0x01000000/imm32  # stream capacity = 16MB
     84     # load 0x400 sectors starting from sector 10080 = 0x2760
     85     (load-sectors Primary-bus-primary-drive 0x2760 0x100 %ecx)
     86     (load-sectors Primary-bus-primary-drive 0x2860 0x100 %ecx)
     87     (load-sectors Primary-bus-primary-drive 0x2960 0x100 %ecx)
     88     (load-sectors Primary-bus-primary-drive 0x2a60 0x100 %ecx)
     89     # - parse pointers to portions of this stream into labels
     90     # var curr/ecx: (addr byte) = s->data
     91     81 0/subop/add %ecx 0xc/imm32
     92     {
     93       # loop termination check
     94       b8/copy-to-eax 0/imm32
     95       8a/byte-> *ecx 0/r32/eax
     96       3d/compare-eax-and 0/imm32
     97       0f 84/jump-if-= break/disp32
     98       # loop body
     99       (skip-to-next-space %ecx)  # => edx
    100       42/increment-edx
    101       (skip-to-next-newline %edx)  # => ebx
    102       (parse-hex-int-helper %edx %ebx)  # => eax
    103       43/increment-ebx
    104       (label-append *(ebp+8) %eax %ecx %edx)
    105       # loop update
    106       89/<- %ecx 3/r32/ebx
    107       #
    108       e9/jump loop/disp32
    109     }
    110 $load-debug-symbols:end:
    111     # . restore registers
    112     5b/pop-to-ebx
    113     5a/pop-to-edx
    114     59/pop-to-ecx
    115     58/pop-to-eax
    116     # . epilogue
    117     89/<- %esp 5/r32/ebp
    118     5d/pop-to-ebp
    119     c3/return
    120
    121 skip-to-next-space:  # curr: (addr byte) -> _/edx: (addr byte)
    122     # . prologue
    123     55/push-ebp
    124     89/<- %ebp 4/r32/esp
    125     # . save registers
    126     50/push-eax
    127     # eax = 0
    128     b8/copy-to-eax 0/imm32
    129     #
    130     8b/-> *(ebp+8) 2/r32/edx
    131     {
    132       8a/byte-> *edx 0/r32/eax
    133       3d/compare-eax-and 0x20/imm32/space
    134       0f 84/jump-if-= break/disp32
    135       3d/compare-eax-and 0/imm32
    136       {
    137         75/jump-if-!= break/disp8
    138         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "done loading" 7 0)
    139         {
    140           eb/jump loop/disp8
    141         }
    142       }
    143       3d/compare-eax-and 0xa/imm32/newline
    144       {
    145         75/jump-if-!= break/disp8
    146         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "unexpected newline" 7 0)
    147         {
    148           eb/jump loop/disp8
    149         }
    150       }
    151       42/increment-edx
    152       e9/jump loop/disp32
    153     }
    154 $skip-to-next-space:end:
    155     # . restore registers
    156     58/pop-to-eax
    157     # . epilogue
    158     89/<- %esp 5/r32/ebp
    159     5d/pop-to-ebp
    160     c3/return
    161
    162 skip-to-next-newline:  # curr: (addr byte) -> _/ebx: (addr byte)
    163     # . prologue
    164     55/push-ebp
    165     89/<- %ebp 4/r32/esp
    166     # . save registers
    167     50/push-eax
    168     # eax = 0
    169     b8/copy-to-eax 0/imm32
    170     #
    171     8b/-> *(ebp+8) 3/r32/ebx
    172     {
    173       8a/byte-> *ebx 0/r32/eax
    174       3d/compare-eax-and 0xa/imm32/newline
    175       0f 84/jump-if-= break/disp32
    176       3d/compare-eax-and 0/imm32
    177       {
    178         75/jump-if-!= break/disp8
    179         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "done loading" 7 0)
    180         {
    181           eb/jump loop/disp8
    182         }
    183       }
    184       3d/compare-eax-and 0x20/imm32/space
    185       {
    186         75/jump-if-!= break/disp8
    187         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "unexpected space" 7 0)
    188         {
    189           eb/jump loop/disp8
    190         }
    191       }
    192       43/increment-ebx
    193       e9/jump loop/disp32
    194     }
    195 $skip-to-next-newline:end:
    196     # . restore registers
    197     58/pop-to-eax
    198     # . epilogue
    199     89/<- %esp 5/r32/ebp
    200     5d/pop-to-ebp
    201     c3/return
    202
    203 label-append:  # labels: (addr stream {start-address, label-slice}), address: int, start: int, end: int
    204     # . prologue
    205     55/push-ebp
    206     89/<- %ebp 4/r32/esp
    207     # . save registers
    208     50/push-eax
    209     51/push-ecx
    210     56/push-esi
    211     # esi = labels
    212     8b/-> *(ebp+8) 6/r32/esi
    213     # ecx = labels->write
    214     8b/-> *esi 1/r32/ecx
    215     # labels->data[labels->write] = address
    216     8b/-> *(ebp+0xc) 0/r32/eax
    217     89/<- *(esi+ecx+0xc) 0/r32/eax
    218     # labels->data[labels->write+4] = start
    219     8b/-> *(ebp+0x10) 0/r32/eax
    220     89/<- *(esi+ecx+0x10) 0/r32/eax
    221     # labels->data[labels->write+8] = end
    222     8b/-> *(ebp+0x14) 0/r32/eax
    223     89/<- *(esi+ecx+0x14) 0/r32/eax
    224     # labels->write += 12
    225     81 0/subop/add *esi 0xc/imm32
    226 $label-append:end:
    227     # . restore registers
    228     5e/pop-to-esi
    229     59/pop-to-ecx
    230     58/pop-to-eax
    231     # . epilogue
    232     89/<- %esp 5/r32/ebp
    233     5d/pop-to-ebp
    234     c3/return
    235
    236 containing-function:  # labels: (addr stream {start-address, label-slice}), address: int -> start/eax: (addr byte), end/ecx: (addr byte)
    237     # . prologue
    238     55/push-ebp
    239     89/<- %ebp 4/r32/esp
    240     # . save registers
    241     52/push-edx
    242     53/push-ebx
    243     56/push-esi
    244     # esi = labels
    245     8b/-> *(ebp+8) 6/r32/esi
    246     # var curr/ecx: (addr byte) = labels->data
    247     8d/copy-address *(esi+0xc) 1/r32/ecx
    248     # var max/edx: (addr byte) = labels->data + labels->write
    249     8b/-> *esi 2/r32/edx
    250     01/add-to %edx 1/r32/ecx
    251     # var previous-function-name/ebx: (addr slice) = 0
    252     bb/copy-to-ebx 0/imm32
    253     {
    254       # abort if not found
    255       39/compare %ecx 2/r32/edx
    256       {
    257         0f 82/jump-if-addr< break/disp32
    258         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "failed to find function for address " 7 0)
    259         (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+0xc) 7 0)
    260         {
    261           eb/jump loop/disp8
    262         }
    263       }
    264       # if *curr > address, break
    265       8b/-> *ecx 0/r32/eax
    266       3b/compare 0/r32/eax *(ebp+0xc)
    267       0f 87/jump-if-addr> break/disp32
    268       # if **(curr+4) not '$' or '@', save curr to previous-function-name
    269       {
    270         8b/-> *(ecx+4) 0/r32/eax
    271         8a/byte-> *eax 0/r32/eax
    272         25/and-with-eax 0xff/imm32
    273         3d/compare-eax-and 0x24/imm32/$
    274         74/jump-if-= break/disp8
    275         3d/compare-eax-and 0x40/imm32/@
    276         74/jump-if-= break/disp8
    277         8d/copy-address *(ecx+4) 3/r32/ebx
    278       }
    279       # loop update
    280       81 0/subop/add %ecx 0xc/imm32
    281       #
    282       e9/jump loop/disp32
    283     }
    284     8b/-> *ebx 0/r32/eax
    285     8b/-> *(ebx+4) 1/r32/ecx
    286 $containing-function:end:
    287     # . restore registers
    288     5e/pop-to-esi
    289     5b/pop-to-ebx
    290     5a/pop-to-edx
    291     # . epilogue
    292     89/<- %esp 5/r32/ebp
    293     5d/pop-to-ebp
    294     c3/return
    295
    296 # unlike variants in .mu files, this only supports ASCII
    297 draw-slice-wrapping-right-then-down-from-cursor-over-full-screen:  # screen: (addr screen), start: (addr byte), end: (addr byte), color: int, background-color: int
    298     # . prologue
    299     55/push-ebp
    300     89/<- %ebp 4/r32/esp
    301     # . save registers
    302     50/push-eax
    303     51/push-ecx
    304     52/push-edx
    305     # var curr/ecx: (addr byte) = start
    306     8b/-> *(ebp+0xc) 1/r32/ecx
    307     # edx = end
    308     8b/-> *(ebp+0x10) 2/r32/edx
    309     # eax = 0
    310     b8/copy-to-eax 0/imm32
    311     {
    312       # if (curr >= end) break
    313       39/compare %ecx 2/r32/edx
    314       73/jump-if-addr>= break/disp8
    315       # print *curr
    316       8a/byte-> *ecx 0/r32/eax
    317       (draw-grapheme-at-cursor *(ebp+8) %eax *(ebp+0x14) *(ebp+0x18))
    318       (move-cursor-rightward-and-downward *(ebp+8))
    319       #
    320       41/increment-ecx
    321       #
    322       eb/jump loop/disp8
    323     }
    324 $draw-slice-wrapping-right-then-down-from-cursor-over-full-screen:end:
    325     # . restore registers
    326     5a/pop-to-edx
    327     59/pop-to-ecx
    328     58/pop-to-eax
    329     # . epilogue
    330     89/<- %esp 5/r32/ebp
    331     5d/pop-to-ebp
    332     c3/return