xog: slightly better debug output
[urforth.git] / level1 / 02_debugger.f
blobc743a9b3b6798289554b26dc5842da87f621c787
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;; UrForth level 1: self-hosting 32-bit Forth compiler
3 ;; Copyright (C) 2020 Ketmar Dark // Invisible Vector
4 ;; GPLv3 ONLY
5 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 $if URFORTH_DEBUG
9 code: (URFORTH-DEBUGGER-CODEBLOCK)
11 urfdebug_active_flag: dd 0
12 urfdebug_input_fd: dd 0 ;; default input is stdin
14 urfdebug_dump_ddepth: dd 7 ;; default depth of the data stack dump-1
15 urfdebug_dump_rdepth: dd 7 ;; default depth of the data stack dump-1
17 urfdebug_bp_rdepth: dd -1 ;; otherwise activate when return stack depth reaches this
18 urfdebug_sobp_address: dd -1 ;; breakpoint address (at which we should restore dword)
19 ;;urfdebug_sobp_value: dd 0 ;; breakpoint address (at which we should restore dword)
21 ;;TODO
22 URFORTH_DEBUG_MAX_BPS equ 16
25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26 urfdebug_tib: rb 258
27 urfdebug_tib_size equ $-urfdebug_tib
29 urfdebug_saved_tio: rb 80 ;; actually, 60, but who cares
30 urfdebug_cooked_tio:
31 dd 0x00002102 ;; c_iflag
32 dd 0x00000005 ;; c_oflag
33 dd 0x000008B0 ;; c_cflag
34 dd 0x00008A3B ;; c_lflag
35 db 0x3B ;; c_line
36 db 0x03,0x1C,0x7F,0x15,0x04,0x00,0x01,0x00,0x11,0x13,0x1A,0x00,0x12,0x0F,0x17,0x16 ;; c_cc
37 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ;; c_cc
38 db 0,0,0 ;; align
39 dd 0 ;; c_ispeed
40 dd 0 ;; c_ospeed
43 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
44 ;; set terminal mode
45 ;; IN:
46 ;; EAX: buffer (60 bytes)
47 ;; OUT:
48 ;; EAX: error-or-0, flags dead, others unchanged
49 urfdebug_set_tio_mode:
50 push ebx
51 push ecx
52 push edx
53 ld edx,eax ;; buffer
54 ld eax,54 ;; ioctl
55 ld ebx,1 ;; stdout
56 ld ecx,0x5404 ;; TCSETSF (TCSAFLUSH)
57 syscall
58 pop edx
59 pop ecx
60 pop ebx
61 ret
64 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
65 ;; get terminal mode
66 ;; IN:
67 ;; EAX: buffer (60 bytes)
68 ;; OUT:
69 ;; EAX: error-or-0, flags dead, others unchanged
70 urfdebug_get_tio_mode:
71 push ebx
72 push ecx
73 push edx
74 ld edx,eax ;; buffer
75 ld eax,54 ;; ioctl
76 ld ebx,1 ;; stdout
77 ld ecx,0x5401 ;; TCGETS
78 syscall
79 pop edx
80 pop ecx
81 pop ebx
82 ret
85 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
86 ;; external API
87 urfdebug_activate_it:
88 ld dword [urfdebug_active_flag],1
89 ;;ld dword [urforth_next_ptr],urfdebug_next
90 ;; put "jmp near urfdebug_next" there
91 ld byte [urforth_next],0xe9 ;; jmp near imm
92 ld eax,urfdebug_next
93 sub eax,urforth_next+5
94 ld [urforth_next+1],eax
95 ret
97 urfdebug_deactivate_it:
98 ld dword [urfdebug_active_flag],0
99 ;;ld dword [urforth_next_ptr],urforth_next_normal
100 ;; put 0xff 0xe0 there (jmp eax)
101 ld eax,0xe0_ff
102 ld dword [urforth_next],eax
106 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
107 ;; print asciiz string from EAX
108 ;; IN:
109 ;; EAX: start address
110 ;; OUT:
111 ;; EAX: right after terminating zero
112 ;; all other registers are preserved (excluding flags)
114 urfdebug_emit_str_asciiz_eax:
115 push esi
116 mov esi,eax
118 lodsb
119 or al,al
120 jr z,@f
121 call urfsegfault_emit_al
122 jr @b
124 mov eax,esi
125 pop esi
129 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
130 ;; read char to AL
131 ;; all registers are preserved, excluding EAX
132 ;; on EOF, EAX is 0, and zero flag is set
133 ;; on success, EAX is !0, and zero flag is reset
135 urfdebug_getch:
136 save_all_regs_no_eax_f
137 xor eax,eax
138 push eax ;; we will read char here
139 mov eax,3 ;; read
140 mov ebx,[urfdebug_input_fd]
141 mov ecx,esp ;; address
142 mov edx,1 ;; length
143 syscall
144 ;; check number of read chars
145 or eax,eax
146 ;; get read char
147 pop eax
148 jr z,urfdebug_getch_eof
149 and eax,0xff ;; safety, and zero check
150 jr nz,urfdebug_getch_ok
151 inc al
152 or al,al ;; reset zero flag
153 urfdebug_getch_ok:
154 restore_all_regs_no_eax_f
156 urfdebug_getch_eof:
157 xor eax,eax ;; also sets zero flag
158 jr urfdebug_getch_ok
161 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
162 ;; read input line into urfdebug_tib
164 ;; OUT:
165 ;; ECX: line length, without CR
166 ;; zero flag: set on EOF (never set on non-empty line)
167 ;; other flags are dead
168 ;; other registers are preserved
169 ;; always terminates input string with 0
170 urfdebug_getline:
171 push edi
172 push eax
173 mov edi,urfdebug_tib
174 mov ecx,urfdebug_tib_size-1
175 urfdebug_getline_loop:
176 ;; EDI: destination
177 ;; ECX: bytes left in destination (excluding room for terminating 0)
178 call urfdebug_getch
179 jr z,urfdebug_getline_eof
180 cmp al,13
181 jr z,urfdebug_getline_done
182 cmp al,10
183 jr z,urfdebug_getline_done
184 stosb
185 loop urfdebug_getline_loop
186 ;; either EOL, or end of input buffer
187 ;; we don't care
188 urfdebug_getline_done:
189 ;; store terminating 0
190 xor al,al
191 stosb
192 ;; calculate line length
193 mov eax,urfdebug_tib_size-1
194 sub eax,ecx
195 mov ecx,eax
196 ;; reset zero flag
197 mov al,1
198 or al,al
199 urfdebug_getline_exit:
200 ;; exit
201 pop eax
202 pop edi
204 urfdebug_getline_eof:
205 ;; eof: check for empty line
206 cmp edi,urfdebug_tib
207 jr nz,urfdebug_getline_done
208 ;; empty line and eof: real eof
209 ;; store terminating zero
210 xor al,al
211 stosb
212 ;; return 0 length, and zero flag set
213 xor ecx,ecx
214 jr urfdebug_getline_exit
217 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
218 ;; skip blanks
220 ;; IN:
221 ;; ESI: input buffer start
222 ;; OUT:
223 ;; ESI: pointer to non-blank or zero char
224 ;; EAX: non-blank char
225 ;; ZERO SET if ESI points to zero char (i.e. on EOL)
226 ;; all other registers are preserved, except flags
228 urfdebug_skip_blanks:
229 lodsb
230 or al,al
231 jr z,urfdebug_skip_blanks_stop
232 cmp al,32+1
233 jr c,urfdebug_skip_blanks
234 urfdebug_skip_blanks_stop:
235 dec esi
236 and eax,0xff
240 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
241 ;; convert AL to digit in hex
242 ;; sets carry on error (and AL is undefined)
243 ;; all other registers are preserved, except flags
245 urfdebug_digit_al:
246 sub al,'0'
247 ret c
248 cp al,10
250 ret nc
251 sub al,7
252 ret c
253 cp al,16
255 ret nc
256 sub al,32
257 ret c
258 cp al,16
263 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
264 ;; parse number
266 ;; IN:
267 ;; ESI: input buffer start (blanks must be skipped!)
268 ;; OUT:
269 ;; ESI: pointer to non-blank or zero char
270 ;; EAX: parsed number
271 ;; CARRY SET if not a number (in this case ESI is not modified, and EAX is garbage)
272 ;; all other registers are preserved, except flags
274 urfdebug_parse_number:
275 push esi ;; we may need to restore it
276 xor eax,eax
277 push eax ;; negate flag
278 push eax ;; number base
279 push eax ;; number accumulator
280 ;; locals:
281 ;; [esp] -- accumulator
282 ;; [esp+4] -- base
283 ;; [esp+8] -- negate flag
284 movzx eax,byte [esi]
285 or al,al
286 jp z,urfdebug_parse_number_error
288 ;; check for negative/positive
289 cp al,'-'
290 jr nz,@f
291 ld dword [esp+8],1
292 inc esi
293 jr urfdebug_parse_number_check_prefixes
295 cp al,'+'
296 jr nz,@f
297 inc esi
300 urfdebug_parse_number_check_prefixes:
301 ;; default base
302 ld dword [esp+4],10
303 ;; check for possible sigils
304 movzx eax,byte [esi]
305 cp al,'$'
306 jr nz,@f
307 ld dword [esp+4],16
308 inc esi
310 cp al,'#'
311 jr nz,@f
312 ld dword [esp+4],16
313 inc esi
315 cp al,'%'
316 jr nz,@f
317 ld dword [esp+4],2
318 inc esi
320 ;; check for possible C-like prefix
321 cp al,'0'
322 jp nz,urfdebug_parse_number_no_c_prefix
323 cp byte [esi+1],'x'
324 jr nz,@f
325 add esi,2
326 ld dword [esp+4],16
327 jp urfdebug_parse_number_no_c_prefix
329 cp byte [esi+1],'X'
330 jr nz,@f
331 add esi,2
332 ld dword [esp+4],16
333 jr urfdebug_parse_number_no_c_prefix
335 cp byte [esi+1],'b'
336 jr nz,@f
337 add esi,2
338 ld dword [esp+4],2
339 jr urfdebug_parse_number_no_c_prefix
341 cp byte [esi+1],'B'
342 jr nz,@f
343 add esi,2
344 ld dword [esp+4],2
345 jr urfdebug_parse_number_no_c_prefix
347 cp byte [esi+1],'o'
348 jr nz,@f
349 add esi,2
350 ld dword [esp+4],8
351 jr urfdebug_parse_number_no_c_prefix
353 cp byte [esi+1],'O'
354 jr nz,@f
355 add esi,2
356 ld dword [esp+4],8
357 jr urfdebug_parse_number_no_c_prefix
359 cp byte [esi+1],'d'
360 jr nz,@f
361 add esi,2
362 ld dword [esp+4],10
363 jr urfdebug_parse_number_no_c_prefix
365 cp byte [esi+1],'D'
366 jr nz,@f
367 add esi,2
368 ld dword [esp+4],10
369 jr urfdebug_parse_number_no_c_prefix
372 urfdebug_parse_number_no_c_prefix:
373 ;; first must be a digit
374 movzx eax,byte [esi]
375 call urfdebug_digit_al
376 jr c,urfdebug_parse_number_error
377 cp al,[esp+4]
378 jr nc,urfdebug_parse_number_error
380 urfdebug_parse_number_loop:
381 lodsb
382 cp al,32+1
383 jr c,urfdebug_parse_number_complete
384 cp al,'_'
385 jr z,urfdebug_parse_number_loop
386 call urfdebug_digit_al
387 jr c,urfdebug_parse_number_error
388 cp al,byte [esp+4]
389 jr nc,urfdebug_parse_number_error
390 push edx
391 push ecx
392 push eax
393 xor edx,edx
394 ld eax,[esp+4+4+4]
395 movzx ecx,byte [esp+4+4+4+4]
396 mul ecx
397 ld edx,eax
398 pop eax
399 movzx eax,al
400 add edx,eax
401 ld [esp+4+4],edx
402 pop ecx
403 pop edx
404 jr urfdebug_parse_number_loop
406 urfdebug_parse_number_complete:
407 dec esi ;; back to the last non-number char
408 call urfdebug_skip_blanks
409 ld eax,[esp]
410 cp dword [esp+8],0
411 jr z,@f
412 neg eax
414 add esp,4*4
415 or eax,eax ;; reset carry
418 urfdebug_parse_number_error:
419 add esp,4*3
420 pop esi
425 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
426 ;; find command
428 ;; IN:
429 ;; ESI: input buffer start
430 ;; OUT:
431 ;; found:
432 ;; ESI: next input buffer token (blanks skipped)
433 ;; EAX: command handler address
434 ;; ZERO RESET
435 ;; not found:
436 ;; ESI: unchanged
437 ;; EAX: 0
438 ;; ZERO SET
439 ;; all registers are preserved, except flags
441 urfdebug_find_command:
442 push esi ;; in case we'll need to restore it
443 call urfdebug_skip_blanks
444 jr z,urfdebug_find_command_eol
445 ;; save two reginster we will use as working set
446 push edi
447 push ecx
448 ;; load table address
449 mov edi,urfdebug_command_table
450 urfdebug_find_command_loop:
451 ;; ESI: input buffer
452 ;; EDI: commant table
453 ;; EAX,ECX: scratchpad
454 mov eax,[edi] ;; handler address
455 or eax,eax
456 jr z,urfdebug_find_command_not_found
457 ;; EAX contains handler address
458 ;; save it for now
459 push eax
460 ;; compare name
461 add edi,4 ;; skip handler address
462 ;; save esi, because we will need to restore it on failure
463 push esi
464 ;; compare until different, or until table name terminates
465 urfdebug_find_command_cmpname_loop:
466 movzx eax,byte [esi] ;; input byte
467 ;; if space, convert to 0, so we can compare it with name terminator
468 cmp al,32+1
469 jr nc,urfdebug_find_command_cmpname_loop_good_al
470 mov al,0
471 urfdebug_find_command_cmpname_loop_good_al:
472 cmp al,[edi] ;; compare with name byte
473 jr nz,urfdebug_find_command_cmpname_loop_failed
474 ;; the bytes are the same, advance pointers
475 inc esi
476 inc edi
477 ;; check for end-of-word
478 or al,al
479 jr nz,urfdebug_find_command_cmpname_loop
480 ;; command name matches
481 ;; ESI: after the command text, and the last space/0
482 ;; EDI: after the command name in the table, and the trailing 0
483 ;; on stack: original ESI, handler address
484 dec esi ;; backup one char in case it was 0
485 call urfdebug_skip_blanks
486 pop eax ;; drop original ESI, we don't need it anymore
487 pop eax ;; this is our return result
488 ;; restore trashed registers
489 pop ecx
490 pop edi
491 add esp,4 ;; ignore saved oritinal ESI
494 urfdebug_find_command_cmpname_loop_failed:
495 ;; ESI: in the command text
496 ;; EDI: in the name text
497 ;; on stack: original ESI, handler address
498 pop esi ;; restore original ESI
499 pop eax ;; we don't need it, so this is just "drop"
500 ;; skip the rest of the name
501 urfdebug_find_command_cmpname_loop_failed_skip:
502 movzx eax,byte [edi]
503 inc edi
504 or al,al
505 jr nz,urfdebug_find_command_cmpname_loop_failed_skip
506 ;; skip help text
508 movzx eax,byte [edi]
509 inc edi
510 or al,al
511 jr nz,@b
512 jr urfdebug_find_command_loop
514 urfdebug_find_command_not_found:
515 pop ecx
516 pop edi
517 urfdebug_find_command_eol:
518 xor eax,eax
519 pop esi
523 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
524 $if URFORTH_EXTRA_STACK_CHECKS
525 ;; flags and EAX are already saved; all others should be preserved
526 urfdebug_stack_check:
527 ld eax,ts:[ua_ofs_sp0]
528 sub eax,4*3
529 cmp esp,eax
530 jr be,.ok
531 ;;jr b,.ok
532 ld dword [urfdebug_active_flag],1
533 .ok:
535 $endif
538 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
539 ;; main debugger entry point
540 ;; all registers are preserved, including flags
542 urfdebug_next:
543 ;; save eax and flags, check for early exit
544 pushfd
545 cmp dword [urfdebug_active_flag],-1
546 jr z,@f
547 push eax
548 $if URFORTH_EXTRA_STACK_CHECKS
549 call urfdebug_stack_check
550 $endif
551 cmp dword [urfdebug_active_flag],0
552 jr nz,urfdebug_active
553 ;; special flag, means "no breakpoint checks"
554 ;; check for breakpoints
555 ld eax,[urfdebug_sobp_address]
556 add eax,4 ;; compensate for loaded dword
557 cp eax,EIP
558 jr z,urfdebug_active
559 ;; check for rdepth breakpoint
560 cp ERP,[urfdebug_bp_rdepth]
561 jr z,urfdebug_active
562 ;; no breakpoints, get out
563 pop eax
565 popfd
566 ;;ret
567 jmp eax
569 ;; debugger is active here
570 urfdebug_active:
571 ;; save other registers
572 pop eax ;; original eax
573 mov [urfsegfault_reg_eax],eax
574 pop eax ;; flags
575 mov [urfsegfault_reg_flags],eax
576 mov [urfsegfault_reg_ebx],ebx
577 mov [urfsegfault_reg_ebx],ebx
578 mov [urfsegfault_reg_ecx],ecx
579 mov [urfsegfault_reg_edx],edx
580 mov [urfsegfault_reg_esi],esi
581 mov [urfsegfault_reg_edi],edi
582 mov [urfsegfault_reg_ebp],ebp
583 $if 0
584 ;; get return address
585 pop eax
586 ;; and save it too
587 mov [urfsegfault_dbg_retaddr],eax
588 $endif
589 ;; now save stack pointer
590 mov [urfsegfault_reg_esp],esp
591 ;; just in case, ensure that the direction flag is clear (autoincrement)
594 ;; switch to our own stack
595 ld eax,[urfsegfault_stack_bottom]
596 ld esp,eax
598 ;; save terminal mode
599 ld eax,urfdebug_saved_tio
600 call urfdebug_get_tio_mode
601 ;; set "cooked" terminal mode
602 ld eax,urfdebug_cooked_tio
603 call urfdebug_set_tio_mode
605 ;; activate debugger
606 ;;ld dword [urfdebug_active_flag],1
607 call urfdebug_activate_it
608 ;; reset rdepth breakpoint
609 ld dword [urfdebug_bp_rdepth],-1
610 ;; reset step-over breakpoint
611 ld dword [urfdebug_sobp_address],-1
613 ;; main debugger loop
614 urfdebug_loop:
615 ;; print debugger prompt, data stack depth, return stack depth
616 ld eax,[urfsegfault_reg_esp]
617 call urfsegfault_emit_hex_eax
618 urfsegfault_emit '|'
619 urfsegfault_printstr "UDBG:D("
621 ;; print data stack depth
622 ld eax,ts:[ua_ofs_sp0]
623 sub eax,[urfsegfault_reg_esp]
624 sar eax,2 ;; divide by cell
625 urfsegfault_printdec eax
627 ;; print return stack depth
628 urfsegfault_printstr "):R("
629 ld eax,ts:[ua_ofs_rp0]
630 sub eax,[urfsegfault_reg_ebp]
631 sar eax,2 ;; divide by cell
632 urfsegfault_printdec eax
634 ;; print the word we're currently executing
635 urfsegfault_printstr "):"
636 mov eax,[urfsegfault_reg_esi]
637 sub eax,4 ;; because we just loaded it
638 call urfsegfault_find_by_addr
639 ;; print line number if we know it
640 push eax
641 ld esi,eax
642 ld eax,[urfsegfault_reg_esi]
643 sub eax,4 ;; because we just loaded it
644 call urfsegfault_find_pc_line_nfa
645 or eax,eax
646 jr z,@f
647 urfsegfault_printstr "{"
648 call urfsegfault_emit_dec_eax
649 ld eax,[esp] ;; nfa
650 call urfsegfault_nfa_write_fname
651 urfsegfault_printstr "}"
653 pop eax
654 ;; done printing line
655 call urfsegfault_nfaprint
657 ;; print the word we'll jump into
658 urfsegfault_printstr " -> "
659 mov eax,[urfsegfault_reg_eax]
660 call urfsegfault_find_by_addr
661 push eax
662 call urfsegfault_nfaprint
663 pop eax
664 ld esi,[urfsegfault_reg_esi]
665 call urfsegfault_nfa_print_arg
667 ;; print current EIP
668 urfsegfault_printstr " | "
669 mov eax,[urfsegfault_reg_esi]
670 sub eax,4 ;; because we just loaded it
671 call urfsegfault_emit_hex_eax
673 ;; print next EIP
674 urfsegfault_printstr " -> "
675 mov eax,[urfsegfault_reg_eax]
676 call urfsegfault_emit_hex_eax
678 ;; print the rest of the prompt
679 urfsegfault_printstr " |>"
681 ;; read user command
682 call urfdebug_getline
683 ;; ECX is length; ZERO SET on EOF
684 ;; deactivate on EOF
685 jr z,urfdebug_deactivate
686 ;; continue on empty command
687 or ecx,ecx
688 jr z,urfdebug_exit
690 mov esi,urfdebug_tib
691 call urfdebug_find_command
692 jr z,urfdebug_unknown_command
694 ;;call uefdebug_call_eax
695 call eax
696 jp urfdebug_loop
698 urfdebug_unknown_command:
699 ;; for now, simply echo the command
700 mov esi,urfdebug_tib
701 urfdebug_echo_loop:
702 lodsb
703 or al,al
704 jr z,urfdebug_echo_loop_done
705 call urfsegfault_emit_al
706 jr urfdebug_echo_loop
707 urfdebug_echo_loop_done:
708 ;;urfsegfault_emit '|'
709 ;;urfsegfault_cr
710 urfsegfault_printstrnl "? what?"
711 jp urfdebug_loop
713 urfdebug_deactivate:
714 urfsegfault_cr
715 urfdebug_deactivate_nocr:
716 ;;mov dword [urfdebug_active_flag],0
717 call urfdebug_deactivate_it
718 ;; and exit
719 urfdebug_exit:
720 ;; restore terminal mode
721 ld eax,urfdebug_saved_tio
722 call urfdebug_set_tio_mode
724 ;; restore registers
725 mov ebx,[urfsegfault_reg_ebx]
726 mov ecx,[urfsegfault_reg_ecx]
727 mov edx,[urfsegfault_reg_edx]
728 mov esi,[urfsegfault_reg_esi]
729 mov edi,[urfsegfault_reg_edi]
730 mov ebp,[urfsegfault_reg_ebp]
731 ;; restore flags
732 mov eax,[urfsegfault_reg_flags]
733 push eax
734 popfd
735 ;; restore stack
736 mov esp,[urfsegfault_reg_esp]
737 $if 0
738 ;; push return address
739 mov eax,[urfsegfault_dbg_retaddr]
740 push eax
741 $endif
742 ;; restore eax, and exit
743 mov eax,[urfsegfault_reg_eax]
744 ;;ret
745 jmp eax
748 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
749 ;; command handlers
750 ;; handler will be called with ESI pointing to unparsed arguments (blanks skipped)
752 urfdebug_command_table:
753 dd urfdebug_cmd_printdstack
754 db ".",0
755 db "dump data stack",0
756 dd urfdebug_cmd_printrstack
757 db ".r",0
758 db "dump return stack",0
759 dd urfdebug_cmd_step_out
760 db "sup",0
761 db "continue until current word returned",0
762 dd urfdebug_cmd_step_over
763 db "s",0
764 db "step over the current word",0
765 dd urfdebug_cmd_deactivate
766 db "c",0
767 db "deactivate debugger",0
769 dd urfdebug_cmd_drop
770 db "drop",0
771 db "drop number from data stack",0
772 dd urfdebug_cmd_swap
773 db "swap",0
774 db "swap numbers at data stack",0
775 dd urfdebug_cmd_push
776 db "push",0
777 db "push number at data stack",0
779 dd urfdebug_cmd_ddepth
780 db "depth",0
781 db "set data stack dump depth",0
782 dd urfdebug_cmd_rdepth
783 db "rdepth",0
784 db "set return stack dump depth",0
786 dd urfdebug_cmd_see
787 db "see",0
788 db "decompile forth word",0
790 dd urfdebug_cmd_fcall
791 db "fcall",0
792 db "call forth word",0
794 dd urfdebug_cmd_help
795 db "h",0
796 db "show this help",0
797 dd 0
800 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
801 ;; drop value from data stack
803 urfdebug_cmd_drop:
804 ld eax,ts:[ua_ofs_sp0]
805 sub eax,[urfsegfault_reg_esp]
806 jr nc,@f
807 ;; underflow
808 urfsegfault_printstrnl "stack underflowed"
811 ret z
812 ;; drop value
813 ld edi,[urfsegfault_reg_esp]
814 ;; set new TOS
815 ld eax,[edi]
816 ld [urfsegfault_reg_ecx],eax
817 add edi,4
818 ld [urfsegfault_reg_esp],edi
822 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
823 ;; swap values on data stack
825 urfdebug_cmd_swap:
826 ld eax,ts:[ua_ofs_sp0]
827 sub eax,[urfsegfault_reg_esp]
828 jr nc,@f
829 ;; underflow
830 urfsegfault_printstrnl "stack underflowed"
833 cp al,2
834 jr nc,@f
835 urfsegfault_printstrnl "stack should have at least two values"
838 ;; swap values
839 ld edi,[urfsegfault_reg_esp]
840 ld eax,[edi]
841 ld ecx,[urfsegfault_reg_ecx]
842 ld [edi],ecx
843 ld [urfsegfault_reg_ecx],eax
847 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
848 ;; push value on data stack
850 urfdebug_cmd_push:
851 ld eax,ts:[ua_ofs_sp0]
852 sub eax,[urfsegfault_reg_esp]
853 jr nc,@f
854 ;; underflow
855 urfsegfault_printstrnl "stack underflowed"
858 urfdebug_cmd_push_parse_loop:
859 call urfdebug_skip_blanks
860 ret z
861 call urfdebug_parse_number
862 jr nc,@f
863 urfsegfault_printstrnl "not a number!"
866 ld edi,[urfsegfault_reg_esp]
867 ld ecx,[urfsegfault_reg_ecx]
868 sub edi,4
869 ld [edi],ecx
870 ld [urfsegfault_reg_ecx],eax
871 ld [urfsegfault_reg_esp],edi
872 jr urfdebug_cmd_push_parse_loop
875 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
876 ;; show command help
878 urfdebug_cmd_help:
879 urfsegfault_printstrnl "------ HELP ------"
880 ld esi,urfdebug_command_table
881 urfdebug_cmd_help_loop:
882 lodsd
883 or eax,eax
884 ret z
885 ld eax,esi
886 urfsegfault_printstr " "
887 call urfdebug_emit_str_asciiz_eax
888 urfsegfault_emit 9
889 call urfdebug_emit_str_asciiz_eax
890 urfsegfault_cr
891 ld esi,eax
892 jr urfdebug_cmd_help_loop
895 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
896 ;; deactivate until current word exited
898 urfdebug_cmd_step_out:
899 ;; check return stack depth
900 ld eax,ts:[ua_ofs_rp0]
901 cmp eax,[urfsegfault_reg_ebp]
902 jr z,urfdebug_cmd_step_out_cannot
903 jr c,urfdebug_cmd_step_out_cannot
904 ld eax,[urfsegfault_reg_ebp]
905 add eax,4 ;; one level up
906 ld [urfdebug_bp_rdepth],eax
907 ;; done, deactivate debugger
908 ;;jp urfdebug_deactivate_nocr
909 ld dword [urfdebug_active_flag],0
910 jp urfdebug_exit
911 urfdebug_cmd_step_out_cannot:
912 urfsegfault_printstrnl "cannot step out, return stack is empty!"
916 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
917 ;; perform "step over"
919 urfdebug_cmd_step_over:
920 ;; sanity check, just in case
921 cp dword [urfdebug_sobp_address],-1
922 jr nz,urfdebug_cmd_step_over_error
923 ;; check if the next address is still in the current word
924 ;; that next address is actually in EIP
925 mov eax,[urfsegfault_reg_esi]
926 call urfsegfault_find_by_addr
927 jp z,urfdebug_cmd_step_over_bad_addr
928 mov ecx,eax
929 mov eax,[urfsegfault_reg_esi]
930 sub eax,4 ;; current instruction
931 call urfsegfault_find_by_addr
932 jp z,urfdebug_cmd_step_over_bad_addr
933 cp eax,ecx
934 jp nz,urfdebug_cmd_step_over_bad_addr
935 ;; get flags
936 mov eax,[urfsegfault_reg_eax]
937 call urfsegfault_find_by_addr
938 jp z,urfdebug_cmd_step_over_bad_addr
939 ld edi,eax
940 movzx eax,word [edi+2]
941 test eax,FLAG_NORETURN
942 jp nz,urfdebug_cmd_step_over_noreturn
943 ;; load argument type
944 movzx eax,byte [edi+1]
945 ld esi,[urfsegfault_reg_esi]
946 ;; check instruction argument type
947 cp al,WARG_BRANCH
948 jr z,urfdebug_cmd_step_over_branch
949 ;; skip argument
950 call urfsegfault_nfa_skip_arg_type_in_eax
951 urfdebug_cmd_step_over_set_sobp:
952 ;; setup debugger activation address
953 ld dword [urfdebug_sobp_address],esi
954 ;; done, deactivate debugger
955 ;;jp urfdebug_deactivate_nocr
956 ld dword [urfdebug_active_flag],0
957 jp urfdebug_exit
958 urfdebug_cmd_step_over_error:
959 urfsegfault_printstrnl "step-over breakpoint already set for some strange reason!"
961 urfdebug_cmd_step_over_bad_addr:
962 ;;urfsegfault_printstrnl "cannot step-over outside of the word!"
963 ;;ret
964 ;; just continue into it
965 jp urfdebug_exit
966 urfdebug_cmd_step_over_branch:
967 ;;urfsegfault_printstrnl "cannot step over branches yet!"
968 ;;ret
969 ;; just continue into it
970 jp urfdebug_exit
971 urfdebug_cmd_step_over_noreturn:
972 ;;urfsegfault_printstrnl "cannot step over NORETURN words!"
973 ;;ret
974 ;; just continue into it
975 jp urfdebug_exit
978 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
979 ;; print return stack
981 urfdebug_cmd_printrstack:
982 push dword [urfsegfault_dump_rdepth]
983 ld eax,[urfdebug_dump_rdepth]
984 ld [urfsegfault_dump_rdepth],eax
985 call urfsegfault_cmd_printrstack
986 pop dword [urfsegfault_dump_rdepth]
990 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
991 ;; print data stack
993 urfdebug_cmd_printdstack:
994 push dword [urfsegfault_dump_ddepth]
995 ld eax,[urfdebug_dump_ddepth]
996 ld [urfsegfault_dump_ddepth],eax
997 call urfsegfault_cmd_printdstack
998 pop dword [urfsegfault_dump_ddepth]
1002 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1003 ;; deactivate debugger
1005 urfdebug_cmd_deactivate:
1006 jp urfdebug_deactivate_nocr
1009 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1010 ;; set data stack dump depth
1012 urfdebug_cmd_ddepth:
1013 call urfdebug_parse_number
1014 jr nc,@f
1015 urfsegfault_printstrnl "number expected!"
1018 cp eax,2
1019 jr nc,@f
1020 urfsegfault_printstrnl "minimum value is 2!"
1023 cp eax,201
1024 jr c,@f
1025 urfsegfault_printstrnl "minimum value is 200!"
1028 dec eax
1029 ld [urfdebug_dump_ddepth],eax
1033 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1034 ;; set return stack dump depth
1036 urfdebug_cmd_rdepth:
1037 call urfdebug_parse_number
1038 jr nc,@f
1039 urfsegfault_printstrnl "number expected!"
1042 cp eax,2
1043 jr nc,@f
1044 urfsegfault_printstrnl "minimum value is 2!"
1047 cp eax,201
1048 jr c,@f
1049 urfsegfault_printstrnl "minimum value is 200!"
1052 dec eax
1053 ld [urfdebug_dump_rdepth],eax
1057 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1058 ;; decompile forth word
1060 urfdebug_cmd_see:
1061 ;; find argument length
1062 ld edi,esi
1063 urfdebug_cmd_see_arglen_loop:
1064 lodsb
1065 cp al,33
1066 jr nc,urfdebug_cmd_see_arglen_loop
1067 dec esi
1068 ld ecx,esi
1069 sub ecx,edi
1070 jr nz,@f
1071 urfsegfault_printstrnl "what word?"
1074 ;; save starting address and length
1075 push edi
1076 push ecx
1077 ;; call WFIND
1078 push edi ;; addr
1079 mov ebp,[urfsegfault_reg_ebp]
1080 ld eax,cfa "wfind"
1081 call ur_mc_fcall
1082 or TOS,TOS
1083 jr nz,@f
1084 pop edx
1085 pop ecx
1086 call urfsegfault_emit_str_ecx_edx_safe
1087 urfsegfault_printstrnl "? what is it?"
1090 pop ebx ;; word cfa
1091 pop edx ;; saved count
1092 pop ecx ;; saved address
1093 call urfsegfault_emit_str_ecx_edx_safe
1094 urfsegfault_printstr " is "
1095 ld eax,ebx
1096 call urfsegfault_find_by_addr
1097 or eax,eax
1098 jr nz,@f
1099 urfsegfault_printstrnl "some shit"
1102 ;;ld esi,eax ;; save cfa
1103 ;;call urfsegfault_cfa2nfa
1104 ld [urfdebug_cmd_see_nfa],eax ;; we'll need it for debug info printing
1105 call urfsegfault_nfaprint
1106 urfsegfault_printstr " at (nfa) 0x"
1107 call urfsegfault_emit_hex_eax
1108 urfsegfault_cr
1110 ;; now decompile it
1111 ld eax,[urfsegfault_fba_nfa]
1112 call urfsegfault_nfa2cfa
1113 mov esi,eax
1114 lodsb
1115 cp al,0xe8 ;; call?
1116 jp nz,urfdebug_cmd_see_notforth
1117 lodsd
1118 lea ecx,[esi+eax]
1119 $if URFORTH_ALIGN_PFA
1120 dec esi
1121 or esi,3
1122 inc esi
1123 $endif
1124 cp ecx,ur_DOFORTH
1125 jp nz,urfdebug_cmd_see_notforth
1126 urfdebug_cmd_see_loop:
1127 cmp esi,[urfsegfault_fba_end]
1128 jr nc,urfdebug_cmd_see_loop_done
1129 ;; print address (and store it on the stack)
1130 ld eax,esi
1131 push eax
1132 call urfsegfault_emit_hex_eax
1133 urfsegfault_printstr ": "
1134 lodsd ;; cfa
1135 call urfsegfault_cfa2nfa
1136 call urfsegfault_nfaprint
1137 ;; print arguments
1138 call urfsegfault_nfa_print_arg
1139 ;; print source code line
1140 pop eax
1141 push esi
1142 ld esi,[urfdebug_cmd_see_nfa]
1143 call urfsegfault_find_pc_line_nfa
1144 or eax,eax
1145 jr z,@f
1146 push eax
1147 urfsegfault_printstr 9,"; line #"
1148 pop eax
1149 call urfsegfault_emit_dec_eax
1151 pop esi
1152 ;; done
1153 urfsegfault_cr
1154 jr urfdebug_cmd_see_loop
1156 urfdebug_cmd_see_loop_done:
1157 urfsegfault_cr
1160 urfdebug_cmd_see_notforth:
1161 urfsegfault_printstrnl "cannot decompile non-Forth words yet."
1164 urfdebug_cmd_see_nfa: dd 0
1167 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1168 ;; call forth words
1170 urfdebug_cmd_fcall:
1171 ;; find argument length
1172 call urfdebug_skip_blanks
1173 ld edi,esi
1175 lodsb
1176 cp al,33
1177 jr nc,@b
1178 dec esi
1179 ld ecx,esi
1180 sub ecx,edi
1181 jr nz,@f
1182 ;;urfsegfault_printstrnl "what word?"
1185 ;; save starting address and length
1186 push edi
1187 push ecx
1188 ;; call WFIND
1189 push edi ;; addr
1190 mov ebp,[urfsegfault_reg_ebp]
1191 ld eax,cfa "wfind"
1192 call ur_mc_fcall
1193 or TOS,TOS
1194 jr nz,@f
1195 ;; try to parse it as a number
1196 pop ecx
1197 pop esi
1198 push esi
1199 push ecx
1200 call urfdebug_parse_number
1201 jr c,.nan
1202 ;; number, push it
1203 ld edi,[urfsegfault_reg_esp]
1204 ld ecx,[urfsegfault_reg_ecx]
1205 sub edi,4
1206 ld [edi],ecx
1207 ld [urfsegfault_reg_ecx],eax
1208 ld [urfsegfault_reg_esp],edi
1209 pop ecx
1210 pop edi
1211 ;; esi is ok here
1212 jr urfdebug_cmd_fcall
1213 .nan:
1214 pop edx
1215 pop ecx
1216 call urfsegfault_emit_str_ecx_edx_safe
1217 urfsegfault_printstrnl "? what is it?"
1220 pop eax ;; word cfa
1221 ;; switch data stacks
1222 ld [urfdebug_cmd_fcall_esp],esp
1223 ld esp,[urfsegfault_reg_esp]
1224 ld TOS,[urfsegfault_reg_ecx]
1225 call ur_mc_fcall
1226 ld [urfsegfault_reg_ecx],TOS
1227 ld [urfsegfault_reg_esp],esp
1228 ld esp,[urfdebug_cmd_fcall_esp]
1229 ;; skip word and continue
1230 pop ecx
1231 pop esi
1232 add esi,ecx
1233 jp urfdebug_cmd_fcall
1235 urfdebug_cmd_fcall_esp: dd 0
1237 endcode
1238 (hidden) (codeblock)
1241 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1242 code: DBG ( ??? )
1243 call urfdebug_activate_it
1244 urnext
1245 endcode
1247 $constant "(JMP-NEXT-ADDR)" urforth_next
1248 $constant "(HAS-DEBUGGER?)" 1
1249 ;;$constant "(NEXT-REF-ADDR)" urforth_next_ptr
1251 $else
1252 $constant "(HAS-DEBUGGER?)" 0
1253 ;; so i can avoid conditional compilation
1254 ;; it MUST be 0
1255 ;;$constant "(NEXT-REF-ADDR)" 0
1256 $constant "(JMP-NEXT-ADDR)" 0
1258 code: DBG ( ??? )
1259 urnext
1260 endcode
1261 $endif