1 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;; and now
for something completely different
...
3 ;; UrForth
/C Forth Engine
!
4 ;; Copyright
(C
) 2023 Ketmar Dark
// Invisible Vector
6 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 ;; interactive debugger
8 ;; optional
; not included by
default
9 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
11 debug
:single
-step@
[IF]
13 $include
-once
<linore
.f
>
20 ;; task the we
're debugging now
26 0 value source-name-stx
30 ;; are we on the alt (debugger) screen now?
35 false value debug-show-keys
38 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
42 ;; switch to alternative screen
46 alt-clear if " \e[?1049h"
47 else " \e[?1048h\e[?47h" endif
48 tty:raw-type \ tty:raw-flush
53 ;; switch to main screen
56 alt-clear if " \e[?1049l"
57 else " \e[?47l\e[?1048l" endif
58 tty:raw-type tty:raw-flush
64 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
65 ;; replace standard emit
68 : (new-norm-emit-char) ( ch -- ch )
69 lo-byte alt-scr? ifnot
75 otherwise drop [char] ?
77 else dup 127 = if drop [char] ? endif
82 debug:replace (norm-emit-char) (new-norm-emit-char)
85 : (debug-emit) ( ch -- )
86 ;; for alt-screen, prepend CR to LF
88 dup nl = if 13 tty:raw-emit endif
92 ;; for main screen, flush on CR or LF
94 dup nl = swap 10 = or if tty:raw-flush endif
99 debug:replace (emit) (debug-emit)
102 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
106 : same-source? ( addr count -- bool )
108 source-name-stx if source-name-stx count string:=
109 else 2drop false endif
113 source-stx handle:free 0-to source-stx
114 source-name-stx handle:free 0-to source-name-stx
117 : load-source-file ( addr count -- )
119 2dup handle:load-file dup to source-stx
121 0 handle:new to source-name-stx
122 dup 8 + source-name-stx handle:size!
123 dup source-name-stx ! ;; counter
124 source-name-stx cell+ swap move
128 : load-source ( addr count -- )
129 2dup same-source? if 2drop
130 else dup 0> if load-source-file else 2drop endif
135 : source-c@ ( ofs -- char )
137 dup source-stx handle:size@ u< if
147 : norm-source-ofs ( ofs -- ofs )
148 source-stx if 0 max source-stx handle:size@ min
152 : source-to-bol ( ofs -- ofs )
153 begin dup 1- + source-c@ dup 0= swap nl = or not-while 1- repeat
156 : source-to-eol ( ofs -- ofs )
157 begin dup source-c@ dup 0= swap nl = or not-while 1+ repeat
161 : source-prev-line ( ofs -- ofs )
162 1- source-to-bol norm-source-ofs
166 : source-next-line ( ofs -- ofs )
167 source-to-eol 1+ norm-source-ofs
170 : find-source-line ( line -- ofs )
171 0 swap 1- for source-next-line endfor
174 : .-source-line ( ofs -- next-line-ofs )
176 begin dup source-c@ dup 0= over nl = or not-while xemit 1+ repeat drop
181 : show-source-line ( addr count line -- )
184 r@ 8 - find-source-line
185 8 for .-source-line endfor
186 ." \e[0;1m" .-source-line ." \e[0m"
187 8 for .-source-line endfor
194 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
195 ;; M-F5 to show main screen
198 also-defs: linore:osc-actions
202 alt-scr? if to-main-scr else to-alt-scr endif
206 // switch "show keys" mode
208 debug-show-keys not to debug-show-keys
214 defer (linore-draw-prompt)
216 ;; return TRUE if key should be eaten
217 : (check-key-alt) ( flag -- FALSE / TRUE )
218 drop alt-scr? ifnot to-alt-scr true else false endif
221 : (show-keycode) ( ch -- )
224 cr ." KEY: code: " dup .
225 dup 33 127 within if ." char: " dup xemit endif
232 : (show-osc) ( addr count -- )
234 cr ." OSC-KEY(" dup 0 u.r ." ): "
239 ..: linore:key-processor ( ch FALSE -- ch FALSE / TRUE )
242 (check-key-alt) if drop true else false endif
243 dup ifnot >r dup (show-keycode) r> endif
248 ..: linore:osc-key-processor ( addr count FALSE -- addr count FALSE / TRUE )
251 (check-key-alt) if 2drop true else false endif
252 dup ifnot >r 2dup (show-osc) r> endif
257 ..: linore:on-before-draw ( FALSE -- FALSE / TRUE )
261 dup ifnot (linore-draw-prompt) endif
267 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
273 debuggee-stid mtask:debugger-resume
278 debuggee-stid mtask:debugger-single-step
282 : show-current-word ( -- )
283 ." \r*** CURRENT WORD: "
284 debuggee-stid mtask:state-ip@ debug:ip->nfa id-count xtype cr
285 debuggee-stid mtask:state-ip@ 6 u.r ." : "
286 debuggee-stid mtask:state-ip@ @ ." [" 4 u.r ." ]: "
287 debuggee-stid mtask:state-ip@ @ cfa->nfa id-count xtype cr
289 debuggee-stid mtask:state-ip@ debug:ip->file/line
290 ( ip -- addr count line TRUE / FALSE )
292 >r 2dup r@ show-source-line r>
293 ." line " . ." in `" xtype ." `" cr
297 : process-task-switch ( action old-stid -- )
298 to debuggee-stid drop ;; drop action id
303 -1 of ." yay, a breakpoint!" cr endof
304 -2 of ." yay, a single-step!" cr endof
305 otherwise " wutafuck?!" error
308 base @ 10 <> " ass!" ?error
309 compiler:comp? " hole!" ?error
310 true to alt-clear to-alt-scr false to alt-clear
316 0 value step-from-line
317 0 value step-from-file-hash
318 0 value step-from-file-nlen
319 ;; used in step-l-over
320 0 value step-curr-nfa
321 0 value step-over-loops
324 ;; call before various dumps
326 flush-emit cr tty:raw-flush
329 : (.push-one) ( n -- )
330 debuggee-stid mtask:state-sp@ 1+ debuggee-stid mtask:state-sp!
331 0 debuggee-stid mtask:state-ds!
335 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
339 simple-vocabulary commands
345 alias-for .source is ?
349 debuggee-stid debug:dump-stack-task
351 alias-for .stack is .s
355 debuggee-stid debug:dump-rstack-task
357 alias-for .rstack is .r
361 debuggee-stid debug:backtrace-task
364 ;; drop value from the debuggee stack
366 debuggee-stid mtask:state-sp@ dup if
367 1- debuggee-stid mtask:state-sp!
368 debuggee-stid debug:dump-stack-task
369 else drop ." ERROR: debuggee stack is empty!" cr
373 ;; push value(s) to the debuggee stack
377 depth debuggee-stid mtask:state-sp@ + debuggee-stid mtask:state-sp!
379 depth for i debuggee-stid mtask:state-ds! endfor
380 debuggee-stid debug:dump-stack-task
381 else ." ERROR: push what?" cr
385 ;; clear debugger data stack
390 ;; debugger data stack depth
392 ." debugger stack depth: " depth . cr
396 to-main-scr tty:raw-flush
400 alias-for continue is c
403 to-main-scr tty:raw-flush
410 to-main-scr tty:raw-flush
415 ;; run current line, stop when it changed
417 debuggee-stid mtask:state-ip@ debug:ip->file-hash/line
418 // ( ip -- len hash line TRUE / FALSE )
421 to step-from-file-hash
422 to step-from-file-nlen
423 ;; now single-step until everything is the same
424 to-main-scr tty:raw-flush
427 ." line: " step-from-line .
428 ." hash: " step-from-file-hash .
429 ." nlen: " step-from-file-nlen .
430 ." ip: " debuggee-stid mtask:state-ip@ .
435 debuggee-stid mtask:debugger-single-step
436 to debuggee-stid drop ;; drop action id
437 debuggee-stid mtask:state-ip@ debug:ip->file-hash/line
444 ." new-ip: " debuggee-stid mtask:state-ip@ .
448 swap step-from-file-hash = and
449 swap step-from-file-nlen = and
454 -3 debuggee-stid ;; for PROCESS-TASK-SWITCH
460 ;; run current line, stop when we returned to the current word
461 : step-l-over ( over-loops -- )
463 ;; HACK: check for FORTH:(EXIT)
464 debuggee-stid mtask:state-ip@ @
465 ['] forth
:(exit
) = if step exit
endif
466 debuggee
-stid mtask
:state
-ip@ debug
:ip
->file
-hash
/line
467 // ( ip
-- len hash line TRUE
/ FALSE
)
470 to step
-from
-file
-hash
471 to step
-from
-file
-nlen
472 debuggee
-stid mtask
:state
-ip@ debug
:ip
->nfa
to step
-curr
-nfa
473 ;; now single
-step until everything is the same
474 to-main
-scr tty
:raw
-flush
478 debuggee
-stid mtask
:debugger
-single
-step
479 to debuggee
-stid drop
;; drop action id
480 debuggee
-stid mtask
:state
-ip@ debug
:ip
->nfa
483 debuggee
-stid mtask
:state
-ip@ debug
:ip
->file
-hash
/line
485 step
-over
-loops
if step
-from
-line
<=
486 else step
-from
-line
= endif
487 swap step
-from
-file
-hash
= and
488 swap step
-from
-file
-nlen
= and
494 -3 debuggee
-stid
;; for PROCESS
-TASK
-SWITCH
500 ;; run one forth instruction
, step when it changed
502 ;; HACK
: check
for FORTH
:(EXIT
)
503 debuggee
-stid mtask
:state
-ip@ @
504 ['] forth:(exit) = if step exit endif
505 debuggee-stid mtask:state-ip@ debug:ip->nfa to step-curr-nfa
507 to-main-scr tty:raw-flush
511 debuggee-stid mtask:debugger-single-step
512 to debuggee-stid drop ;; drop action id
513 debuggee-stid mtask:state-ip@ debug:ip->nfa
517 -3 debuggee-stid ;; for PROCESS-TASK-SWITCH
521 alias-for step-over is o
524 ." ? .source -- show source code (also enter)" CR
525 ." .s .stack -- dump debuggee data stack" CR
526 ." .r .rstack -- dump debuggee return stack" CR
527 ." bt -- show backtrace" CR
528 ." .drop -- drop value from debuggee data stack" CR
529 ." .push -- push values to debuggee data stack (use `:` to exec Forth in dbg)" CR
530 ." .dclear -- clear *debugger* data stack" CR
531 ." .depth -- print *debugger* data stack depth" CR
532 ." c continue -- leave debugger, continue program execution" CR
533 ." s step -- single-step (execute once Forth instruction)" CR
534 ." o step-over -- execute over once forth instruction (rstack check)" CR
535 ." q quit -- quit application" CR
536 ." step-line -- step over the current line (enter subroutines)" CR
537 ." step-l-over -- ( over-loops ) step over the current line (skip subroutines)" CR
539 alias-for .help is .h
544 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
545 ;; more LINORE hotkeys
548 also-defs: linore:osc-actions
551 : 18~ ( -- ) commands:step-line ;
553 : 19~ ( -- ) false commands:step-over ;
555 : 32~ ( -- ) true commands:step-over ;
560 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
561 ;; working with breakpoints
565 ;; breakpoint info in user buffer
566 16384 forth:(addr-temp-bit) + constant bp-area
570 dd address ; 0: unused slot
571 dd old-value ; original value (~0: BP not set)
574 : (bp-rec) ( idx -- addr )
579 : bp-addr@ ( bpinfo -- addr ) @ ;
580 : bp-addr! ( value bpinfo -- ) ! ;
582 : bp-oldval@ ( bpinfo -- oldval ) cell+ @ ;
583 : bp-oldval! ( value bpinfo -- ) cell+ ! ;
586 : bp-alloc ( -- bpinfo )
588 i (bp-rec) @ ifnot i (bp-rec) (unloop) exit endif
590 bp-used (bp-rec) +1-to bp-used
593 : bp-find ( addr -- bpinfo / FALSE )
595 dup i (bp-rec) @ = if drop i (bp-rec) (unloop) exit endif
599 ;; add new breakpoint
601 dup bp-find ifnot ;; create new breakpoint
602 bp-alloc 2dup bp-addr! 0 bitnot over bp-oldval!
605 dup bp-oldval@ 0 bitnot = ifnot ;; not set yet
606 dup bp-addr@ @ over bp-oldval! ;; save old value
607 ['] debug
:(bp
) swap bp
-addr@
! ;; and patch the code
613 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
617 : debugger
-draw
-prompt
( -- )
618 " \r\e[0;1;5m>\e[0m" tty
:raw
-type
621 ' debugger-draw-prompt to (linore-draw-prompt)
623 0 value run-forth-ret-addr
625 : run-forth-done ( -- )
630 rp0! run-forth-ret-addr >r
633 ;; do not allow debugger to abort on bad forth word
634 ..: forth:(interpret-word-not-found-post) ( addr count -- addr count )
636 space xtype ." ? -- wut?!" cr
642 ;; run Forth code in debugger context.
643 ;; this is used to execute all debugger commands, yay!
645 ;; set TIB and run one line
646 0 linore:line + c! ;; finish tib with 0 byte
647 linore:line drop tib ! >in 0! ;; setup tib
648 ['] run
-forth
-done
(user
-interpret
-next
-line
) !
649 r@
to run
-forth
-ret
-addr
652 tib @
>a begin c@a
while c@a xemit
+1>a repeat cr
661 : debugger
-main
-loop
( -- )
666 tty
:size drop
to linore
:draw
-width
667 \ linore
:visible
-amount
2* to linore
:draw
-width
668 flush
-emit linore
:edit
-line
669 linore
:accept
-result
0<
671 linore
:accept
-result linore
:result
-esc
<> if
675 linore
:line nip
0= if " .source" linore
:line
! endif
676 only forth definitions also commands
682 : debugger
-startup \ multitask entry point
683 \
." yay, we are in debugger!" cr
684 \
." debugger stid: " mtask
:active
-state
. cr
685 ( ... argc old
-stid
)
686 swap
" wuta?!" ?error
;; there should be no args
687 mtask
:set
-self
-as
-debugger
688 0 swap mtask
:yield
-to ;; return to the caller
689 ['] debugger-main-loop execute-tail
693 : setup-debugger ( -- )
694 ['] debugger
-startup mtask
:new
-state
to debugger
-task
695 0 debugger
-task mtask
:yield
-to ;; init it
696 debugger
-task
<> " shit!" ?error
697 " fuck!" ?error
;; check number of args
703 debugger
:setup
-debugger