]> git.sur5r.net Git - cc65/blob - libsrc/dbg/dbg.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / libsrc / dbg / dbg.c
1 /*
2  * dbg.c
3  *
4  * Ullrich von Bassewitz, 08.08.1998
5  *
6  */
7
8
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <conio.h>
14 #include <ctype.h>
15 #include <6502.h>
16 #include <dbg.h>
17
18
19
20 /*****************************************************************************/
21 /*                             Function forwards                             */
22 /*****************************************************************************/
23
24
25
26 /* Forwards for handler functions */
27 static char AsmHandler (void);
28 static char RegHandler (void);
29 static char StackHandler (void);
30 static char CStackHandler (void);
31 static char DumpHandler (void);
32 static char HelpHandler (void);
33
34 /* Forwards for other functions */
35 static void DisplayPrompt (char* s);
36 static void SingleStep (char StepInto);
37 static void RedrawStatic (char  Frame);
38 static void Redraw (char Frame);
39 static char GetKeyUpdate (void);
40
41
42
43 /*****************************************************************************/
44 /*                                   Data                                    */
45 /*****************************************************************************/
46
47
48
49 /* Color definitions */
50 #if defined(__PLUS4__) || defined(__C16__)
51 #  define COLOR_BORDER          (BCOLOR_DARKBLUE | CATTR_LUMA6)
52 #  define COLOR_BACKGROUND      COLOR_WHITE
53 #  define COLOR_TEXTHIGH        COLOR_BLACK
54 #  define COLOR_TEXTLOW         COLOR_GRAY1
55 #  define COLOR_FRAMEHIGH       COLOR_BLACK
56 #  define COLOR_FRAMELOW        COLOR_GRAY2
57 #else
58 #  if defined(COLOR_GRAY3)
59 #    define COLOR_BORDER        COLOR_BLACK
60 #    define COLOR_BACKGROUND    COLOR_BLACK
61 #    define COLOR_TEXTHIGH      COLOR_WHITE
62 #    define COLOR_TEXTLOW       COLOR_GRAY3
63 #    define COLOR_FRAMEHIGH     COLOR_WHITE
64 #    define COLOR_FRAMELOW      COLOR_GRAY3
65 #  else
66 #    if defined(__APPLE2__) || defined(__APPLE2ENH__)
67 #      define COLOR_BORDER      COLOR_BLACK
68 #      define COLOR_BACKGROUND  COLOR_BLACK
69 #      define COLOR_TEXTHIGH    COLOR_BLACK
70 #      define COLOR_TEXTLOW     COLOR_BLACK
71 #      define COLOR_FRAMEHIGH   COLOR_BLACK
72 #      define COLOR_FRAMELOW    COLOR_BLACK
73 #    else
74 #      define COLOR_BORDER      COLOR_BLACK
75 #      define COLOR_BACKGROUND  COLOR_BLACK
76 #      define COLOR_TEXTHIGH    COLOR_WHITE
77 #      define COLOR_TEXTLOW     COLOR_WHITE
78 #      define COLOR_FRAMEHIGH   COLOR_WHITE
79 #      define COLOR_FRAMELOW    COLOR_WHITE
80 #    endif
81 #  endif
82 #endif
83 #ifndef COLOR_BLACK
84 #  define COLOR_BLACK   0
85 #endif
86 #ifndef COLOR_WHITE
87 #  define COLOR_WHITE   1
88 #endif
89
90 /* Screen definitions */
91 #if defined(__CBM610__)
92 #  define BIGSCREEN
93 #  define MAX_X         80
94 #  define MAX_Y         25
95 #  define DUMP_BYTES    16
96 #elif defined(__APPLE2__) || defined(__APPLE2ENH__) || defined(__ATARI__)
97 #  define MAX_X         40
98 #  define MAX_Y         24
99 #  define DUMP_BYTES     8
100 #else
101 #  define MAX_X         40
102 #  define MAX_Y         25
103 #  define DUMP_BYTES     8
104 #endif
105
106 /* Replacement key definitions */
107 #if defined(__APPLE2__) || defined(__LYNX__) || defined(__SUPERVISION__)
108 #  define CH_DEL        ('H' - 'A' + 1)         /* Ctrl+H */
109 #endif
110
111 /* Replacement char definitions */
112 #ifndef CH_ULCORNER
113 #  define CH_ULCORNER   '+'
114 #endif
115 #ifndef CH_URCORNER
116 #  define CH_URCORNER   '+'
117 #endif
118 #ifndef CH_LLCORNER
119 #  define CH_LLCORNER   '+'
120 #endif
121 #ifndef CH_LRCORNER
122 #  define CH_LRCORNER   '+'
123 #endif
124 #ifndef CH_TTEE
125 #  define CH_TTEE       '+'
126 #endif
127 #ifndef CH_LTEE
128 #  define CH_LTEE       '+'
129 #endif
130 #ifndef CH_RTEE
131 #  define CH_RTEE       '+'
132 #endif
133 #ifndef CH_BTEE
134 #  define CH_BTEE       '+'
135 #endif
136 #ifndef CH_CROSS
137 #  define CH_CROSS      '+'
138 #endif
139
140 /* Defines for opcodes */
141 #define OPC_BRK         0x00
142 #define OPC_BPL         0x10
143 #define OPC_JSR         0x20
144 #define OPC_BMI         0x30
145 #define OPC_RTI         0x40
146 #define OPC_JMP         0x4C
147 #define OPC_BVC         0x50
148 #define OPC_RTS         0x60
149 #define OPC_JMPIND      0x6C
150 #define OPC_BVS         0x70
151 #define OPC_BCC         0x90
152 #define OPC_BCS         0xB0
153 #define OPC_BNE         0xD0
154 #define OPC_BEQ         0xF0
155
156
157
158 /* Register values that are used also in the assembler stuff */
159 extern unsigned char DbgSP;             /* Stack pointer */
160 extern unsigned      DbgCS;             /* C stack pointer */
161 extern unsigned      DbgHI;             /* High 16 bit of primary reg */
162
163
164
165 /* Descriptor for one text line */
166 typedef struct {
167     unsigned char x;
168     unsigned char y;
169     char*         text;
170 } TextDesc;
171
172 /* Window descriptor */
173 typedef struct {
174     unsigned char fd_tl;                /* Top left char */
175     unsigned char fd_tr;                /* Top right char */
176     unsigned char fd_bl;                /* Bottom left char */
177     unsigned char fd_br;                /* Bottom right char */
178     unsigned char fd_x1, fd_y1;         /* Upper left corner */
179     unsigned char fd_x2, fd_y2;         /* Lower right corner */
180     unsigned char fd_width, fd_height;  /* Redundant but faster */
181     unsigned char fd_visible;           /* Is the window currently visible? */
182     char (*fd_func) (void);             /* Handler function */
183     unsigned char fd_textcount;         /* Number of text lines to print */
184     TextDesc*     fd_text;              /* Static text in the window */
185 } FrameDesc;
186
187
188
189 /* Texts for the windows */
190 static TextDesc RegText [] = {
191     { 1,  0, "PC" },
192     { 1,  1, "SR" },
193     { 1,  2, "A"  },
194     { 1,  3, "X"  },
195     { 1,  4, "Y"  },
196     { 1,  5, "SP" },
197     { 1,  6, "CS" },
198     { 1,  7, "HI" }
199 };
200 static TextDesc HelpText [] = {
201     { 1,  0, "F1, ?     Help"                           },
202     { 1,  1, "F2, t     Toggle breakpoint"              },
203     { 1,  2, "F3, u     Run until subroutine returns"   },
204     { 1,  3, "F4, h     Run to cursor"                  },
205     { 1,  4, "F7, space Step into"                      },
206     { 1,  5, "F8, enter Step over"                      },
207     { 1,  6, "1-5       Select active window"           },
208     { 1,  7, "+         Page down"                      },
209     { 1,  8, "-         Page up"                        },
210     { 1,  9, "Cursor    Move up/down"                   },
211     { 1, 10, "a/z       Move up/down"                   },
212     { 1, 11, "c         Continue"                       },
213     { 1, 12, "f         Follow instruction"             },
214     { 1, 13, "o         Goto origin"                    },
215     { 1, 14, "p         Use as new PC value"            },
216     { 1, 15, "q         Quit"                           },
217     { 1, 16, "r         Redraw screen"                  },
218     { 1, 17, "s         Skip next instruction"          },
219 };
220
221
222 /* Window data */
223 static FrameDesc AsmFrame = {
224     CH_ULCORNER, CH_TTEE, CH_LTEE, CH_CROSS,
225     0, 0, MAX_X - 10, 15,
226     MAX_X - 11, 14,
227     1,
228     AsmHandler,
229     0, 0
230 };
231 static FrameDesc RegFrame = {
232     CH_TTEE, CH_URCORNER, CH_LTEE, CH_RTEE,
233     MAX_X - 10, 0, MAX_X - 1, 9,
234     8, 8,
235     1,
236     RegHandler,
237     sizeof (RegText) / sizeof (RegText [0]), RegText
238 };
239 static FrameDesc StackFrame = {
240     CH_LTEE, CH_RTEE, CH_CROSS, CH_RTEE,
241     MAX_X - 10, 9, MAX_X - 1, 15,
242     8, 5,
243     1,
244     StackHandler,
245     0, 0
246 };
247 static FrameDesc CStackFrame = {
248     CH_CROSS, CH_RTEE, CH_BTEE, CH_LRCORNER,
249     MAX_X - 10, 15, MAX_X - 1, MAX_Y - 1,
250     8, MAX_Y - 17,
251     1,
252     CStackHandler,
253     0, 0
254 };
255 static FrameDesc DumpFrame = {
256     CH_LTEE, CH_CROSS, CH_LLCORNER, CH_BTEE,
257     0, 15, MAX_X - 10, MAX_Y-1,
258     MAX_X - 11, MAX_Y - 17,
259     1,
260     DumpHandler,
261     0, 0
262 };
263 static FrameDesc HelpFrame = {
264     CH_ULCORNER, CH_URCORNER, CH_LLCORNER, CH_LRCORNER,
265     0, 0, MAX_X - 1, MAX_Y-1,
266     MAX_X - 2, MAX_Y - 2,
267     0,
268     HelpHandler,
269     sizeof (HelpText) / sizeof (HelpText [0]), HelpText
270 };
271 static FrameDesc* Frames [] = {
272     &AsmFrame,
273     &RegFrame,
274     &StackFrame,
275     &CStackFrame,
276     &DumpFrame,
277     &HelpFrame
278 };
279
280 /* Number of active frame, -1 = none */
281 static int ActiveFrame = -1;
282
283 /* Window names */
284 #define WIN_ASM         0
285 #define WIN_REG         1
286 #define WIN_STACK       2
287 #define WIN_CSTACK      3
288 #define WIN_DUMP        4
289 #define WIN_HELP        5
290
291 /* Other window data */
292 static unsigned AsmAddr;        /* Start address of output */
293 static unsigned DumpAddr;       /* Start address of output */
294 static unsigned CStackAddr;     /* Start address of output */
295 static unsigned char StackAddr; /* Start address of output */
296
297
298
299 /* Prompt line data */
300 static char* ActivePrompt = 0;  /* Last prompt line displayed */
301 static char PromptColor;        /* Color behind prompt */
302 static char PromptLength;       /* Length of current prompt string */
303
304
305
306 /* Values for the bk_use field of struct BreakPoint */
307 #define BRK_EMPTY       0x00
308 #define BRK_USER        0x01
309 #define BRK_TMP         0x80
310
311 /* Structure describing a breakpoint */
312 typedef struct {
313     unsigned      bk_addr;      /* Address, 0 if unused */
314     unsigned char bk_opc;       /* Opcode */
315     unsigned char bk_use;       /* 1 if in use, 0 otherwise */
316 } BreakPoint;
317
318
319
320 /* Temporary breakpoints - also accessed from the assembler source */
321 #define MAX_USERBREAKS  10
322 unsigned char DbgBreakCount = 0;
323 BreakPoint DbgBreaks [MAX_USERBREAKS+2];
324
325
326
327 /*****************************************************************************/
328 /*              Forwards for functions in the assembler source               */
329 /*****************************************************************************/
330
331
332
333 BreakPoint* DbgGetBreakSlot (void);
334 /* Search for a free breakpoint slot. Return a pointer to the slot or 0 */
335
336 BreakPoint* DbgIsBreak (unsigned Addr);
337 /* Check if there is a user breakpoint at the given address, if so, return
338  * a pointer to the slot, else return 0.
339  */
340
341
342
343 /*****************************************************************************/
344 /*                         Frame/window drawing code                         */
345 /*****************************************************************************/
346
347
348
349 static void DrawFrame (register FrameDesc* F, char Active)
350 /* Draw one window frame */
351 {
352     TextDesc* T;
353     unsigned char Count;
354     unsigned char tl, tr, bl, br;
355     unsigned char x1, y1, width;
356     unsigned char OldColor;
357
358     /* Determine the characters for the corners, set frame color */
359     if (Active) {
360         OldColor = textcolor (COLOR_FRAMEHIGH);
361         tl = CH_ULCORNER;
362         tr = CH_URCORNER;
363         bl = CH_LLCORNER;
364         br = CH_LRCORNER;
365     } else {
366         OldColor = textcolor (COLOR_FRAMELOW);
367         tl = F->fd_tl;
368         tr = F->fd_tr;
369         bl = F->fd_bl;
370         br = F->fd_br;
371     }
372
373     /* Get the coordinates into locals for faster access */
374     x1 = F->fd_x1;
375     y1 = F->fd_y1;
376     width = F->fd_width;
377
378     /* Top line */
379     cputcxy (x1, y1, tl);
380     chline (width);
381     cputc (tr);
382
383     /* Left line */
384     cvlinexy (x1, ++y1, F->fd_height);
385
386     /* Bottom line */
387     cputc (bl);
388     chline (width);
389     cputc (br);
390
391     /* Right line */
392     cvlinexy (F->fd_x2, y1, F->fd_height);
393
394     /* If the window has static text associated, print the text */
395     (void) textcolor (COLOR_TEXTLOW);
396     Count = F->fd_textcount;
397     T = F->fd_text;
398     while (Count--) {
399         cputsxy (x1 + T->x, y1 + T->y, T->text);
400         ++T;
401     }
402
403     /* Set the old color */
404     (void) textcolor (OldColor);
405 }
406
407
408
409 static void DrawFrames (void)
410 /* Draw all frames */
411 {
412     unsigned char I;
413     FrameDesc* F;
414
415     /* Build the frame layout of the screen */
416     for (I = 0; I < sizeof (Frames) / sizeof (Frames [0]); ++I) {
417         F = Frames [I];
418         if (F->fd_visible) {
419             DrawFrame (F, 0);
420         }
421     }
422 }
423
424
425
426 static void ActivateFrame (int Num, unsigned char Clear)
427 /* Activate a new frame, deactivate the old one */
428 {
429     unsigned char y;
430     register FrameDesc* F;
431
432     if (ActiveFrame != Num) {
433
434         /* Deactivate the old one */
435         if (ActiveFrame >= 0) {
436             DrawFrame (Frames [ActiveFrame], 0);
437         }
438
439         /* Activate the new one */
440         if ((ActiveFrame = Num) >= 0) {
441             F = Frames [ActiveFrame];
442             /* Clear the frame if requested */
443             if (Clear) {
444                 for (y = F->fd_y1+1; y < F->fd_y2; ++y) {
445                     cclearxy (F->fd_x1+1, y, F->fd_width);
446                 }
447             }
448             DrawFrame (F, 1);
449         }
450
451         /* Redraw the current prompt line */
452         DisplayPrompt (ActivePrompt);
453
454     }
455 }
456
457
458
459 /*****************************************************************************/
460 /*                                Prompt line                                */
461 /*****************************************************************************/
462
463
464
465 static void DisplayPrompt (char* s)
466 /* Display a prompt */
467 {
468     unsigned char OldColor;
469
470     /* Remember the current color */
471     OldColor = textcolor (COLOR_TEXTHIGH);
472
473     /* Clear the old prompt if there is one */
474     if (ActivePrompt) {
475         (void) textcolor (PromptColor);
476         chlinexy ((MAX_X - PromptLength) / 2, MAX_Y-1, PromptLength);
477     }
478
479     /* Get the new prompt data */
480     ActivePrompt = s;
481     PromptColor  = OldColor;
482     PromptLength = strlen (ActivePrompt);
483
484     /* Display the new prompt */
485     (void) textcolor (COLOR_TEXTHIGH);
486     cputsxy ((MAX_X - PromptLength) / 2, MAX_Y-1, ActivePrompt);
487
488     /* Restore the old color */
489     (void) textcolor (PromptColor);
490 }
491
492
493
494 static void HelpPrompt (void)
495 /* Display a prompt line mentioning the help key */
496 {
497     DisplayPrompt ("Press F1 for help");
498 }
499
500
501
502 static void AnyKeyPrompt (void)
503 {
504     DisplayPrompt ("Press any key to continue");
505 }
506
507
508
509 static char IsAbortKey (char C)
510 /* Return true if C is an abort key */
511 {
512 #if defined(CH_ESC)
513     if (C == CH_ESC) {
514         return 1;
515     }
516 #endif
517 #if defined(CH_STOP)
518     if (C == CH_STOP) {
519         return 1;
520     }
521 #endif
522     return 0;
523 }
524
525
526
527 static char Input (char* Prompt, char* Buf, unsigned char Count)
528 /* Read input from the user, return 1 on success, 0 if aborted */
529 {
530     int Frame;
531     unsigned char OldColor;
532     unsigned char OldCursor;
533     unsigned char x1;
534     unsigned char i;
535     unsigned char done;
536     char c;
537
538     /* Clear the current prompt line */
539     cclearxy (0, MAX_Y-1, MAX_X);
540
541     /* Display the new prompt */
542     OldColor = textcolor (COLOR_TEXTHIGH);
543     cputsxy (0, MAX_Y-1, Prompt);
544     (void) textcolor (COLOR_TEXTLOW);
545
546     /* Remember where we are, enable the cursor */
547     x1 = wherex ();
548     OldCursor = cursor (1);
549
550     /* Get input and handle it */
551     i = done = 0;
552     do {
553         c = cgetc ();
554         if (isalnum (c) && i < Count) {
555             Buf [i] = c;
556             cputcxy (x1 + i, MAX_Y-1, c);
557             ++i;
558         } else if (i > 0 && c == CH_DEL) {
559             --i;
560             cputcxy (x1 + i, MAX_Y-1, ' ');
561             gotoxy (x1 + i, MAX_Y-1);
562         } else if (c == '\n') {
563             Buf [i] = '\0';
564             done = 1;
565         } else if (IsAbortKey (c)) {
566             /* Abort */
567             done = 2;
568         }
569     } while (!done);
570
571     /* Reset settings, display old prompt line */
572     cursor (OldCursor);
573     (void) textcolor (OldColor);
574     DrawFrames ();
575     Frame = ActiveFrame;
576     ActiveFrame = -1;
577     ActivateFrame (Frame, 0);
578
579     return (done == 1);
580 }
581
582
583
584 static char InputHex (char* Prompt, unsigned* Val)
585 /* Prompt for a hexadecimal value. Return 0 on failure. */
586 {
587     char Buf [5];
588     char* P;
589     char C;
590     unsigned V;
591
592     /* Read input from the user (4 digits max), check input */
593     if (Input (Prompt, Buf, sizeof (Buf)-1) && isxdigit (Buf [0])) {
594
595         /* Check the characters and convert to hex */
596         P = Buf;
597         V = 0;
598         while ((C = *P) && isxdigit (C)) {
599             V <<= 4;
600             if (isdigit (C)) {
601                 C -= '0';
602             } else {
603                 C = toupper (C) - ('A' - 10);
604             }
605             V += C;
606             ++P;
607         }
608
609         /* Assign the value */
610         *Val = V;
611
612         /* Success */
613         return 1;
614
615     } else {
616
617         /* Failure */
618         return 0;
619
620     }
621 }
622
623
624
625 static void ErrorPrompt (char* Msg)
626 /* Display an error message and wait for a key */
627 {
628     /* Save the current prompt */
629     char* OldPrompt = ActivePrompt;
630
631     /* Display the new one */
632     DisplayPrompt (Msg);
633
634     /* Wait for a key and discard it */
635     cgetc ();
636
637     /* Restore the old prompt */
638     DisplayPrompt (OldPrompt);
639 }
640
641
642
643 static char InputGoto (unsigned* Addr)
644 /* Prompt "Goto" and read an address. Print an error and return 0 on failure. */
645 {
646     char Ok;
647     Ok = InputHex ("Goto: ", Addr);
648     if (!Ok) {
649         ErrorPrompt ("Invalid input - press a key");
650     }
651     return Ok;
652 }
653
654
655
656 static void BreakInRomError (void)
657 /* Print an error message if we cannot set a breakpoint */
658 {
659     ErrorPrompt ("Cannot set breakpoint - press a key");
660 }
661
662
663
664 /*****************************************************************************/
665 /*                            Breakpoint handling                            */
666 /*****************************************************************************/
667
668
669
670 static void DbgSetTmpBreak (unsigned Addr)
671 /* Set a breakpoint */
672 {
673     BreakPoint* B = DbgGetBreakSlot ();
674     B->bk_addr    = Addr;
675     B->bk_use     = BRK_TMP;
676 }
677
678
679
680 static void DbgToggleUserBreak (unsigned Addr)
681 /* Set a breakpoint */
682 {
683     register BreakPoint* B = DbgIsBreak (Addr);
684
685     if (B) {
686         /* We have a breakpoint, remove it */
687         B->bk_use = BRK_EMPTY;
688         --DbgBreakCount;
689     } else {
690         /* We don't have a breakpoint, set one */
691         if (DbgBreakCount >= MAX_USERBREAKS) {
692             ErrorPrompt ("Too many breakpoints - press a key");
693         } else {
694             /* Test if we can set a breakpoint at that address */
695             if (!DbgIsRAM (Addr)) {
696                 BreakInRomError ();
697             } else {
698                 /* Set the breakpoint */
699                 B = DbgGetBreakSlot ();
700                 B->bk_addr = Addr;
701                 B->bk_use  = BRK_USER;
702                 ++DbgBreakCount;
703             }
704         }
705     }
706 }
707
708
709
710 static void DbgResetTmpBreaks (void)
711 /* Reset all temporary breakpoints */
712 {
713     unsigned char i;
714     BreakPoint* B = DbgBreaks;
715
716     for (i = 0; i < MAX_USERBREAKS; ++i) {
717         if (B->bk_use == BRK_TMP) {
718             B->bk_use = BRK_EMPTY;
719         }
720         ++B;
721     }
722 }
723
724
725
726 static unsigned char DbgTmpBreaksOk (void)
727 /* Check if the temporary breakpoints can be set, if so, return 1, if not,
728  * reset them all and return 0.
729  */
730 {
731     unsigned char i;
732     BreakPoint* B = DbgBreaks;
733     for (i = 0; i < MAX_USERBREAKS; ++i) {
734         if (B->bk_use == BRK_TMP && !DbgIsRAM (B->bk_addr)) {
735             BreakInRomError ();
736             DbgResetTmpBreaks ();
737             return 0;
738         }
739         ++B;
740     }
741     return 1;
742 }
743
744
745
746 /*****************************************************************************/
747 /*                          Assembler window stuff                           */
748 /*****************************************************************************/
749
750
751
752 static unsigned AsmBack (unsigned mem, unsigned char lines)
753 /* Go back in the assembler window the given number of lines (calculate
754  * new start address).
755  */
756 {
757     unsigned cur;
758     unsigned adr [32];
759     unsigned char in;
760
761     unsigned offs = 6;
762     while (1) {
763         in = 0;
764         cur = mem - (lines * 3) - offs;
765         while (1) {
766             cur += DbgDisAsmLen (cur);
767             adr [in] = cur;
768             in = (in + 1) & 0x1F;
769             if (cur >= mem) {
770                 if (cur == mem || offs == 12) {
771                     /* Found */
772                     return adr [(in - lines - 1) & 0x1F];
773                 } else {
774                     /* The requested address is inside an instruction, go back
775                      * one more byte and try again.
776                      */
777                     ++offs;
778                     break;
779                 }
780             }
781         }
782     }
783 }
784
785
786
787 static unsigned UpdateAsm (void)
788 /* Update the assembler window starting at the given address */
789 {
790     char buf [MAX_X];
791     unsigned char len;
792     unsigned char y;
793     unsigned char width = AsmFrame.fd_width;
794     unsigned char x = AsmFrame.fd_x1 + 1;
795     unsigned      m = AsmBack (AsmAddr, 2);
796
797     for (y = AsmFrame.fd_y1+1; y < AsmFrame.fd_y2; ++y) {
798         len = DbgDisAsm (m, buf, width);
799         if (m == brk_pc) {
800             buf [4] = '-';
801             buf [5] = '>';
802         }
803         if (DbgIsBreak (m)) {
804             buf [5] = '*';
805         }
806         if (m == AsmAddr) {
807             revers (1);
808             cputsxy (1, y, buf);
809             revers (0);
810         } else {
811             cputsxy (1, y, buf);
812         }
813         m += len;
814     }
815     return m;
816 }
817
818
819
820 static unsigned AsmArg16 (void)
821 /* Return a 16 bit argument */
822 {
823     return *(unsigned*)(AsmAddr+1);
824 }
825
826
827
828 static void AsmFollow (void)
829 /* Follow the current instruction */
830 {
831     switch (*(unsigned char*) AsmAddr) {
832
833         case OPC_JMP:
834         case OPC_JSR:
835             AsmAddr = AsmArg16 ();
836             break;
837
838         case OPC_JMPIND:
839             AsmAddr = *(unsigned*)AsmArg16 ();
840             break;
841
842         case OPC_BPL:
843         case OPC_BMI:
844         case OPC_BVC:
845         case OPC_BVS:
846         case OPC_BCC:
847         case OPC_BCS:
848         case OPC_BNE:
849         case OPC_BEQ:
850             AsmAddr = AsmAddr + 2 + *(signed char*)(AsmAddr+1);
851             break;
852
853         case OPC_RTS:
854             AsmAddr = (*(unsigned*) (DbgSP + 0x101) + 1);
855             break;
856
857         case OPC_RTI:
858             AsmAddr = *(unsigned*) (DbgSP + 0x102);
859             break;
860
861     }
862 }
863
864
865
866 static void AsmHome (void)
867 /* Set the cursor to home position */
868 {
869     AsmAddr = brk_pc;
870 }
871
872
873
874 static void InitAsm (void)
875 /* Initialize the asm window */
876 {
877     AsmHome ();
878     UpdateAsm ();
879 }
880
881
882
883 static char AsmHandler (void)
884 /* Get characters and handle them */
885 {
886     char c;
887     unsigned Last;
888
889     while (1) {
890
891         /* Update the window contents */
892         Last = UpdateAsm ();
893
894         /* Read and handle input */
895         switch (c = GetKeyUpdate ()) {
896
897             case  '+':
898                 AsmAddr = Last;
899                 break;
900
901             case '-':
902                 AsmAddr = AsmBack (AsmAddr, AsmFrame.fd_height);
903                 break;
904
905             case 't':
906 #ifdef CH_F2
907             case CH_F2:
908 #endif
909                 DbgToggleUserBreak (AsmAddr);
910                 break;
911
912             case 'f':
913                 AsmFollow ();
914                 break;
915
916             case 'g':
917                 InputGoto (&AsmAddr);
918                 break;
919
920             case 'o':
921                 AsmHome ();
922                 break;
923
924             case 'p':
925                 brk_pc = AsmAddr;
926                 break;
927
928             case 'a':
929 #ifdef CH_CURS_UP
930             case CH_CURS_UP:
931 #endif
932                 AsmAddr = AsmBack (AsmAddr, 1);
933                 break;
934
935             case 'z':
936 #ifdef CH_CURS_DOWN
937             case CH_CURS_DOWN:
938 #endif
939                 AsmAddr += DbgDisAsmLen (AsmAddr);
940                 break;
941
942             default:
943                 return c;
944
945         }
946     }
947 }
948
949
950
951 /*****************************************************************************/
952 /*                           Register window stuff                           */
953 /*****************************************************************************/
954
955
956
957 static unsigned UpdateReg (void)
958 /* Update the register window */
959 {
960     unsigned char x1 = RegFrame.fd_x1 + 5;
961     unsigned char x2 = x1 + 2;
962     unsigned char y = RegFrame.fd_y1;
963
964     /* Print the register contents */
965     gotoxy (x1, ++y);   cputhex16 (brk_pc);
966     gotoxy (x2, ++y);   cputhex8  (brk_sr);
967     gotoxy (x2, ++y);   cputhex8  (brk_a);
968     gotoxy (x2, ++y);   cputhex8  (brk_x);
969     gotoxy (x2, ++y);   cputhex8  (brk_y);
970     gotoxy (x2, ++y);   cputhex8  (DbgSP);
971     gotoxy (x1, ++y);   cputhex16 (DbgCS);
972     gotoxy (x1, ++y);   cputhex16 (DbgHI);
973
974     /* Not needed */
975     return 0;
976 }
977
978
979
980 static void InitReg (void)
981 /* Initialize the register window */
982 {
983     UpdateReg ();
984 }
985
986
987
988 static char RegHandler (void)
989 /* Get characters and handle them */
990 {
991     return GetKeyUpdate ();
992 }
993
994
995
996 /*****************************************************************************/
997 /*                             Stack window stuff                            */
998 /*****************************************************************************/
999
1000
1001
1002 static unsigned UpdateStack (void)
1003 /* Update the stack window */
1004 {
1005     unsigned char mem = StackAddr;
1006     unsigned char x1 = StackFrame.fd_x1 + 1;
1007     unsigned char x2 = x1 + 6;
1008     unsigned char y;
1009
1010     for (y = StackFrame.fd_y2-1; y > StackFrame.fd_y1; --y) {
1011         gotoxy (x1, y);
1012         cputhex8 (mem);
1013         gotoxy (x2, y);
1014         cputhex8 (* (unsigned char*) (mem + 0x100));
1015         ++mem;
1016     }
1017     return mem;
1018 }
1019
1020
1021
1022 static void StackHome (void)
1023 /* Set the cursor to home position */
1024 {
1025     StackAddr = DbgSP + 1;
1026 }
1027
1028
1029
1030 static void InitStack (void)
1031 /* Initialize the stack window */
1032 {
1033     StackHome ();
1034     UpdateStack ();
1035 }
1036
1037
1038
1039 static char StackHandler (void)
1040 /* Get characters and handle them */
1041 {
1042     char c;
1043     unsigned char BytesPerPage = StackFrame.fd_height;
1044
1045     while (1) {
1046
1047         /* Read and handle input */
1048         switch (c = GetKeyUpdate ()) {
1049
1050             case  '+':
1051                 StackAddr += BytesPerPage;
1052                 break;
1053
1054             case '-':
1055                 StackAddr -= BytesPerPage;
1056                 break;
1057
1058             case 'o':
1059                 StackHome ();
1060                 break;
1061
1062             case 'a':
1063 #ifdef CH_CURS_UP:
1064             case CH_CURS_UP:
1065 #endif
1066                 --StackAddr;
1067                 break;
1068
1069             case 'z':
1070 #ifdef CH_CURS_DOWN
1071             case CH_CURS_DOWN:
1072 #endif
1073                 ++StackAddr;
1074                 break;
1075
1076             default:
1077                 return c;
1078
1079         }
1080
1081         /* Update the window contents */
1082         UpdateStack ();
1083     }
1084 }
1085
1086
1087
1088 /*****************************************************************************/
1089 /*                           C Stack window stuff                            */
1090 /*****************************************************************************/
1091
1092
1093
1094 static unsigned UpdateCStack (void)
1095 /* Update the C stack window */
1096 {
1097     unsigned mem     = CStackAddr;
1098     unsigned char x  = CStackFrame.fd_x1 + 5;
1099     unsigned char y;
1100
1101     for (y = CStackFrame.fd_y2-1; y > CStackFrame.fd_y1; --y) {
1102         gotoxy (x, y);
1103         cputhex16 (* (unsigned*)mem);
1104         mem += 2;
1105     }
1106     cputsxy (CStackFrame.fd_x1+1, CStackFrame.fd_y2-1, "->");
1107     return mem;
1108 }
1109
1110
1111
1112 static void CStackHome (void)
1113 /* Set the cursor to home position */
1114 {
1115     CStackAddr = DbgCS;
1116 }
1117
1118
1119
1120 static void InitCStack (void)
1121 /* Initialize the C stack window */
1122 {
1123     CStackHome ();
1124     UpdateCStack ();
1125 }
1126
1127
1128
1129 static char CStackHandler (void)
1130 /* Get characters and handle them */
1131 {
1132     char c;
1133     unsigned char BytesPerPage = CStackFrame.fd_height * 2;
1134
1135     while (1) {
1136
1137         /* Read and handle input */
1138         switch (c = GetKeyUpdate ()) {
1139
1140             case  '+':
1141                 CStackAddr += BytesPerPage;
1142                 break;
1143
1144             case '-':
1145                 CStackAddr -= BytesPerPage;
1146                 break;
1147
1148             case 'o':
1149                 CStackHome ();
1150                 break;
1151
1152             case 'a':
1153 #ifdef CH_CURS_UP
1154             case CH_CURS_UP:
1155 #endif
1156                 CStackAddr -= 2;
1157                 break;
1158
1159             case 'z':
1160 #ifdef CH_CURS_DOWN
1161             case CH_CURS_DOWN:
1162 #endif
1163                 CStackAddr += 2;
1164                 break;
1165
1166             default:
1167                 return c;
1168
1169         }
1170
1171         /* Update the window contents */
1172         UpdateCStack ();
1173     }
1174 }
1175
1176
1177
1178 /*****************************************************************************/
1179 /*                             Dump window stuff                             */
1180 /*****************************************************************************/
1181
1182
1183
1184 static unsigned UpdateDump (void)
1185 /* Update the dump window */
1186 {
1187     char Buf [MAX_X];
1188     unsigned char y;
1189     unsigned mem = DumpAddr;
1190     unsigned char x = DumpFrame.fd_x1 + 1;
1191     unsigned char* p = (unsigned char*) mem;
1192
1193     for (y = DumpFrame.fd_y1+1; y < DumpFrame.fd_y2; ++y) {
1194         cputsxy (x, y, DbgMemDump (mem, Buf, DUMP_BYTES));
1195         mem += DUMP_BYTES;
1196     }
1197     return mem;
1198 }
1199
1200
1201
1202 static void DumpHome (void)
1203 /* Set the cursor to home position */
1204 {
1205     DumpAddr = 0;
1206 }
1207
1208
1209
1210 static char DumpHandler (void)
1211 /* Get characters and handle them */
1212 {
1213     char c;
1214     unsigned BytesPerPage = DumpFrame.fd_height * 8;
1215
1216     while (1) {
1217
1218         /* Read and handle input */
1219         switch (c = GetKeyUpdate ()) {
1220
1221             case  '+':
1222                 DumpAddr += BytesPerPage;
1223                 break;
1224
1225             case '-':
1226                 DumpAddr -= BytesPerPage;
1227                 break;
1228
1229             case 'g':
1230                 InputGoto (&DumpAddr);
1231                 break;
1232
1233             case 'o':
1234                 DumpHome ();
1235                 break;
1236
1237             case 'a':
1238 #ifdef CH_CURS_UP
1239             case CH_CURS_UP:
1240 #endif
1241                 DumpAddr -= 8;
1242                 break;
1243
1244             case 'z':
1245 #ifdef CH_CURS_DOWN
1246             case CH_CURS_DOWN:
1247 #endif
1248                 DumpAddr += 8;
1249                 break;
1250
1251             default:
1252                 return c;
1253
1254         }
1255
1256         /* Update the window contents */
1257         UpdateDump ();
1258     }
1259 }
1260
1261
1262
1263 /*****************************************************************************/
1264 /*                             Help window stuff                             */
1265 /*****************************************************************************/
1266
1267
1268
1269 static char HelpHandler (void)
1270 /* Get characters and handle them */
1271 {
1272     /* Activate the frame */
1273     int OldActive = ActiveFrame;
1274     ActivateFrame (WIN_HELP, 1);
1275
1276     /* Say that we're waiting for a key */
1277     AnyKeyPrompt ();
1278
1279     /* Get a character and discard it */
1280     cgetc ();
1281
1282     /* Redraw the old stuff */
1283     Redraw (OldActive);
1284
1285     /* Done, return no char */
1286     return 0;
1287 }
1288
1289
1290
1291 /*****************************************************************************/
1292 /*                                Singlestep                                 */
1293 /*****************************************************************************/
1294
1295
1296
1297 static unsigned GetArg16 (void)
1298 /* Read an argument */
1299 {
1300     return *(unsigned*)(brk_pc+1);
1301 }
1302
1303
1304
1305 static unsigned GetStack16 (unsigned char Offs)
1306 /* Fetch a 16 bit value from stack top */
1307 {
1308     return *(unsigned*)(DbgSP+Offs+0x101);
1309 }
1310
1311
1312
1313 static void SetRTSBreak (void)
1314 /* Set a breakpoint at the return target */
1315 {
1316     DbgSetTmpBreak (GetStack16 (0) + 1);
1317 }
1318
1319
1320
1321 static void SingleStep (char StepInto)
1322 {
1323     signed char Offs;
1324
1325     switch (*(unsigned char*) brk_pc) {
1326
1327         case OPC_JMP:
1328             /* Set breakpoint at target */
1329             DbgSetTmpBreak (GetArg16 ());
1330             return;
1331
1332         case OPC_JMPIND:
1333             /* Indirect jump, ignore CPU error when crossing page */
1334             DbgSetTmpBreak (*(unsigned*)GetArg16 ());
1335             return;
1336
1337         case OPC_BPL:
1338         case OPC_BMI:
1339         case OPC_BVC:
1340         case OPC_BVS:
1341         case OPC_BCC:
1342         case OPC_BCS:
1343         case OPC_BNE:
1344         case OPC_BEQ:
1345             /* Be sure not to set the breakpoint twice if this is a jump to
1346              * the following instruction.
1347              */
1348             Offs = ((signed char*)brk_pc)[1];
1349             if (Offs) {
1350                 DbgSetTmpBreak (brk_pc + Offs + 2);
1351             }
1352             break;
1353
1354         case OPC_RTS:
1355             /* Set a breakpoint at the return target */
1356             SetRTSBreak ();
1357             return;
1358
1359         case OPC_RTI:
1360             /* Set a breakpoint at the return target */
1361             DbgSetTmpBreak (GetStack16 (1));
1362             return;
1363
1364         case OPC_JSR:
1365             if (StepInto) {
1366                 /* Set breakpoint at target */
1367                 DbgSetTmpBreak (GetArg16 ());
1368                 return;
1369             }
1370             break;
1371     }
1372
1373     /* Place a breakpoint behind the instruction */
1374     DbgSetTmpBreak (brk_pc + DbgDisAsmLen (brk_pc));
1375 }
1376
1377
1378
1379 /*****************************************************************************/
1380 /*                        High level window handling                         */
1381 /*****************************************************************************/
1382
1383
1384
1385 static void RedrawStatic (char Frame)
1386 /* Redraw static display stuff */
1387 {
1388     /* Reset the active frame */
1389     ActiveFrame = -1;
1390
1391     /* Clear the screen hide the cursor */
1392     (void) bordercolor (COLOR_BORDER);
1393     (void) bgcolor (COLOR_BACKGROUND);
1394     clrscr ();
1395     cursor (0);
1396
1397     /* Build the frame layout of the screen */
1398     (void) textcolor (COLOR_FRAMELOW);
1399     DrawFrames ();
1400
1401     /* Draw the prompt line */
1402     HelpPrompt ();
1403
1404     /* Activate the active frame */
1405     ActivateFrame (Frame, 0);
1406 }
1407
1408
1409
1410 static void Redraw (char Frame)
1411 /* Redraw the display in case it's garbled */
1412 {
1413     /* Redraw the static stuff */
1414     RedrawStatic (Frame);
1415
1416     /* Init the window contents */
1417     UpdateAsm ();
1418     UpdateReg ();
1419     UpdateStack ();
1420     UpdateCStack ();
1421     UpdateDump ();
1422 }
1423
1424
1425
1426 static char GetKeyUpdate (void)
1427 /* Wait for a key updating the windows in the background */
1428 {
1429     static unsigned char Win;
1430
1431     /* While there are no keys... */
1432     while (!kbhit ()) {
1433
1434         switch (Win) {
1435
1436             case 0:
1437                 UpdateAsm ();
1438                 break;
1439
1440             case 1:
1441                 UpdateStack ();
1442                 break;
1443
1444             case 2:
1445                 UpdateCStack ();
1446                 break;
1447
1448             case 3:
1449                 UpdateDump ();
1450                 break;
1451         }
1452
1453         Win = (Win + 1) & 0x03;
1454
1455     }
1456
1457     /* We have a key - return it */
1458     return cgetc ();
1459 }
1460
1461
1462
1463 /*****************************************************************************/
1464 /*                       Externally visible functions                        */
1465 /*****************************************************************************/
1466
1467
1468
1469 void DbgEntry (void)
1470 /* Start up the debugger */
1471 {
1472     static unsigned char FirstTime = 1;
1473     char c;
1474     char done;
1475
1476     /* If this is the first call, setup the display */
1477     if (FirstTime) {
1478         FirstTime = 0;
1479
1480         /* Draw the window, default active frame is ASM frame */
1481         RedrawStatic (WIN_ASM);
1482         InitAsm ();
1483         InitReg ();
1484         InitStack ();
1485         InitCStack ();
1486         UpdateDump ();
1487     }
1488
1489     /* Only initialize variables here, don't do a display update. The actual
1490      * display update will be done while waiting for user input.
1491      */
1492     AsmHome ();
1493     UpdateReg ();               /* Must update this (static later) */
1494     StackHome ();
1495     CStackHome ();
1496
1497     /* Wait for user input */
1498     done = 0;
1499     while (!done) {
1500         c = Frames [ActiveFrame]->fd_func ();
1501         switch (c) {
1502
1503             case '1':
1504             case '2':
1505             case '3':
1506             case '4':
1507             case '5':
1508                 ActivateFrame (c - '1', 0);
1509                 break;
1510
1511             case '?':
1512 #ifdef CH_F1
1513             case CH_F1:
1514 #endif
1515                 HelpHandler ();
1516                 break;
1517
1518             case 'u':
1519 #ifdef CH_F3
1520             case CH_F3:
1521 #endif
1522                 /* Go until return */
1523                 SetRTSBreak ();
1524                 done = 1;
1525                 break;
1526
1527             case 'h':
1528 #ifdef CH_F4
1529             case CH_F4:
1530 #endif
1531                 /* Go to cursor, only possible if cursor not at current PC */
1532                 if (AsmAddr != brk_pc) {
1533                     DbgSetTmpBreak (AsmAddr);
1534                     done = 1;
1535                 }
1536                 break;
1537
1538             case ' ':
1539 #ifdef CH_F7
1540             case CH_F7:
1541 #endif
1542                 SingleStep (1);
1543                 if (DbgTmpBreaksOk ()) {
1544                     /* Could set breakpoints */
1545                     done = 1;
1546                 }
1547                 break;
1548
1549             case '\n':
1550 #ifdef CH_F8
1551             case CH_F8:
1552 #endif
1553                 SingleStep (0);
1554                 if (DbgTmpBreaksOk ()) {
1555                     /* Could set breakpoints */
1556                     done = 1;
1557                 }
1558                 break;
1559
1560             case 'c':
1561             case 0:
1562                 done = 1;
1563                 break;
1564
1565             case 's':
1566                 /* Skip instruction */
1567                 brk_pc += DbgDisAsmLen (brk_pc);
1568                 InitAsm ();
1569                 break;
1570
1571             case 'r':
1572                 /* Redraw screen */
1573                 Redraw (ActiveFrame);
1574                 break;
1575
1576             case 'q':
1577                 /* Quit program */
1578                 clrscr ();
1579                 exit (1);
1580
1581         }
1582     }
1583 }
1584
1585
1586