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