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} 0x5000)
 31     # start addresses are in ascending order
 32     81 5/subop/subtract %esp 0x3c000/imm32  # 0x5000 labels * 12 bytes per label
 33     68/push  0x3c000/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 sectors starting from sector 10080 = 0x2760
 85     (load-sectors Primary-bus-primary-drive 0x2760 0x800 %ecx)  # 0x800 sectors = 1MB
 86     # - parse pointers to portions of this stream into labels
 87     # var curr/ecx: (addr byte) = s->data
 88     81 0/subop/add %ecx 0xc/imm32
 89     {
 90       # loop termination check
 91       b8/copy-to-eax 0/imm32
 92       8a/byte-> *ecx 0/r32/eax
 93       3d/compare-eax-and 0/imm32
 94       0f 84/jump-if-= break/disp32
 95       # loop body
 96       (skip-to-next-space %ecx)  # => edx
 97       42/increment-edx
 98       (skip-to-next-newline %edx)  # => ebx
 99       (parse-hex-int-helper %edx %ebx)  # => eax
100       43/increment-ebx
101       (label-append *(ebp+8) %eax %ecx %edx)
102       # loop update
103       89/<- %ecx 3/r32/ebx
104       #
105       e9/jump loop/disp32
106     }
107 $load-debug-symbols:end:
108     # . restore registers
109     5b/pop-to-ebx
110     5a/pop-to-edx
111     59/pop-to-ecx
112     58/pop-to-eax
113     # . epilogue
114     89/<- %esp 5/r32/ebp
115     5d/pop-to-ebp
116     c3/return
117 
118 skip-to-next-space:  # curr: (addr byte) -> _/edx: (addr byte)
119     # . prologue
120     55/push-ebp
121     89/<- %ebp 4/r32/esp
122     # . save registers
123     50/push-eax
124     # eax = 0
125     b8/copy-to-eax 0/imm32
126     #
127     8b/-> *(ebp+8) 2/r32/edx
128     {
129       8a/byte-> *edx 0/r32/eax
130       3d/compare-eax-and 0x20/imm32/space
131       0f 84/jump-if-= break/disp32
132       3d/compare-eax-and 0/imm32
133       {
134         75/jump-if-!= break/disp8
135         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "done loading" 7 0)
136         {
137           eb/jump loop/disp8
138         }
139       }
140       3d/compare-eax-and 0xa/imm32/newline
141       {
142         75/jump-if-!= break/disp8
143         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "unexpected newline" 7 0)
144         {
145           eb/jump loop/disp8
146         }
147       }
148       42/increment-edx
149       e9/jump loop/disp32
150     }
151 $skip-to-next-space:end:
152     # . restore registers
153     58/pop-to-eax
154     # . epilogue
155     89/<- %esp 5/r32/ebp
156     5d/pop-to-ebp
157     c3/return
158 
159 skip-to-next-newline:  # curr: (addr byte) -> _/ebx: (addr byte)
160     # . prologue
161     55/push-ebp
162     89/<- %ebp 4/r32/esp
163     # . save registers
164     50/push-eax
165     # eax = 0
166     b8/copy-to-eax 0/imm32
167     #
168     8b/-> *(ebp+8) 3/r32/ebx
169     {
170       8a/byte-> *ebx 0/r32/eax
171       3d/compare-eax-and 0xa/imm32/newline
172       0f 84/jump-if-= break/disp32
173       3d/compare-eax-and 0/imm32
174       {
175         75/jump-if-!= break/disp8
176         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "done loading" 7 0)
177         {
178           eb/jump loop/disp8
179         }
180       }
181       3d/compare-eax-and 0x20/imm32/space
182       {
183         75/jump-if-!= break/disp8
184         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "unexpected space" 7 0)
185         {
186           eb/jump loop/disp8
187         }
188       }
189       43/increment-ebx
190       e9/jump loop/disp32
191     }
192 $skip-to-next-newline:end:
193     # . restore registers
194     58/pop-to-eax
195     # . epilogue
196     89/<- %esp 5/r32/ebp
197     5d/pop-to-ebp
198     c3/return
199 
200 label-append:  # labels: (addr stream {start-address, label-slice}), address: int, start: int, end: int
201     # . prologue
202     55/push-ebp
203     89/<- %ebp 4/r32/esp
204     # . save registers
205     50/push-eax
206     51/push-ecx
207     56/push-esi
208     # esi = labels
209     8b/-> *(ebp+8) 6/r32/esi
210     # ecx = labels->write
211     8b/-> *esi 1/r32/ecx
212     # labels->data[labels->write] = address
213     8b/-> *(ebp+0xc) 0/r32/eax
214     89/<- *(esi+ecx+0xc) 0/r32/eax
215     # labels->data[labels->write+4] = start
216     8b/-> *(ebp+0x10) 0/r32/eax
217     89/<- *(esi+ecx+0x10) 0/r32/eax
218     # labels->data[labels->write+8] = end
219     8b/-> *(ebp+0x14) 0/r32/eax
220     89/<- *(esi+ecx+0x14) 0/r32/eax
221     # labels->write += 12
222     81 0/subop/add *esi 0xc/imm32
223 $label-append:end:
224     # . restore registers
225     5e/pop-to-esi
226     59/pop-to-ecx
227     58/pop-to-eax
228     # . epilogue
229     89/<- %esp 5/r32/ebp
230     5d/pop-to-ebp
231     c3/return
232 
233 containing-function:  # labels: (addr stream {start-address, label-slice}), address: int -> start/eax: (addr byte), end/ecx: (addr byte)
234     # . prologue
235     55/push-ebp
236     89/<- %ebp 4/r32/esp
237     # . save registers
238     52/push-edx
239     53/push-ebx
240     56/push-esi
241     # esi = labels
242     8b/-> *(ebp+8) 6/r32/esi
243     # var curr/ecx: (addr byte) = labels->data
244     8d/copy-address *(esi+0xc) 1/r32/ecx
245     # var max/edx: (addr byte) = labels->data + labels->write
246     8b/-> *esi 2/r32/edx
247     01/add-to %edx 1/r32/ecx
248     # var previous-function-name/ebx: (addr slice) = 0
249     bb/copy-to-ebx 0/imm32
250     {
251       # abort if not found
252       39/compare %ecx 2/r32/edx
253       {
254         0f 82/jump-if-addr< break/disp32
255         (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "failed to find function for address " 7 0)
256         (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+0xc) 7 0)
257         {
258           eb/jump loop/disp8
259         }
260       }
261       # if *curr > address, break
262       8b/-> *ecx 0/r32/eax
263       3b/compare 0/r32/eax *(ebp+0xc)
264       0f 87/jump-if-addr> break/disp32
265       # if **(curr+4) not '$' or '@', save curr to previous-function-name
266       {
267         8b/-> *(ecx+4) 0/r32/eax
268         8a/byte-> *eax 0/r32/eax
269         25/and-with-eax 0xff/imm32
270         3d/compare-eax-and 0x24/imm32/$
271         74/jump-if-= break/disp8
272         3d/compare-eax-and 0x40/imm32/@
273         74/jump-if-= break/disp8
274         8d/copy-address *(ecx+4) 3/r32/ebx
275       }
276       # loop update
277       81 0/subop/add %ecx 0xc/imm32
278       #
279       e9/jump loop/disp32
280     }
281     8b/-> *ebx 0/r32/eax
282     8b/-> *(ebx+4) 1/r32/ecx
283 $containing-function:end:
284     # . restore registers
285     5e/pop-to-esi
286     5b/pop-to-ebx
287     5a/pop-to-edx
288     # . epilogue
289     89/<- %esp 5/r32/ebp
290     5d/pop-to-ebp
291     c3/return
292 
293 # unlike variants in .mu files, this only supports ASCII
294 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
295     # . prologue
296     55/push-ebp
297     89/<- %ebp 4/r32/esp
298     # . save registers
299     50/push-eax
300     51/push-ecx
301     52/push-edx
302     # var curr/ecx: (addr byte) = start
303     8b/-> *(ebp+0xc) 1/r32/ecx
304     # edx = end
305     8b/-> *(ebp+0x10) 2/r32/edx
306     # eax = 0
307     b8/copy-to-eax 0/imm32
308     {
309       # if (curr >= end) break
310       39/compare %ecx 2/r32/edx
311       73/jump-if-addr>= break/disp8
312       # print *curr
313       8a/byte-> *ecx 0/r32/eax
314       (draw-grapheme-at-cursor *(ebp+8) %eax *(ebp+0x14) *(ebp+0x18))
315       (move-cursor-rightward-and-downward *(ebp+8))
316       #
317       41/increment-ecx
318       #
319       eb/jump loop/disp8
320     }
321 $draw-slice-wrapping-right-then-down-from-cursor-over-full-screen:end:
322     # . restore registers
323     5a/pop-to-edx
324     59/pop-to-ecx
325     58/pop-to-eax
326     # . epilogue
327     89/<- %esp 5/r32/ebp
328     5d/pop-to-ebp
329     c3/return