URFORTH.me updates
[urforth.git] / level0 / urforth0_dbg.asm
blob46a042d0c477433689b6d46baf2f739bf94b122a
1 ;; Native x86 GNU/Linux Forth System, Direct Threaded Code
2 ;; Forth debugger
3 ;;
4 ;; Copyright (C) 2020 Ketmar Dark // Invisible Vector
5 ;;
6 ;; This program is free software: you can redistribute it and/or modify
7 ;; it under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation, version 3 of the License ONLY.
9 ;;
10 ;; This program is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;; GNU General Public License for more details.
15 ;; You should have received a copy of the GNU General Public License
16 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
19 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 urfdebug_active_flag: dd 0
21 urfdebug_input_fd: dd 0 ; default input is stdin
23 urfdebug_dump_ddepth: dd 7 ; default depth of the data stack dump-1
24 urfdebug_dump_rdepth: dd 7 ; default depth of the data stack dump-1
26 urfdebug_bp_rdepth: dd 0xffffffff ; otherwise activate when return stack depth reaches this
27 urfdebug_sobp_address: dd 0xffffffff ; breakpoint address (at which we should restore dword)
28 ;urfdebug_sobp_value: dd 0 ; breakpoint address (at which we should restore dword)
30 ;TODO
31 URFORTH_DEBUG_MAX_BPS = 16
34 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
35 ;; external API
36 urfdebug_activate_it:
37 ld dword [urfdebug_active_flag],1
38 ld dword [urforth_next_ptr],urfdebug_next
39 ret
41 urfdebug_deactivate_it:
42 ld dword [urfdebug_active_flag],0
43 ld dword [urforth_next_ptr],urforth_next_normal
44 ret
47 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
48 ;; print asciiz string from EAX
49 ;; IN:
50 ;; EAX: start address
51 ;; OUT:
52 ;; EAX: right after terminating zero
53 ;; all other registers are preserved (excluding flags)
55 urfdebug_emit_str_asciiz_eax:
56 push esi
57 mov esi,eax
58 @@:
59 lodsb
60 or al,al
61 jr z,@f
62 call urfsegfault_emit_al
63 jr @b
64 @@:
65 mov eax,esi
66 pop esi
67 ret
70 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
71 ;; read char to AL
72 ;; all registers are preserved, excluding EAX
73 ;; on EOF, EAX is 0, and zero flag is set
74 ;; on success, EAX is !0, and zero flag is reset
76 urfdebug_getch:
77 urfsegfault_save_all_registers_no_eax_f
78 xor eax,eax
79 push eax ; we will read char here
80 mov eax,3 ; read
81 mov ebx,[urfdebug_input_fd]
82 mov ecx,esp ; address
83 mov edx,1 ; length
84 syscall
85 ; check number of read chars
86 or eax,eax
87 ; get read char
88 pop eax
89 jz urfdebug_getch_eof
90 and eax,0xff ; safety, and zero check
91 jnz urfdebug_getch_ok
92 inc al
93 or al,al ; reset zero flag
94 urfdebug_getch_ok:
95 urfsegfault_restore_all_registers_no_eax_f
96 ret
97 urfdebug_getch_eof:
98 xor eax,eax ; also sets zero flag
99 jmp urfdebug_getch_ok
102 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
103 ;; read input line into urfdebug_tib
105 ;; OUT:
106 ;; ECX: line length, without CR
107 ;; zero flag: set on EOF (never set on non-empty line)
108 ;; other flags are dead
109 ;; other registers are preserved
110 ;; always terminates input string with 0
111 urfdebug_getline:
112 push edi
113 push eax
114 mov edi,urfdebug_tib
115 mov ecx,urfdebug_tib_size-1
116 urfdebug_getline_loop:
117 ; EDI: destination
118 ; ECX: bytes left in destination (excluding room for terminating 0)
119 call urfdebug_getch
120 jz urfdebug_getline_eof
121 cmp al,13
122 jz urfdebug_getline_done
123 cmp al,10
124 jz urfdebug_getline_done
125 stosb
126 loop urfdebug_getline_loop
127 ; either EOL, or end of input buffer
128 ; we don't care
129 urfdebug_getline_done:
130 ; store terminating 0
131 xor al,al
132 stosb
133 ; calculate line length
134 mov eax,urfdebug_tib_size-1
135 sub eax,ecx
136 mov ecx,eax
137 ; reset zero flag
138 mov al,1
139 or al,al
140 urfdebug_getline_exit:
141 ; exit
142 pop eax
143 pop edi
145 urfdebug_getline_eof:
146 ; eof: check for empty line
147 cmp edi,urfdebug_tib
148 jnz urfdebug_getline_done
149 ; empty line and eof: real eof
150 ; store terminating zero
151 xor al,al
152 stosb
153 ; return 0 length, and zero flag set
154 xor ecx,ecx
155 jmp urfdebug_getline_exit
158 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
159 ;; skip blanks
161 ;; IN:
162 ;; ESI: input buffer start
163 ;; OUT:
164 ;; ESI: pointer to non-blank or zero char
165 ;; EAX: non-blank char
166 ;; ZERO SET if ESI points to zero char (i.e. on EOL)
167 ;; all other registers are preserved, except flags
169 urfdebug_skip_blanks:
170 lodsb
171 or al,al
172 jz urfdebug_skip_blanks_stop
173 cmp al,32+1
174 jc urfdebug_skip_blanks
175 urfdebug_skip_blanks_stop:
176 dec esi
177 and eax,0xff
181 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
182 ;; convert AL to digit in hex
183 ;; sets carry on error (and AL is undefined)
184 ;; all other registers are preserved, except flags
186 urfdebug_digit_al:
187 sub al,'0'
188 ret c
189 cp al,10
191 ret nc
192 sub al,7
193 ret c
194 cp al,16
196 ret nc
197 sub al,32
198 ret c
199 cp al,16
204 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
205 ;; parse number
207 ;; IN:
208 ;; ESI: input buffer start (blanks must be skipped!)
209 ;; OUT:
210 ;; ESI: pointer to non-blank or zero char
211 ;; EAX: parsed number
212 ;; CARRY SET if not a number (in this case ESI is not modified, and EAX is garbage)
213 ;; all other registers are preserved, except flags
215 urfdebug_parse_number:
216 push esi ; we may need to restore it
217 xor eax,eax
218 push eax ; negate flag
219 push eax ; number base
220 push eax ; number accumulator
221 ; locals:
222 ; [esp] -- accumulator
223 ; [esp+4] -- base
224 ; [esp+8] -- negate flag
225 ld al,[esi]
226 or al,al
227 jr z,urfdebug_parse_number_error
229 ; check for negative/positive
230 cp al,'-'
231 jr nz,@f
232 ld dword [esp+8],1
233 inc esi
234 jr urfdebug_parse_number_check_prefixes
236 cp al,'+'
237 jr nz,@f
238 inc esi
241 urfdebug_parse_number_check_prefixes:
242 ; default base
243 ld dword [esp+4],10
244 ; check for possible sigils
245 ld al,[esi]
246 cp al,'$'
247 jr nz,@f
248 ld dword [esp+4],16
249 inc esi
251 cp al,'#'
252 jr nz,@f
253 ld dword [esp+4],16
254 inc esi
256 cp al,'%'
257 jr nz,@f
258 ld dword [esp+4],2
259 inc esi
261 ; check for possible C-like prefix
262 cp al,'0'
263 jr nz,urfdebug_parse_number_no_c_prefix
264 cp byte [esi+1],'x'
265 jr nz,@f
266 add esi,2
267 ld dword [esp+4],16
268 jr urfdebug_parse_number_no_c_prefix
270 cp byte [esi+1],'X'
271 jr nz,@f
272 add esi,2
273 ld dword [esp+4],16
274 jr urfdebug_parse_number_no_c_prefix
276 cp byte [esi+1],'b'
277 jr nz,@f
278 add esi,2
279 ld dword [esp+4],2
280 jr urfdebug_parse_number_no_c_prefix
282 cp byte [esi+1],'B'
283 jr nz,@f
284 add esi,2
285 ld dword [esp+4],2
286 jr urfdebug_parse_number_no_c_prefix
288 cp byte [esi+1],'o'
289 jr nz,@f
290 add esi,2
291 ld dword [esp+4],8
292 jr urfdebug_parse_number_no_c_prefix
294 cp byte [esi+1],'O'
295 jr nz,@f
296 add esi,2
297 ld dword [esp+4],8
298 jr urfdebug_parse_number_no_c_prefix
300 cp byte [esi+1],'d'
301 jr nz,@f
302 add esi,2
303 ld dword [esp+4],10
304 jr urfdebug_parse_number_no_c_prefix
306 cp byte [esi+1],'D'
307 jr nz,@f
308 add esi,2
309 ld dword [esp+4],10
310 jr urfdebug_parse_number_no_c_prefix
313 urfdebug_parse_number_no_c_prefix:
314 ; first must be a digit
315 ld al,[esi]
316 call urfdebug_digit_al
317 jr c,urfdebug_parse_number_error
318 cp al,[esp+4]
319 jr nc,urfdebug_parse_number_error
321 urfdebug_parse_number_loop:
322 lodsb
323 cp al,32+1
324 jr c,urfdebug_parse_number_complete
325 cp al,'_'
326 jr z,urfdebug_parse_number_loop
327 call urfdebug_digit_al
328 jr c,urfdebug_parse_number_error
329 cp al,byte [esp+4]
330 jr nc,urfdebug_parse_number_error
331 push edx
332 push ecx
333 push eax
334 xor edx,edx
335 xor ecx,ecx
336 ld eax,[esp+4*3]
337 ld cl,[esp+4+4*3]
338 mul ecx
339 ld edx,eax
340 pop eax
341 and eax,0xff
342 add edx,eax
343 ld [esp+4*2],edx
344 pop ecx
345 pop edx
346 jr urfdebug_parse_number_loop
348 urfdebug_parse_number_complete:
349 dec esi ; back to the last non-number char
350 call urfdebug_skip_blanks
351 ld eax,[esp]
352 cp dword [esp+8],0
353 jr z,@f
354 neg eax
356 add esp,4*4
357 or eax,eax ; reset carry
360 urfdebug_parse_number_error:
361 add esp,4*3
362 pop esi
367 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
368 ;; find command
370 ;; IN:
371 ;; ESI: input buffer start
372 ;; OUT:
373 ;; found:
374 ;; ESI: next input buffer token (blanks skipped)
375 ;; EAX: command handler address
376 ;; ZERO RESET
377 ;; not found:
378 ;; ESI: unchanged
379 ;; EAX: 0
380 ;; ZERO SET
381 ;; all registers are preserved, except flags
383 urfdebug_find_command:
384 push esi ; in case we'll need to restore it
385 call urfdebug_skip_blanks
386 jz urfdebug_find_command_eol
387 ; save two reginster we will use as working set
388 push edi
389 push ecx
390 ; load table address
391 mov edi,urfdebug_command_table
392 urfdebug_find_command_loop:
393 ; ESI: input buffer
394 ; EDI: commant table
395 ; EAX,ECX: scratchpad
396 mov eax,[edi] ; handler address
397 or eax,eax
398 jz urfdebug_find_command_not_found
399 ; EAX contains handler address
400 ; save it for now
401 push eax
402 ; compare name
403 add edi,4 ; skip handler address
404 ; save esi, because we will need to restore it on failure
405 push esi
406 ; compare until different, or until table name terminates
407 urfdebug_find_command_cmpname_loop:
408 mov al,[esi] ; input byte
409 ; if space, convert to 0, so we can compare it with name terminator
410 cmp al,32+1
411 jnc urfdebug_find_command_cmpname_loop_good_al
412 mov al,0
413 urfdebug_find_command_cmpname_loop_good_al:
414 cmp al,[edi] ; compare with name byte
415 jnz urfdebug_find_command_cmpname_loop_failed
416 ; the bytes are the same, advance pointers
417 inc esi
418 inc edi
419 ; check for end-of-word
420 or al,al
421 jnz urfdebug_find_command_cmpname_loop
422 ; command name matches
423 ; ESI: after the command text, and the last space/0
424 ; EDI: after the command name in the table, and the trailing 0
425 ; on stack: original ESI, handler address
426 dec esi ; backup one char in case it was 0
427 call urfdebug_skip_blanks
428 pop eax ; drop original ESI, we don't need it anymore
429 pop eax ; this is our return result
430 ; restore trashed registers
431 pop ecx
432 pop edi
433 add esp,4 ; ignore saved oritinal ESI
436 urfdebug_find_command_cmpname_loop_failed:
437 ; ESI: in the command text
438 ; EDI: in the name text
439 ; on stack: original ESI, handler address
440 pop esi ; restore original ESI
441 pop eax ; we don't need it, so this is just "drop"
442 ; skip the rest of the name
443 urfdebug_find_command_cmpname_loop_failed_skip:
444 mov al,[edi]
445 inc edi
446 or al,al
447 jnz urfdebug_find_command_cmpname_loop_failed_skip
448 ; skip help text
450 mov al,[edi]
451 inc edi
452 or al,al
453 jnz @b
454 jmp urfdebug_find_command_loop
456 urfdebug_find_command_not_found:
457 pop ecx
458 pop edi
459 urfdebug_find_command_eol:
460 xor eax,eax
461 pop esi
465 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
466 if URFORTH_EXTRA_STACK_CHECKS
467 ;; flags and EAX are already saved; all others should be preserved
468 urfdebug_stack_check:
469 ld eax,[fvar_sp0_data]
470 sub eax,4*3
471 cmp esp,eax
472 jr be,.ok
473 ;jr b,.ok
474 ld dword [urfdebug_active_flag],1
475 .ok:
477 end if
480 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
481 ;; main debugger entry point
482 ;; all registers are preserved, including flags
484 urfdebug_next:
485 ; save eax and flags, check for early exit
486 pushfd
487 cmp dword [urfdebug_active_flag],0xffffffff
488 jr z,@f
489 push eax
490 if URFORTH_EXTRA_STACK_CHECKS
491 call urfdebug_stack_check
492 end if
493 cmp dword [urfdebug_active_flag],0
494 jr nz,urfdebug_active
495 ; special flag, means "no breakpoint checks"
496 ; check for breakpoints
497 ld eax,[urfdebug_sobp_address]
498 add eax,4 ; compensate for loaded dword
499 cp eax,EIP
500 jr z,urfdebug_active
501 ; check for rdepth breakpoint
502 cp ERP,[urfdebug_bp_rdepth]
503 jr z,urfdebug_active
504 ; no breakpoints, get out
505 pop eax
507 popfd
508 ;ret
509 jmp eax
511 ; debugger is active here
512 urfdebug_active:
513 ; save other registers
514 pop eax ; original eax
515 mov [urfsegfault_reg_eax],eax
516 pop eax ; flags
517 mov [urfsegfault_reg_flags],eax
518 mov [urfsegfault_reg_ebx],ebx
519 mov [urfsegfault_reg_ebx],ebx
520 mov [urfsegfault_reg_ecx],ecx
521 mov [urfsegfault_reg_edx],edx
522 mov [urfsegfault_reg_esi],esi
523 mov [urfsegfault_reg_edi],edi
524 mov [urfsegfault_reg_ebp],ebp
525 if 0
526 ; get return address
527 pop eax
528 ; and save it too
529 mov [urfsegfault_dbg_retaddr],eax
530 end if
531 ; now save stack pointer
532 mov [urfsegfault_reg_esp],esp
533 ; just in case, ensure that the direction flag is clear (autoincrement)
536 ; switch to our own stack
537 ;ld eax,[fvar_dp_data]
538 ;add eax,1024*128
539 ld eax,[urfsegfault_stack_bottom]
540 ld esp,eax
542 ; activate debugger
543 ;ld dword [urfdebug_active_flag],1
544 call urfdebug_activate_it
545 ; reset rdepth breakpoint
546 ld dword [urfdebug_bp_rdepth],0xffffffff
547 ; reset step-over breakpoint
548 ld dword [urfdebug_sobp_address],0xffffffff
550 ; main debugger loop
551 urfdebug_loop:
552 ; print debugger prompt, data stack depth, return stack depth
553 ld eax,[urfsegfault_reg_esp]
554 call urfsegfault_emit_hex_eax
555 urfsegfault_emit '|'
556 urfsegfault_printstr "UDBG:D("
558 ; print data stack depth
559 mov eax,[fvar_sp0_data]
560 sub eax,[urfsegfault_reg_esp]
561 sar eax,2 ; divide by cell
562 urfsegfault_printdec eax
564 ; print return stack depth
565 urfsegfault_printstr "):R("
566 mov eax,[fvar_rp0_data]
567 sub eax,[urfsegfault_reg_ebp]
568 sar eax,2 ; divide by cell
569 urfsegfault_printdec eax
571 ; print the word we're currently executing
572 urfsegfault_printstr "):"
573 mov eax,[urfsegfault_reg_esi]
574 sub eax,4 ; because we just loaded it
575 call urfsegfault_find_by_addr
576 ; print line number if we know it
577 push eax
578 ld esi,eax
579 ld eax,[urfsegfault_reg_esi]
580 sub eax,4 ; because we just loaded it
581 call urfsegfault_find_pc_line_nfa
582 or eax,eax
583 jr z,@f
584 urfsegfault_printstr "{"
585 call urfsegfault_emit_dec_eax
586 urfsegfault_printstr "}"
588 pop eax
589 ; done printing line
590 call urfsegfault_nfaprint
592 ; print the word we'll jump into
593 urfsegfault_printstr " -> "
594 mov eax,[urfsegfault_reg_eax]
595 call urfsegfault_find_by_addr
596 push eax
597 call urfsegfault_nfaprint
598 pop eax
599 ld esi,[urfsegfault_reg_esi]
600 call urfsegfault_nfa_print_arg
602 ; print current EIP
603 urfsegfault_printstr " | "
604 mov eax,[urfsegfault_reg_esi]
605 sub eax,4 ; because we just loaded it
606 call urfsegfault_emit_hex_eax
608 ; print next EIP
609 urfsegfault_printstr " -> "
610 mov eax,[urfsegfault_reg_eax]
611 call urfsegfault_emit_hex_eax
613 ; print the rest of the prompt
614 urfsegfault_printstr " |>"
616 ; read user command
617 call urfdebug_getline
618 ; ECX is length; ZERO SET on EOF
619 ; deactivate on EOF
620 jz urfdebug_deactivate
621 ; continue on empty command
622 or ecx,ecx
623 jz urfdebug_exit
625 mov esi,urfdebug_tib
626 call urfdebug_find_command
627 jz urfdebug_unknown_command
629 ;call uefdebug_call_eax
630 call eax
631 jmp urfdebug_loop
633 urfdebug_unknown_command:
634 ; for now, simply echo the command
635 mov esi,urfdebug_tib
636 urfdebug_echo_loop:
637 lodsb
638 or al,al
639 jz urfdebug_echo_loop_done
640 call urfsegfault_emit_al
641 jmp urfdebug_echo_loop
642 urfdebug_echo_loop_done:
643 ;urfsegfault_emit '|'
644 ;urfsegfault_cr
645 urfsegfault_printstrnl "? what?"
646 jmp urfdebug_loop
648 urfdebug_deactivate:
649 urfsegfault_cr
650 urfdebug_deactivate_nocr:
651 ;mov dword [urfdebug_active_flag],0
652 call urfdebug_deactivate_it
653 ; and exit
654 urfdebug_exit:
655 ; restore registers
656 mov ebx,[urfsegfault_reg_ebx]
657 mov ecx,[urfsegfault_reg_ecx]
658 mov edx,[urfsegfault_reg_edx]
659 mov esi,[urfsegfault_reg_esi]
660 mov edi,[urfsegfault_reg_edi]
661 mov ebp,[urfsegfault_reg_ebp]
662 ; restore flags
663 mov eax,[urfsegfault_reg_flags]
664 push eax
665 popfd
666 ; restore stack
667 mov esp,[urfsegfault_reg_esp]
668 if 0
669 ; push return address
670 mov eax,[urfsegfault_dbg_retaddr]
671 push eax
672 end if
673 ; restore eax, and exit
674 mov eax,[urfsegfault_reg_eax]
675 ;ret
676 jmp eax
679 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
680 ;; command handlers
681 ;; handler will be called with ESI pointing to unparsed arguments (blanks skipped)
683 urfdebug_command_table:
684 dd urfdebug_cmd_printdstack
685 db ".",0
686 db "dump data stack",0
687 dd urfdebug_cmd_printrstack
688 db ".r",0
689 db "dump return stack",0
690 dd urfdebug_cmd_step_out
691 db "sup",0
692 db "continue until current word returned",0
693 dd urfdebug_cmd_step_over
694 db "s",0
695 db "step over the current word",0
696 dd urfdebug_cmd_deactivate
697 db "c",0
698 db "deactivate debugger",0
700 dd urfdebug_cmd_drop
701 db "drop",0
702 db "drop number from data stack",0
703 dd urfdebug_cmd_swap
704 db "swap",0
705 db "swap numbers at data stack",0
706 dd urfdebug_cmd_push
707 db "push",0
708 db "push number at data stack",0
710 dd urfdebug_cmd_ddepth
711 db "depth",0
712 db "set data stack dump depth",0
713 dd urfdebug_cmd_rdepth
714 db "rdepth",0
715 db "set return stack dump depth",0
717 dd urfdebug_cmd_see
718 db "see",0
719 db "decompile forth word",0
721 dd urfdebug_cmd_fcall
722 db "fcall",0
723 db "call forth word",0
725 dd urfdebug_cmd_help
726 db "h",0
727 db "show this help",0
728 dd 0
731 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
732 ;; drop value from data stack
734 urfdebug_cmd_drop:
735 ld eax,[fvar_sp0_data]
736 sub eax,[urfsegfault_reg_esp]
737 jp nc,@f
738 ; underflow
739 urfsegfault_printstrnl "stack underflowed"
742 ret z
743 ; drop value
744 ld edi,[urfsegfault_reg_esp]
745 ; set new TOS
746 ld eax,[edi]
747 ld [urfsegfault_reg_ecx],eax
748 add edi,4
749 ld [urfsegfault_reg_esp],edi
753 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
754 ;; swap values on data stack
756 urfdebug_cmd_swap:
757 ld eax,[fvar_sp0_data]
758 sub eax,[urfsegfault_reg_esp]
759 jp nc,@f
760 ; underflow
761 urfsegfault_printstrnl "stack underflowed"
764 cp al,2
765 jr nc,@f
766 urfsegfault_printstrnl "stack should have at least two values"
769 ; swap values
770 ld edi,[urfsegfault_reg_esp]
771 ld eax,[edi]
772 ld ecx,[urfsegfault_reg_ecx]
773 ld [edi],ecx
774 ld [urfsegfault_reg_ecx],eax
778 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
779 ;; push value on data stack
781 urfdebug_cmd_push:
782 ld eax,[fvar_sp0_data]
783 sub eax,[urfsegfault_reg_esp]
784 jp nc,@f
785 ; underflow
786 urfsegfault_printstrnl "stack underflowed"
789 urfdebug_cmd_push_parse_loop:
790 call urfdebug_skip_blanks
791 ret z
792 call urfdebug_parse_number
793 jr nc,@f
794 urfsegfault_printstrnl "not a number!"
797 ld edi,[urfsegfault_reg_esp]
798 ld ecx,[urfsegfault_reg_ecx]
799 sub edi,4
800 ld [edi],ecx
801 ld [urfsegfault_reg_ecx],eax
802 ld [urfsegfault_reg_esp],edi
803 jr urfdebug_cmd_push_parse_loop
806 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
807 ;; show command help
809 urfdebug_cmd_help:
810 urfsegfault_printstrnl "------ HELP ------"
811 ld esi,urfdebug_command_table
812 urfdebug_cmd_help_loop:
813 lodsd
814 or eax,eax
815 ret z
816 ld eax,esi
817 urfsegfault_printstr " "
818 call urfdebug_emit_str_asciiz_eax
819 urfsegfault_emit 9
820 call urfdebug_emit_str_asciiz_eax
821 urfsegfault_cr
822 ld esi,eax
823 jr urfdebug_cmd_help_loop
826 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
827 ;; deactivate until current word exited
829 urfdebug_cmd_step_out:
830 ; check return stack depth
831 ld eax,[fvar_rp0_data]
832 cmp eax,[urfsegfault_reg_ebp]
833 jr z,urfdebug_cmd_step_out_cannot
834 jr c,urfdebug_cmd_step_out_cannot
835 ld eax,[urfsegfault_reg_ebp]
836 add eax,4 ; one level up
837 ld [urfdebug_bp_rdepth],eax
838 ; done, deactivate debugger
839 ;;jp urfdebug_deactivate_nocr
840 ld dword [urfdebug_active_flag],0
841 jp urfdebug_exit
842 urfdebug_cmd_step_out_cannot:
843 urfsegfault_printstrnl "cannot step out, return stack is empty!"
847 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
848 ;; perform "step over"
850 urfdebug_cmd_step_over:
851 ; sanity check, just in case
852 cp dword [urfdebug_sobp_address],0xffffffff
853 jr nz,urfdebug_cmd_step_over_error
854 ; check if the next address is still in the current word
855 ; that next address is actually in EIP
856 mov eax,[urfsegfault_reg_esi]
857 call urfsegfault_find_by_addr
858 jr z,urfdebug_cmd_step_over_bad_addr
859 mov ecx,eax
860 mov eax,[urfsegfault_reg_esi]
861 sub eax,4 ; current instruction
862 call urfsegfault_find_by_addr
863 jr z,urfdebug_cmd_step_over_bad_addr
864 cp eax,ecx
865 jr nz,urfdebug_cmd_step_over_bad_addr
866 ; get flags
867 mov eax,[urfsegfault_reg_eax]
868 call urfsegfault_find_by_addr
869 jr z,urfdebug_cmd_step_over_bad_addr
870 ld edi,eax
871 movzx eax,word [edi+2]
872 test eax,FLAG_NORETURN
873 jr nz,urfdebug_cmd_step_over_noreturn
874 ; load argument type
875 movzx eax,byte [edi+1]
876 ld esi,[urfsegfault_reg_esi]
877 ; check instruction argument type
878 cp al,WARG_BRANCH
879 jr z,urfdebug_cmd_step_over_branch
880 ; skip argument
881 call urfsegfault_nfa_skip_arg_type_in_eax
882 urfdebug_cmd_step_over_set_sobp:
883 ; setup debugger activation address
884 ld dword [urfdebug_sobp_address],esi
885 ; done, deactivate debugger
886 ;;jp urfdebug_deactivate_nocr
887 ld dword [urfdebug_active_flag],0
888 jp urfdebug_exit
889 urfdebug_cmd_step_over_error:
890 urfsegfault_printstrnl "step-over breakpoint already set for some strange reason!"
892 urfdebug_cmd_step_over_bad_addr:
893 ;urfsegfault_printstrnl "cannot step-over outside of the word!"
894 ;ret
895 ; just continue into it
896 jr urfdebug_exit
897 urfdebug_cmd_step_over_branch:
898 ;urfsegfault_printstrnl "cannot step over branches yet!"
899 ;ret
900 ; just continue into it
901 jr urfdebug_exit
902 urfdebug_cmd_step_over_noreturn:
903 ;urfsegfault_printstrnl "cannot step over NORETURN words!"
904 ;ret
905 ; just continue into it
906 jr urfdebug_exit
909 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
910 ;; print return stack
912 urfdebug_cmd_printrstack:
913 push dword [urfsegfault_dump_rdepth]
914 ld eax,[urfdebug_dump_rdepth]
915 ld [urfsegfault_dump_rdepth],eax
916 call urfsegfault_cmd_printrstack
917 pop dword [urfsegfault_dump_rdepth]
921 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
922 ;; print data stack
924 urfdebug_cmd_printdstack:
925 push dword [urfsegfault_dump_ddepth]
926 ld eax,[urfdebug_dump_ddepth]
927 ld [urfsegfault_dump_ddepth],eax
928 call urfsegfault_cmd_printdstack
929 pop dword [urfsegfault_dump_ddepth]
933 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
934 ;; deactivate debugger
936 urfdebug_cmd_deactivate:
937 jmp urfdebug_deactivate_nocr
940 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
941 ;; set data stack dump depth
943 urfdebug_cmd_ddepth:
944 call urfdebug_parse_number
945 jr nc,@f
946 urfsegfault_printstrnl "number expected!"
949 cp eax,2
950 jr nc,@f
951 urfsegfault_printstrnl "minimum value is 2!"
954 cp eax,201
955 jr c,@f
956 urfsegfault_printstrnl "minimum value is 200!"
959 dec eax
960 ld [urfdebug_dump_ddepth],eax
964 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
965 ;; set return stack dump depth
967 urfdebug_cmd_rdepth:
968 call urfdebug_parse_number
969 jr nc,@f
970 urfsegfault_printstrnl "number expected!"
973 cp eax,2
974 jr nc,@f
975 urfsegfault_printstrnl "minimum value is 2!"
978 cp eax,201
979 jr c,@f
980 urfsegfault_printstrnl "minimum value is 200!"
983 dec eax
984 ld [urfdebug_dump_rdepth],eax
988 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
989 ;; decompile forth word
991 urfdebug_cmd_see:
992 ; find argument length
993 ld edi,esi
994 urfdebug_cmd_see_arglen_loop:
995 lodsb
996 cp al,33
997 jr nc,urfdebug_cmd_see_arglen_loop
998 dec esi
999 ld ecx,esi
1000 sub ecx,edi
1001 jr nz,@f
1002 urfsegfault_printstrnl "what word?"
1005 ; save starting address and length
1006 push edi
1007 push ecx
1008 ; call (WFIND-STR)
1009 push edi ; addr
1010 mov ebp,[urfsegfault_reg_ebp]
1011 ld eax,fword_par_wfind_str
1012 call ur_mc_fcall
1013 or TOS,TOS
1014 jr nz,@f
1015 pop edx
1016 pop ecx
1017 call urfsegfault_emit_str_ecx_edx_safe
1018 urfsegfault_printstrnl "? what is it?"
1021 pop ebx ; word cfa
1022 pop edx ; saved count
1023 pop ecx ; saved address
1024 call urfsegfault_emit_str_ecx_edx_safe
1025 urfsegfault_printstr " is "
1026 ld eax,ebx
1027 call urfsegfault_find_by_addr
1028 or eax,eax
1029 jnz @f
1030 urfsegfault_printstrnl "some shit"
1033 ;ld esi,eax ; save cfa
1034 ;call urfsegfault_cfa2nfa
1035 ld [urfdebug_cmd_see_nfa],eax ; we'll need it for debug info printing
1036 call urfsegfault_nfaprint
1037 urfsegfault_printstr " at (nfa) 0x"
1038 call urfsegfault_emit_hex_eax
1039 urfsegfault_cr
1041 ; now decompile it
1042 ld eax,[urfsegfault_fba_nfa]
1043 call urfsegfault_nfa2cfa
1044 mov esi,eax
1045 lodsb
1046 cp al,0xe8 ; call?
1047 jr nz,urfdebug_cmd_see_notforth
1048 lodsd
1049 ld ecx,esi
1050 add ecx,eax
1051 cp ecx,fword_par_urforth_nocall_doforth
1052 jr nz,urfdebug_cmd_see_notforth
1053 urfdebug_cmd_see_loop:
1054 cmp esi,[urfsegfault_fba_end]
1055 jr nc,urfdebug_cmd_see_loop_done
1056 ; print address (and store it on the stack)
1057 ld eax,esi
1058 push eax
1059 call urfsegfault_emit_hex_eax
1060 urfsegfault_printstr ": "
1061 lodsd ; cfa
1062 call urfsegfault_cfa2nfa
1063 call urfsegfault_nfaprint
1064 ; print arguments
1065 call urfsegfault_nfa_print_arg
1066 ; print source code line
1067 pop eax
1068 push esi
1069 ld esi,[urfdebug_cmd_see_nfa]
1070 call urfsegfault_find_pc_line_nfa
1071 or eax,eax
1072 jr z,@f
1073 push eax
1074 urfsegfault_printstr 9,"; line #"
1075 pop eax
1076 call urfsegfault_emit_dec_eax
1078 pop esi
1079 ; done
1080 urfsegfault_cr
1081 jr urfdebug_cmd_see_loop
1083 urfdebug_cmd_see_loop_done:
1084 urfsegfault_cr
1087 urfdebug_cmd_see_notforth:
1088 urfsegfault_printstrnl "cannot decompile non-Forth words yet."
1091 urfdebug_cmd_see_nfa: dd 0
1094 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1095 ;; call forth words
1097 urfdebug_cmd_fcall:
1098 ; find argument length
1099 call urfdebug_skip_blanks
1100 ld edi,esi
1102 lodsb
1103 cp al,33
1104 jr nc,@b
1105 dec esi
1106 ld ecx,esi
1107 sub ecx,edi
1108 jr nz,@f
1109 ;urfsegfault_printstrnl "what word?"
1112 ; save starting address and length
1113 push edi
1114 push ecx
1115 ; call (WFIND-STR)
1116 push edi ; addr
1117 mov ebp,[urfsegfault_reg_ebp]
1118 ld eax,fword_par_wfind_str
1119 call ur_mc_fcall
1120 or TOS,TOS
1121 jr nz,@f
1122 ; try to parse it as a number
1123 pop ecx
1124 pop esi
1125 push esi
1126 push ecx
1127 call urfdebug_parse_number
1128 jr c,.nan
1129 ; number, push it
1130 ld edi,[urfsegfault_reg_esp]
1131 ld ecx,[urfsegfault_reg_ecx]
1132 sub edi,4
1133 ld [edi],ecx
1134 ld [urfsegfault_reg_ecx],eax
1135 ld [urfsegfault_reg_esp],edi
1136 pop ecx
1137 pop edi
1138 ; esi is ok here
1139 jr urfdebug_cmd_fcall
1140 .nan:
1141 pop edx
1142 pop ecx
1143 call urfsegfault_emit_str_ecx_edx_safe
1144 urfsegfault_printstrnl "? what is it?"
1147 pop eax ; word cfa
1148 ; switch data stacks
1149 ld [urfdebug_cmd_fcall_esp],esp
1150 ld esp,[urfsegfault_reg_esp]
1151 ld TOS,[urfsegfault_reg_ecx]
1152 call ur_mc_fcall
1153 ld [urfsegfault_reg_ecx],TOS
1154 ld [urfsegfault_reg_esp],esp
1155 ld esp,[urfdebug_cmd_fcall_esp]
1156 ; skip word and continue
1157 pop ecx
1158 pop esi
1159 add esi,ecx
1160 jr urfdebug_cmd_fcall
1162 urfdebug_cmd_fcall_esp: dd 0
1165 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1166 urfdebug_tib: db 256 dup(0)
1167 urfdebug_tib_size = $-urfdebug_tib