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