https://github.com/akkartik/mu/blob/main/315stack-debug.subx

      1 # The stack shouldn't grow into the code area.
      2
      3 == code
      4
      5 check-stack:
      6     # . prologue
      7     55/push-ebp
      8     89/<- %ebp 4/r32/esp
      9     # . save registers
     10     50/push-eax
     11     #
     12     89/<- %eax 4/r32/esp
     13     81 7/subop/compare %eax 0x01000000/imm32
     14     {
     15       7f/jump-if-> break/disp8
     16       (abort "stack overflow")
     17     }
     18 $check-stack:end:
     19     # . restore registers
     20     58/pop-to-eax
     21     # . epilogue
     22     89/<- %esp 5/r32/ebp
     23     5d/pop-to-ebp
     24     c3/return
     25
     26 show-stack-state:
     27     # . prologue
     28     55/push-ebp
     29     89/<- %ebp 4/r32/esp
     30     # . save registers
     31     50/push-eax
     32     51/push-ecx
     33     52/push-edx
     34     #
     35     89/<- %edx 4/r32/esp
     36     # save old cursor position
     37     (cursor-position 0)  # => eax, ecx
     38     # print at top-right
     39     (set-cursor-position 0 0x70 0)
     40     (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 %edx 0xf 0xc)
     41     # restore cursor position
     42     (set-cursor-position %eax %ecx)
     43 $check-stack:end:
     44     # . restore registers
     45     5a/pop-to-edx
     46     59/pop-to-ecx
     47     58/pop-to-eax
     48     # . epilogue
     49     89/<- %esp 5/r32/ebp
     50     5d/pop-to-ebp
     51     c3/return
     52
     53 # Helper for debugging deeply recursive calls without logs or traces.
     54 # Turn it on, insert calls in the right places, and you get a terse sense of
     55 # important parts of the call stack. A poor sophont's stack trace.
     56 debug-print:  # x: (addr array byte), fg: int, bg: int    # x is very short; usually a single character
     57     # . prologue
     58     55/push-ebp
     59     89/<- %ebp 4/r32/esp
     60     # . save registers
     61     50/push-eax
     62     51/push-ecx
     63     #
     64     {
     65       81 7/subop/compare *Really-debug-print 0/imm32/false
     66       74/jump-if-= break/disp8
     67       (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+8) *(ebp+0xc) *(ebp+0x10))
     68       # clear the screen and continue if we got too close to the bottom
     69       (cursor-position 0)  # => eax, ecx
     70       81 7/subop/compare %ecx 0x28/imm32
     71       75/jump-if-!= break/disp8
     72       (clear-screen 0)
     73       (set-cursor-position 0 0 0)
     74     }
     75 $debug-print:end:
     76     # . restore registers
     77     59/pop-to-ecx
     78     58/pop-to-eax
     79     # . epilogue
     80     89/<- %esp 5/r32/ebp
     81     5d/pop-to-ebp
     82     c3/return
     83
     84 debug-print?:  # -> _/eax: boolean
     85     # . prologue
     86     55/push-ebp
     87     89/<- %ebp 4/r32/esp
     88     #
     89     8b/-> *Really-debug-print 0/r32/eax
     90 $debug-print?:end:
     91     # . epilogue
     92     89/<- %esp 5/r32/ebp
     93     5d/pop-to-ebp
     94     c3/return
     95
     96 turn-on-debug-print:
     97     # . prologue
     98     55/push-ebp
     99     89/<- %ebp 4/r32/esp
    100     #
    101     c7 0/subop/copy *Really-debug-print 1/imm32/true
    102 $turn-on-debug-print:end:
    103     # . epilogue
    104     89/<- %esp 5/r32/ebp
    105     5d/pop-to-ebp
    106     c3/return
    107
    108 turn-off-debug-print:
    109     # . prologue
    110     55/push-ebp
    111     89/<- %ebp 4/r32/esp
    112     #
    113     c7 0/subop/copy *Really-debug-print 0/imm32/false
    114 $turn-off-debug-print:end:
    115     # . epilogue
    116     89/<- %esp 5/r32/ebp
    117     5d/pop-to-ebp
    118     c3/return
    119
    120 == data
    121 Really-debug-print:
    122   0/imm32/false
    123 #?   1/imm32/true