1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/TADS2/unix/dbgu.c,v 1.3 1999/07/11 00:46:39 MJRoberts Exp $";
4 #endif
5
6 /* Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. */
7 /*
8 Name
9 dbgu.c - user interface for debugger
10 Function
11 Implements the user interface for the command-line version of the
12 debugger. This portion of the debugger performs only user-interface
13 functions; the UI-indenpendent section actually performs most of
14 the work of debugging.
15 Notes
16 None
17 Modified
18 04/12/92 MJRoberts - creation
19 */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <ctype.h>
25 #include "os.h"
26 #include "dbg.h"
27 #include "tio.h"
28 #include "obj.h"
29 #include "prp.h"
30 #include "tok.h"
31 #include "run.h"
32 #include "lin.h"
33 #include "linf.h"
34 #include "osdbg.h"
35
36 #ifdef UNIX
37 #include "osdbg_un.h"
38
39 #define sdesc_color text_normal_color
40 extern int watchpoint_color;
41 extern int breakpoint_color;
42 extern int filename_color;
43 #endif
44
45 #ifdef MICROSOFT
46 extern char **__argv;
47 #define ARG0 __argv[0]
48 #endif /* MICROSOFT */
49
50 #ifdef __IBMC__
51 extern char **_argv;
52 void osdbgpt( oswdef *, char *fmt, ... );
53 #endif /* __IBMC__ */
54
55 #ifdef __EMX__
56 # define ARG0 (char *)0
57 #endif /* __EMX__ */
58
59 #ifndef ARG0
60 # define ARG0 _argv[0]
61 #endif /* !ARG0 */
62
63 /* some special characters for PC's */
64 #define DBGUHLIN 196 /* horizontal line */
65 #define DBGARROW 16 /* right-pointing arrow */
66 #define DBGBPCHAR 15 /* breakpoint indicator character */
67
68 #ifdef UNIX
69 #undef DBGUHLIN
70 #undef DBGARROW
71 #undef DBGBPCHAR
72
73 #define DBGUHLIN '-'
74 #define DBGARROW '>'
75 #define DBGBPCHAR '*'
76 #endif
77
78 /* debug text, status, source, and watch windows */
79 static oswdef dbgtxtwin;
80 static oswdef dbgstawin;
81 static oswdef dbgsrcwin;
82 static oswdef dbgwatwin;
83
84 /* space for lindef containing arbitrary file not part of game source */
85 static char filesrc[sizeof(linfdef) + 128];
86
87 /* location in source file of top current source window and current line */
88 static uchar dbgsrctop[LINLLNMAX]; /* current display seek location */
89 static uchar dbgsrccsr[LINLLNMAX]; /* current cursor line */
90 static uchar dbgsrcloc[LINLLNMAX]; /* current source line in file */
91 static uchar dbgsrchome[LINLLNMAX]; /* seek location of "home" screen */
92
93 /* lindef for current source file */
94 static lindef *dbgsrclin; /* lindef for currently displayed file */
95 static lindef *dbgsrclinhome; /* lindef of "home" file */
96
97 /* color for current line/breakpoint line in source window */
98 static int dbgcurcolor = 30;
99 static int dbgbpcolor = 0x47;
100
101 /* private breakpoint data - maintained with dbgbpeach() */
102 static int dbgbpcnt; /* number of breakpoints set */
103 typedef struct dbgubpdef dbgubpdef;
104 static struct dbgubpdef
105 {
106 int dbgubpid; /* line source id */
107 char dbgubpseek[LINLLNMAX]; /* seek position of breakpoint */
108 }
109 dbgbps[DBGBPMAX]; /* seek locations of breakpoints */
110
111 /* forward declarations */
112 static void dbgugetbp(/*_ dbgcxdef *ctx _*/);
113
114
115 /* move back or forward by one or more lines within a file */
116 static char *dbgbuf;
117 static size_t dbgbufsiz;
dbgmove(lin,cur,destlinep)118 static void dbgmove(lin, cur, destlinep)
119 lindef *lin;
120 int cur;
121 int *destlinep;
122 {
123 int dest = *destlinep;
124 char *p;
125 size_t max;
126 long backup;
127 long delta;
128 uchar pos[LINLLNMAX];
129 uchar prvpos[LINLLNMAX];
130
131 /* find current location in line source */
132 lintell(lin, pos);
133
134 if (cur < dest)
135 {
136 while (cur != dest)
137 {
138 max = linread(lin, dbgbuf, (int)dbgbufsiz);
139 if (max == 0) break;
140 for (delta = 0, p = dbgbuf ; p < dbgbuf + max && cur != dest ;
141 ++p, ++delta)
142 if (*p == '\n') ++cur;
143 linpadd(lin, pos, delta);
144 }
145 }
146 else
147 {
148 ++cur;
149 while (cur != dest)
150 {
151 backup = linofs(lin);
152 if (backup > dbgbufsiz) backup = dbgbufsiz;
153 memcpy(prvpos, pos, (size_t)sizeof(prvpos));
154 linpadd(lin, prvpos, -(long)dbgbufsiz);
155 linseek(lin, prvpos);
156 max = linread(lin, dbgbuf, backup);
157 if (max == 0) break;
158 p = dbgbuf + max;
159 delta = 0;
160 do
161 {
162 --p;
163 --delta;
164 if (*p == '\n') --cur;
165 } while (p > dbgbuf && cur != dest);
166 linpadd(lin, pos, delta);
167 if (linqtop(lin, prvpos)) break;
168 }
169 if (!linqtop(lin, pos)) linpadd(lin, pos, (long)1);
170 else cur = 1;
171 }
172
173 linseek(lin, pos);
174 *destlinep = cur;
175 }
176
177 /* show current source page */
dbgsrcshow(lin,win,top,loc)178 static void dbgsrcshow(lin, win, top, loc)
179 lindef *lin;
180 oswdef *win;
181 uchar *top;
182 uchar *loc;
183 {
184 int y;
185 char outbuf[128];
186 int wid = dbgsrcwin.oswx2 - dbgsrcwin.oswx1 + 1;
187 extern int sdesc_color, text_color;
188 char prefix[3];
189 uchar curloc[LINLLNMAX];
190 int i;
191 dbgubpdef *bp;
192 int thisid;
193
194 thisid = lin ? lin->linid : 0;
195 prefix[2] = '\0';
196 osdbgclr(win);
197 for (y = win->oswy1 ; y <= win->oswy2 ; ++y)
198 {
199 win->oswx = win->oswx1;
200 win->oswy = y;
201 linftell(lin, curloc);
202
203 /* special display if this is the currently executing line */
204 if (!memcmp(curloc, loc, lin->linlln) && lin == dbgsrclin)
205 {
206 win->oswcolor = dbgcurcolor;
207 prefix[1] = DBGARROW;
208 }
209 else
210 {
211 win->oswcolor = sdesc_color;
212 prefix[1] = ' ';
213 }
214
215 /* special display if there is a breakpoint at this line */
216 prefix[0] = ' '; /* presume nothing special about this line */
217 for (i = dbgbpcnt, bp = dbgbps ; i ; ++bp, --i)
218 {
219
220 if (!memcmp(bp->dbgubpseek, curloc, lin->linlln)
221 && (bp->dbgubpid == thisid))
222 {
223 prefix[0] = DBGBPCHAR;
224 win->oswcolor = dbgbpcolor;
225 break;
226 }
227 }
228
229 /* display prefix with current execution/breakpoint info */
230 osdbgpt(win, "%s", prefix);
231
232 /* display the line itself */
233 if (!lingets(lin, outbuf, sizeof(outbuf)))
234 break;
235 else
236 {
237 int l;
238
239 l = strlen(outbuf);
240 while (l && outbuf[l-1] == '\n' || outbuf[l-1] == '\r')
241 --l;
242
243 if (l + 2 >= wid)
244 outbuf[wid - 2] = '\0';
245 else
246 {
247 outbuf[l] = '\0';
248 if (prefix[0] == DBGBPCHAR)
249 {
250 /* breakpoint - pad line out with spaces to full width */
251 while (l < wid - 2) outbuf[l++] = ' ';
252 outbuf[l] = '\0';
253 }
254 }
255 osdbgpt(win, "%s", outbuf);
256 if (y != win->oswy2) osdbgpt(win, "\n");
257 }
258 }
259
260 /* restore normal color */
261 win->oswcolor = sdesc_color;
262 }
263
264 /* search for a string in a line source */
dbgusearch(lin,pos,toppos,win,str)265 static void dbgusearch(lin, pos, toppos, win, str)
266 lindef *lin;
267 uchar *pos; /* in: starting position; out: final position */
268 uchar *toppos; /* location of top of screen */
269 oswdef *win;
270 char *str;
271 {
272 int dest;
273
274 /* search is case-insensitive - convert search string to lowercase */
275 os_strlwr(str);
276
277 /* seek to starting location, and read and discard current line */
278 linseek(lin, pos);
279 if (!lingets(lin, dbgbuf, (int)dbgbufsiz)) return;
280
281 for (;;)
282 {
283 lintell(lin, pos);
284 if (!lingets(lin, dbgbuf, (int)dbgbufsiz)) break;
285 os_strlwr(dbgbuf);
286 if (strstr(dbgbuf, str)) break;
287 }
288
289 /* move search expression to middle of screen */
290 linseek(lin, pos);
291 dest = 1;
292 dbgmove(lin, (win->oswy2 - win->oswy1)/2, &dest);
293 lintell(lin, toppos);
294 dbgsrcshow(lin, win, toppos, dbgsrcloc);
295 }
296
297 /* redraw status line */
298 static char dbg_curstat[81];
dbgustat(ctx)299 static void dbgustat(ctx)
300 dbgcxdef *ctx;
301 {
302 VARUSED(ctx);
303
304 dbgstawin.oswx = dbgstawin.oswx1;
305 dbgstawin.oswy = dbgstawin.oswy1;
306 osdbgpt(&dbgstawin, dbg_curstat);
307 }
308
309 /* show the current line */
dbgulsho(ctx,buf)310 static void dbgulsho(ctx, buf)
311 dbgcxdef *ctx;
312 uchar *buf; /* line information retrieved by dbglget */
313 {
314 lindef *lin;
315 char outbuf[128];
316 int cur;
317 int dest;
318 uchar loc[LINLLNMAX];
319 int y;
320 char fnam[182];
321 int i;
322
323 /* search for a line source with this id */
324 for (lin = ctx->dbgcxlin ; lin ; lin = lin->linnxt)
325 if (lin->linid == *buf) break;
326 dbgswitch(&dbgsrclin, lin);
327 dbgsrclinhome = lin;
328
329 if (!lin)
330 {
331 osdbgclr(&dbgsrcwin);
332 osdbgpt(&dbgsrcwin, "*** source line not found ***\n");
333 return;
334 }
335
336 /* seek to line in file, then back up by half a screen to center line */
337 memcpy(loc, buf+1, lin->linlln);
338 linseek(lin, loc);
339 cur = (dbgsrcwin.oswy2 - dbgsrcwin.oswy1) / 2;
340 dest = 1;
341 dbgmove(lin, cur, &dest);
342 lintell(lin, dbgsrctop);
343 memcpy(dbgsrchome, dbgsrctop, (size_t)lin->linlln);
344 memcpy(dbgsrcloc, loc, (size_t)lin->linlln);
345 memcpy(dbgsrccsr, loc, (size_t)lin->linlln);
346 dbgsrcshow(lin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
347
348 /* update status line to show current file */
349 for (i = 0 ; i < 5 ; ++i) dbg_curstat[i] = DBGUHLIN;
350
351 linnam(lin, fnam);
352 sprintf(&dbg_curstat[5], "%s ", fnam);
353 dbgwhere(ctx, &dbg_curstat[strlen(dbg_curstat)]);
354
355 for (i = strlen(dbg_curstat) ;
356 i <= dbgstawin.oswx2 - dbgstawin.oswx1 ; ++i)
357 dbg_curstat[i] = DBGUHLIN;
358 dbg_curstat[i] = '\0';
359
360 /* actually display the status line now */
361 dbgustat(ctx);
362 }
363
364 /* display callback */
dbgudisp(ctx,str,strl)365 static void dbgudisp(ctx, str, strl)
366 void *ctx;
367 char *str;
368 int strl;
369 {
370 oswdef *win = (oswdef *)ctx;
371 int oldpg;
372
373 oldpg = ossvpg(1); /* display values on debugger video page */
374 osdbgpt(win, "%.*s", strl, str);
375 ossvpg(oldpg); /* restore previous page */
376 }
377
378 /* display an error message */
dbguerr(ctx,errno,msg)379 void dbguerr(ctx, errno, msg)
380 dbgcxdef *ctx;
381 int errno;
382 char *msg;
383 {
384 int oldpg;
385 char err_prefix[64];
386
387 VARUSED(ctx);
388
389 oldpg = ossvpg(1); /* figure out what page we're on */
390 ossvpg(oldpg); /* restore previous page */
391 if (oldpg == 0) { /* if we're on the game page... */
392 sprintf(err_prefix, "[TADS-%d: ", errno);
393 os_printz(err_prefix);
394 os_printz(msg);
395 os_printz("]\n");
396 }
397 else
398 osdbgpt(&dbgtxtwin, "TADS-%d: %s\n", errno, msg);
399 }
400
401 /* show user screen, wait for key hit, then change back to dbg screen */
dbguuser(ctx)402 static void dbguuser(ctx)
403 dbgcxdef *ctx;
404 {
405 VARUSED(ctx);
406
407 ossvpg(0);
408 os_waitc();
409 ossvpg(1);
410 }
411
412 /* execute a special keyboard command key */
dbgukbc(ctx,cmd)413 static int dbgukbc(ctx, cmd)
414 dbgcxdef *ctx;
415 char cmd;
416 {
417 int cur = dbgsrcwin.oswy2;
418 int dest = cur;
419 uchar newpos[LINLLNMAX];
420 int scrht = dbgsrcwin.oswy2 - dbgsrcwin.oswy1 - 2;
421
422 switch(cmd)
423 {
424 case CMD_F5: /* show user screen */
425 dbguuser(ctx);
426 return(0);
427
428 case CMD_TAB: /* other screen */
429 case CMD_F7: /* step into */
430 case CMD_F8: /* step over */
431 case CMD_F9: /* go */
432 case CMD_SCR: /* help */
433 return(cmd);
434 }
435
436 /* the remaining commands pertain to the source display only */
437 switch(cmd)
438 {
439 case CMD_CHOME:
440 if (dbgsrclinhome
441 && (dbgsrctop != dbgsrchome || dbgsrclin != dbgsrclinhome))
442 {
443 dbgswitch(&dbgsrclin, dbgsrclinhome);
444
445 linseek(dbgsrclin, dbgsrchome);
446 memcpy(dbgsrctop, dbgsrchome, dbgsrclin->linlln);
447 memcpy(dbgsrccsr, dbgsrcloc, dbgsrclin->linlln);
448 dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
449 }
450 break;
451
452 case CMD_TOP:
453 if (dbgsrctop != 0)
454 {
455 lingoto(dbgsrclin, LINGOTOP);
456 lintell(dbgsrclin, dbgsrccsr);
457 memcpy(dbgsrctop, dbgsrccsr, dbgsrclin->linlln);
458 dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
459 }
460 break;
461
462 case CMD_BOT:
463 lingoto(dbgsrclin, LINGOEND);
464 dest -= scrht;
465 goto move_noseek;
466
467 case CMD_UP:
468 --dest;
469 goto move_source;
470
471 case CMD_DOWN:
472 ++dest;
473 goto move_source;
474
475 case CMD_PGUP:
476 dest -= scrht;
477 goto move_source;
478
479 case CMD_PGDN:
480 dest += scrht;
481 move_source:
482 linseek(dbgsrclin, dbgsrctop);
483 move_noseek:
484 dbgmove(dbgsrclin, cur, &dest);
485 lintell(dbgsrclin, newpos);
486 if (memcmp(dbgsrctop, newpos, dbgsrclin->linlln))
487 {
488 memcpy(dbgsrccsr, newpos, dbgsrclin->linlln);
489 memcpy(dbgsrctop, newpos, dbgsrclin->linlln);
490 dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
491 }
492 break;
493 }
494 return(0);
495 }
496
497 /* get commands in source window */
dbgurunsrc(ctx)498 static int dbgurunsrc(ctx)
499 dbgcxdef *ctx;
500 {
501 int y;
502 char c;
503 oswdef *win = &dbgsrcwin;
504 uchar pos[LINLLNMAX];
505 uchar newpos[LINLLNMAX];
506 int dest;
507 int cur;
508 int scrht = win->oswy2 - win->oswy1 - 2;
509 char *cond;
510 char condbuf[128];
511
512 /* figure out where we are in the source window */
513 find_srcpos:
514 linseek(dbgsrclin, dbgsrctop);
515 memcpy(pos, dbgsrctop, dbgsrclin->linlln);
516 for (y = win->oswy1 ; y < win->oswy2 ; ++y)
517 {
518 lintell(dbgsrclin, newpos);
519 if (!memcmp(dbgsrccsr, newpos, dbgsrclin->linlln)) break;
520 lingets(dbgsrclin, dbgbuf, (int)dbgbufsiz);
521 }
522
523 /* get and process commands */
524 for (;;)
525 {
526 ossdbgloc(y, 2); /* locate cursor properly */
527 cond = (char *)0; /* no condition for breakpoints, by default */
528
529 c = os_getc();
530 switch(c)
531 {
532 case '/':
533 return(CMD_UP);
534
535 case '\0':
536 c = os_getc();
537 switch(c)
538 {
539 case CMD_TAB:
540 case CMD_EOF:
541 return(0);
542
543 case CMD_CHOME:
544 if (dbgsrclinhome
545 && (memcmp(dbgsrctop, dbgsrchome, dbgsrclin->linlln)
546 || dbgsrclin != dbgsrclinhome))
547 {
548 dbgswitch(&dbgsrclin, dbgsrclinhome);
549
550 linseek(dbgsrclin, dbgsrchome);
551 memcpy(pos, dbgsrchome, dbgsrclin->linlln);
552 memcpy(dbgsrctop, dbgsrchome, dbgsrclin->linlln);
553 memcpy(dbgsrccsr, dbgsrcloc, dbgsrclin->linlln);
554 dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
555 goto find_srcpos;
556 }
557 break;
558
559 case CMD_UP:
560 if (y > win->oswy1)
561 {
562 --y;
563 goto find_cur_y;
564 }
565 else
566 {
567 dest = 1;
568 cur = 2;
569 goto move_source;
570 }
571
572 case CMD_DOWN:
573 if (y < win->oswy2)
574 {
575 ++y;
576 goto find_cur_y;
577 }
578 else
579 {
580 dest = 2;
581 cur = 1;
582 goto move_source;
583 }
584
585 case CMD_PGUP:
586 cur = scrht;
587 dest = 1;
588 goto move_source;
589
590 case CMD_PGDN:
591 cur = 1;
592 dest = scrht;
593 move_source:
594 linseek(dbgsrclin, pos);
595 move_noseek:
596 dbgmove(dbgsrclin, cur, &dest);
597 lintell(dbgsrclin, newpos);
598 if (memcmp(pos, newpos, dbgsrclin->linlln))
599 {
600 int i;
601
602 memcpy(dbgsrctop, newpos, dbgsrclin->linlln);
603 memcpy(pos, newpos, dbgsrclin->linlln);
604 dbgsrcshow(dbgsrclin, win, pos, dbgsrcloc);
605
606 /* figure out where dbgsrccsr is */
607 find_cur_y:
608 linseek(dbgsrclin, pos);
609 for (i = win->oswy1 ; i < y ; ++i)
610 lingets(dbgsrclin, dbgbuf, (int)dbgbufsiz);
611 lintell(dbgsrclin, dbgsrccsr);
612 }
613 break;
614
615 case CMD_TOP:
616 if (pos != 0)
617 {
618 lingoto(dbgsrclin, LINGOTOP);
619 lintell(dbgsrclin, pos);
620 memcpy(dbgsrccsr, pos, dbgsrclin->linlln);
621 memcpy(dbgsrctop, pos, dbgsrclin->linlln);
622 y = win->oswy1;
623 dbgsrcshow(dbgsrclin, win, pos, dbgsrcloc);
624 }
625 break;
626
627 case CMD_BOT:
628 lingoto(dbgsrclin, LINGOEND);
629 dest = 1;
630 cur = scrht;
631 goto move_noseek;
632
633 case CMD_HOME:
634 y = win->oswy1;
635 goto find_cur_y;
636
637 case CMD_END:
638 y = win->oswy2;
639 goto find_cur_y;
640
641 case CMD_SF2:
642 if (dbgsrclin)
643 {
644 osdbgpt(&dbgtxtwin, "\nCondition: ");
645 while (osdbggts(&dbgtxtwin, condbuf, dbgukbc, ctx));
646 cond = condbuf;
647 /* FALLTHROUGH */
648 }
649 else break;
650
651 case CMD_F2:
652 if (dbgsrclin)
653 {
654 objnum objn;
655 uint ofs;
656 int newbp;
657 char nambuf[128];
658 int len;
659
660 linfind(dbgsrclin, dbgsrccsr, &objn, &ofs);
661
662 memcpy(nambuf, "<F2> ", (size_t)4);
663 len = dbgnam(ctx, nambuf+4, TOKSTOBJ, objn);
664 if (!memcmp(nambuf+4, "<UNKNOWN>", (size_t)len))
665 len = dbgnam(ctx, nambuf+4, TOKSTFUNC, objn);
666 if (cond)
667 {
668 memcpy(nambuf + 4 + len, " when ", (size_t)6);
669 strcpy(nambuf + 10 + len, cond);
670 }
671 else
672 nambuf[len + 4] = '\0';
673
674 (void)dbgbpat(ctx, objn, MCMONINV, ofs, &newbp, nambuf,
675 TRUE, cond, NULL);
676 dbgugetbp(ctx);
677 }
678 break;
679
680 case CMD_F5:
681 dbguuser(ctx);
682 break;
683
684 case CMD_F7:
685 case CMD_F8:
686 case CMD_F9:
687 return(c);
688 }
689 }
690 }
691 }
692
693 /* callback for dbgugetp */
dbgubp(ctx,id,buf,bufsiz)694 static void dbgubp(ctx, id, buf, bufsiz)
695 dbgcxdef *ctx;
696 int id;
697 uchar *buf;
698 uint bufsiz;
699 {
700 dbgubpdef *bp = &dbgbps[dbgbpcnt++];
701
702 bp->dbgubpid = id;
703 memcpy(bp->dbgubpseek, buf, (size_t)bufsiz);
704 }
705
706 /* reset private breakpoint data - call after setting/clearing bp */
dbgugetbp(ctx)707 static void dbgugetbp(ctx)
708 dbgcxdef *ctx;
709 {
710 dbgbpcnt = 0;
711 dbgbpeach(ctx, dbgubp, ctx);
712 if (dbgsrclin)
713 {
714 linseek(dbgsrclin, dbgsrctop);
715 dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
716 }
717 }
718
719 /* output support for hidden output */
trchid()720 void trchid()
721 {
722 outflushn(1);
723 os_printz("---Hidden output---\n");
724 }
trcsho()725 void trcsho()
726 {
727 outflushn(1);
728 os_printz("---End of hidden output, normal output resumes---\n");
729 }
730
731 /* update display of watchpoints */
dbguwxupd(ctx)732 static void dbguwxupd(ctx)
733 dbgcxdef *ctx;
734 {
735 dbgwatwin.oswy = dbgwatwin.oswy1;
736 dbgwatwin.oswx = dbgwatwin.oswx1;
737 if (dbgwatwin.oswy1 <= dbgwatwin.oswy2) osdbgclr(&dbgwatwin);
738 dbgwxupd(ctx, dbgudisp, &dbgwatwin);
739 }
740
741 /* update window sizes to account for more/fewer watches */
dbguwxadd(ctx,cnt)742 static void dbguwxadd(ctx, cnt)
743 dbgcxdef *ctx;
744 int cnt;
745 {
746 /* move status line up by the number of new watch lines */
747 dbgstawin.oswy1 -= cnt;
748 dbgstawin.oswy2 -= cnt;
749
750 /* move bottom of code window up by the number of watch lines */
751 dbgsrcwin.oswy2 -= cnt;
752
753 /* move top of watch window up by the number of new watch lines */
754 dbgwatwin.oswy1 -= cnt;
755
756 /* update the status, source and watch windows */
757 dbgustat(ctx);
758 if (dbgsrclin)
759 {
760 linseek(dbgsrclin, dbgsrctop);
761 dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
762 }
763 dbguwxupd(ctx);
764 }
765
766 /* flags whether a prompt is needed the next time we run command line */
767 static int needprompt = TRUE;
768
769 /* main command loop */
dbgucmd1(ctx)770 static void dbgucmd1(ctx)
771 dbgcxdef *ctx;
772 {
773 uchar cmdbuf[128];
774 int linid;
775 int err;
776 int disable;
777 int bpnum;
778 char *buf;
779 int cmd;
780 int i;
781 lindef *lin;
782 static int stay_in_src;
783 static int first_search;
784 static char lastsearch[128];
785 int level = ctx->dbgcxfcn - 1;
786
787 dbglget(ctx, cmdbuf); /* get information on current line */
788 dbgulsho(ctx, cmdbuf); /* display current line */
789 dbguwxupd(ctx); /* udpate watchpoint display */
790
791 for (;;)
792 {
793 if (stay_in_src)
794 goto run_other_window;
795 else
796 {
797 if (needprompt) osdbgpt(&dbgtxtwin, "tdb> ");
798 cmd = osdbggts(&dbgtxtwin, cmdbuf, dbgukbc, ctx);
799 }
800
801 process_cmd:
802 needprompt = (cmd == 0); /* ordinary command ==> prompt next time */
803 switch(cmd)
804 {
805 case 0:
806 for (buf = cmdbuf ; isspace(*buf) ; ++buf);
807 break;
808
809 case CMD_F7:
810 buf = "t";
811 break;
812
813 case CMD_F8:
814 buf = "p";
815 break;
816
817 case CMD_F9:
818 buf = "g";
819 break;
820
821 case CMD_TAB:
822 buf = "\t";
823 break;
824
825 case CMD_SCR:
826 buf = "?";
827 break;
828
829 default:
830 continue;
831 }
832
833 if (isupper(buf[0])) buf[0] = tolower(buf[0]);
834 switch(buf[0])
835 {
836 case 'g':
837 ctx->dbgcxflg &= ~(DBGCXFSS + DBGCXFSO);
838 return;
839
840 case 't':
841 ctx->dbgcxflg |= DBGCXFSS;
842 ctx->dbgcxflg &= ~DBGCXFSO;
843 return;
844
845 case 'p':
846 ctx->dbgcxsof = ctx->dbgcxdep; /* step until back at this depth */
847 ctx->dbgcxflg |= (DBGCXFSS + DBGCXFSO);
848 return;
849
850 case 'e':
851 ossvpg(0); /* eval expression on game video page */
852 err = dbgeval(ctx, buf+1, dbgudisp, &dbgtxtwin, level, TRUE);
853 ossvpg(1); /* return to debugger video page */
854 if (err) errlog(ctx->dbgcxerr, err);
855 dbguwxupd(ctx); /* udpate watchpoint display */
856 break;
857
858 case 'c': /* Call history */
859 if (isupper(buf[1])) buf[1] = tolower(buf[1]);
860 switch(buf[1])
861 {
862 case '+':
863 ctx->dbgcxhstf = 0;
864 ctx->dbgcxflg |= DBGCXFTRC;
865 osdbgpt(&dbgtxtwin, "Call tracing activated.\n");
866 break;
867
868 case '-':
869 ctx->dbgcxflg &= ~DBGCXFTRC;
870 osdbgpt(&dbgtxtwin, "Call tracing disactivated.\n");
871 break;
872
873 case 'c':
874 ctx->dbgcxhstf = 0;
875 osdbgpt(&dbgtxtwin, "Call trace log cleared.\n");
876 break;
877
878 case '\0':
879 case ' ':
880 {
881 char *p;
882 char *endp;
883
884 for (p = ctx->dbgcxhstp, endp = p + ctx->dbgcxhstf
885 ; p < endp ; p += strlen(p) + 1)
886 osdbgpt(&dbgtxtwin, "%s\n", p);
887 }
888 }
889 break;
890
891 case 'b':
892 if (isupper(buf[1])) buf[1] = tolower(buf[1]);
893 switch(buf[1])
894 {
895 case 'p':
896 if (err = dbgbpset(ctx, buf+2, &bpnum))
897 errlog(ctx->dbgcxerr, err);
898 else
899 {
900 osdbgpt(&dbgtxtwin, "breakpoint %d set\n", bpnum);
901 dbgugetbp(ctx);
902 }
903 break;
904
905 case 'c':
906 if (err = dbgbpdel(ctx, atoi(buf + 2)))
907 errlog(ctx->dbgcxerr, err);
908 else
909 {
910 osdbgpt(&dbgtxtwin, "breakpoint cleared\n");
911 dbgugetbp(ctx);
912 }
913 break;
914
915 case 'd':
916 disable = TRUE;
917 goto bp_enable_or_disable;
918 case 'e':
919 disable = FALSE;
920 bp_enable_or_disable:
921 if (err = dbgbpdis(ctx, atoi(buf+2), disable))
922 errlog(ctx->dbgcxerr, err);
923 else
924 osdbgpt(&dbgtxtwin, "breakpoint %sabled\n",
925 disable ? "dis" : "en");
926 break;
927
928 case 'l':
929 dbgbplist(ctx, dbgudisp, &dbgtxtwin);
930 break;
931
932 default:
933 osdbgpt(&dbgtxtwin, "invalid breakpoint command\n");
934 break;
935 }
936 break;
937
938 case 'w':
939 if (isupper(buf[1])) buf[1] = tolower(buf[1]);
940 switch(buf[1])
941 {
942 case 's':
943 if (err = dbgwxset(ctx, buf+2, &bpnum, level))
944 errlog(ctx->dbgcxerr, err);
945 else
946 dbguwxadd(ctx, 1); /* adjust windows for a new wx */
947 break;
948
949 case 'd':
950 if (err = dbgwxdel(ctx, atoi(buf + 2)))
951 errlog(ctx->dbgcxerr, err);
952 else
953 dbguwxadd(ctx, -1); /* adjust windows for one less wx */
954 break;
955
956 default:
957 osdbgpt(&dbgtxtwin, "invalid watch command\n");
958 break;
959 }
960 break;
961
962 case 'f':
963 if (isupper(buf[1])) buf[1] = tolower(buf[1]);
964 switch(buf[1])
965 {
966 case 'l':
967 for (i = 1, lin = ctx->dbgcxlin ; lin ;
968 lin = lin->linnxt, ++i)
969 {
970 osdbgpt(&dbgtxtwin, "#%-2d %s", i,
971 ((linfdef *)lin)->linfnam);
972 osdbgpt(&dbgtxtwin, "\n");
973 }
974 break;
975
976 case 'v':
977 if (atoi(buf + 2) == 0)
978 {
979 osfildef *fp;
980
981 /* file by name - read the filename */
982 for (buf += 2 ; isspace(*buf) ; ++buf);
983 if (!(fp = osfoprb(buf, OSFTBIN))) /* dbj: right type?? */
984 osdbgpt(&dbgtxtwin, "file not found\n");
985 else
986 {
987 dbgswitch(&dbgsrclin, (lindef *)0);
988 *(buf-1) = '\0';
989 linfini2(ctx->dbgcxmem, (linfdef *)filesrc, buf-1,
990 (int)strlen(buf) + 2, fp, FALSE);
991 dbgsrclin = (lindef *)filesrc;
992 lintell(dbgsrclin, dbgsrctop);
993 lintell(dbgsrclin, dbgsrccsr);
994 dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop,
995 dbgsrcloc);
996 }
997 break;
998 }
999
1000 /* file by number - look up in line source list */
1001 for (i = 1, lin = ctx->dbgcxlin ; lin ;
1002 lin = lin->linnxt, ++i)
1003 {
1004 if (i == atoi(buf + 2)) break;
1005 }
1006
1007 if (lin)
1008 {
1009 lindef *oldlin;
1010
1011 oldlin = dbgsrclin;
1012 dbgswitch(&dbgsrclin, lin);
1013 lingoto(dbgsrclin, LINGOTOP);
1014 lintell(dbgsrclin, dbgsrccsr);
1015 memcpy(dbgsrctop, dbgsrccsr, dbgsrclin->linlln);
1016 dbgsrcshow(lin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
1017 }
1018 else
1019 osdbgpt(&dbgtxtwin, "invalid file number\n");
1020 break;
1021
1022 default:
1023 osdbgpt(&dbgtxtwin, "invalid file command\n");
1024 break;
1025 }
1026 break;
1027
1028 case '\0':
1029 break;
1030
1031 case 'q':
1032 if (strcmp(buf, "quit"))
1033 osdbgpt(&dbgtxtwin, "invalid debugger command\n");
1034 else
1035 {
1036 ossvpg(0); /* return to game page */
1037 errsig(ctx->dbgcxerr, ERR_RUNQUIT); /* signal QUIT */
1038 }
1039 break;
1040
1041 case 'k':
1042 dbgstktr(ctx, dbgudisp, &dbgtxtwin, level, FALSE, TRUE);
1043 break;
1044
1045 case 'h':
1046 if (!stricmp(buf, "help"))
1047 goto do_help;
1048 else
1049 {
1050 extern int dbghid;
1051
1052 dbghid = !dbghid;
1053 osdbgpt(&dbgtxtwin, "Hidden text will be %s.\n",
1054 dbghid ? "displayed" : "suppressed");
1055 }
1056 break;
1057
1058 case '\\':
1059 dbguuser(ctx);
1060 break;
1061
1062 case '/':
1063 ++buf; /* skip the '/' part */
1064 strcpy(lastsearch, buf);
1065 another_search:
1066 dbgusearch(dbgsrclin, dbgsrccsr, dbgsrctop,
1067 &dbgsrcwin, buf);
1068 needprompt = TRUE;
1069 goto run_other_window;
1070
1071 case '\t':
1072 needprompt = FALSE;
1073 first_search = TRUE;
1074 run_other_window:
1075 cmd = dbgurunsrc(ctx);
1076 if (cmd == CMD_UP) /* special return for "search" */
1077 {
1078 char newbuf[128];
1079
1080 if (first_search)
1081 {
1082 osdbgpt(&dbgtxtwin, "\n");
1083 first_search = FALSE;
1084 }
1085
1086 /* present default if there is one */
1087 if (lastsearch[0])
1088 osdbgpt(&dbgtxtwin, "search [%s]: ", lastsearch);
1089 else
1090 osdbgpt(&dbgtxtwin, "search: ");
1091
1092 /* ignore command keys from search call */
1093 while (osdbggts(&dbgtxtwin, cmdbuf, dbgukbc, ctx)) ;
1094
1095 /* apply default if appropriate */
1096 if (cmdbuf[0])
1097 {
1098 strcpy(lastsearch, cmdbuf);
1099 buf = cmdbuf;
1100 }
1101 else
1102 buf = lastsearch;
1103
1104 stay_in_src = TRUE;
1105 goto another_search;
1106 }
1107 if (cmd == 0)
1108 {
1109 stay_in_src = FALSE;
1110 continue;
1111 }
1112 stay_in_src = TRUE;
1113 goto process_cmd;
1114
1115 case 'u':
1116 if (level < ctx->dbgcxfcn - 1) ++level;
1117 osdbgpt(&dbgtxtwin, "evaluations are now at level %d\n", level+1);
1118 break;
1119
1120 case 'd':
1121 if (level > 0) --level;
1122 osdbgpt(&dbgtxtwin, "evaluations are now at level %d\n", level+1);
1123 break;
1124 #ifndef UNIX
1125 case '?':
1126 {
1127 char buf[128];
1128 osfildef *fp;
1129 do_help:
1130 if ( !os_locate("help.tdb", 8, ARG0, buf, sizeof(buf))
1131 || !(fp = osfoprb(buf)) )
1132 osdbgpt(&dbgtxtwin, "HELP.TDB not found\n");
1133 else
1134 {
1135 dbgswitch(&dbgsrclin, (lindef *)0);
1136 linfini2(ctx->dbgcxmem, (linfdef *)filesrc,
1137 buf, (int)strlen(buf), fp);
1138 dbgsrclin = (lindef *)filesrc;
1139 lintell(dbgsrclin, dbgsrctop);
1140 lintell(dbgsrclin, dbgsrccsr);
1141 dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
1142 }
1143 }
1144 #else
1145 do_help:
1146 #endif
1147 break;
1148
1149 default:
1150 osdbgpt(&dbgtxtwin, "invalid debugger command\n");
1151 break;
1152 }
1153 }
1154 }
1155
dbgucmd(dbgcxdef * ctx,int bphit,int err,unsigned int * exec_ofs)1156 void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs)
1157 {
1158 ossvpg(1); /* change to debugger's video page */
1159
1160 /* display cause of breakpoint if not single-stepping */
1161 if (err || bphit)
1162 {
1163 if (!needprompt) osdbgpt(&dbgtxtwin, "\n");
1164 if (err)
1165 errprelog(ctx->dbgcxerr, err);
1166 else if (bphit)
1167 osdbgpt(&dbgtxtwin, "Stop at breakpoint %d\n", bphit);
1168 needprompt = TRUE;
1169 }
1170
1171 dbgucmd1(ctx); /* run the normal main loop */
1172 ossvpg(0); /* change back to default video page */
1173 }
1174
dbguini(dbgcxdef * ctx,const char * game_filename)1175 void dbguini(dbgcxdef *ctx, const char *game_filename)
1176 {
1177 char buf[150];
1178 int i;
1179 extern int sdesc_color;
1180 extern int text_color;
1181 int ldesc_color = 112;
1182 int watch_color = 0x47;
1183 extern int usebios;
1184 int maxline;
1185 int maxcol;
1186 int divline;
1187
1188 ossgmx(&maxline, &maxcol);
1189 osdbgini(maxline+1, maxcol+1);
1190
1191 usebios = 0;
1192
1193 ossvpg(1);
1194 #ifdef UNIX
1195 ossclr(0, 0, maxline, maxcol, text_normal_color);
1196 #else
1197 ossclr(0, 0, maxline, maxcol, 7);
1198 #endif
1199
1200 if (ossmon())
1201 {
1202 ldesc_color = 7;
1203 dbgcurcolor = 10;
1204 dbgbpcolor = 7;
1205 watch_color = 10;
1206 }
1207
1208 #ifdef UNIX
1209 dbgcurcolor = text_bold_color;
1210 dbgbpcolor = breakpoint_color;
1211 watch_color = watchpoint_color;
1212 ldesc_color = filename_color;
1213 #endif
1214
1215 /* initialize windows */
1216 #ifdef MSOS2
1217 divline = maxline * 2 / 3;
1218 osdbgwop(&dbgsrcwin, 0, 0, maxcol, divline-1, sdesc_color);
1219 osdbgwop(&dbgstawin, 0, divline, maxcol, divline, ldesc_color);
1220 osdbgwop(&dbgtxtwin, 0, divline+1, maxcol, maxline, text_color);
1221 osdbgwop(&dbgwatwin, 0, divline+1, maxcol, divline, watch_color);
1222 #else /* !MSOS2 */
1223 osdbgwop(&dbgsrcwin, 0, 0, 79, 15, sdesc_color);
1224 osdbgwop(&dbgstawin, 0, 16, 79, 16, ldesc_color);
1225 osdbgwop(&dbgtxtwin, 0, 17, 79, 24, text_color);
1226 osdbgwop(&dbgwatwin, 0, 17, 79, 16, watch_color);
1227 #endif /* MSOS2 */
1228 dbgsrcwin.oswflg = OSWFCLIP;
1229 dbgstawin.oswflg = OSWFCLIP;
1230 dbgwatwin.oswflg = OSWFCLIP;
1231 dbgtxtwin.oswflg = OSWFMORE;
1232
1233 /* display status line with bar */
1234 for (i = 0 ; i <= maxcol && i+1 < sizeof(buf) ; ++i) buf[i] = DBGUHLIN;
1235 buf[i] = '\0';
1236 osdbgpt(&dbgstawin, buf);
1237
1238 ossvpg(0);
1239
1240 /* allocate buffer */
1241 dbgbuf = (char *)malloc(dbgbufsiz = 4096);
1242 }
1243
1244 /*
1245 * initialization phase two (called after the .GAM file is loaded)
1246 */
dbguini2(dbgcxdef * ctx)1247 void dbguini2(dbgcxdef *ctx)
1248 {
1249 /* we don't need to do any more setup after loading the .GAM file */
1250 }
1251
1252 /* terminate the UI */
dbguterm(dbgcxdef * ctx)1253 void dbguterm(dbgcxdef *ctx)
1254 {
1255 /* there's nothing extra we need to do here */
1256 }
1257
1258 /*
1259 * Determine if we can resume from an error. This UI doesn't allow the
1260 * user to change the instruction pointer, so we can't resume from an
1261 * error.
1262 */
dbgu_err_resume(dbgcxdef * ctx)1263 int dbgu_err_resume(dbgcxdef *ctx)
1264 {
1265 VARUSED(ctx);
1266 return FALSE;
1267 }
1268
1269 /*
1270 * Quitting - do nothing; let the system terminate
1271 */
dbguquitting(dbgcxdef * ctx)1272 void dbguquitting(dbgcxdef *ctx)
1273 {
1274 VARUSED(ctx);
1275 }
1276
1277
1278 /*
1279 * Locate a source file using UI-dependent mechanisms. We don't have
1280 * anything more we can do beyond what the core debugger does, so we'll
1281 * simply return a failure indication. On some systems, this is used to
1282 * display a "find file" dialog to allow the user to locate a source
1283 * file manually when we can't find it in any of the places we know to
1284 * look.
1285 *
1286 * Refer to the HTML TADS Win32 implementation for details on how this
1287 * can implemented for friendlier behavior on a GUI system.
1288 */
dbgu_find_src(const char * origname,int origlen,char * fullname,size_t full_len,int must_find_file)1289 int dbgu_find_src(const char *origname, int origlen,
1290 char *fullname, size_t full_len, int must_find_file)
1291 {
1292 VARUSED(origname);
1293 VARUSED(origlen);
1294 VARUSED(fullname);
1295 VARUSED(full_len);
1296 VARUSED(must_find_file);
1297
1298 return FALSE;
1299 }
1300
1301