cc65/libsrc/dbg/dbg.c

1594 lines
36 KiB
C

/*
** dbg.c
**
** Ullrich von Bassewitz, 08.08.1998
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <ctype.h>
#include <6502.h>
#include <dbg.h>
/*****************************************************************************/
/* Function forwards */
/*****************************************************************************/
/* Forwards for handler functions */
static char AsmHandler (void);
static char RegHandler (void);
static char StackHandler (void);
static char CStackHandler (void);
static char DumpHandler (void);
static char HelpHandler (void);
/* Forwards for other functions */
static void DisplayPrompt (const char* s);
static void SingleStep (char StepInto);
static void RedrawStatic (char Frame);
static void Redraw (char Frame);
static char GetKeyUpdate (void);
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Color definitions */
#if defined(__C16__)
# define COLOR_BORDER (BCOLOR_DARKBLUE | CATTR_LUMA6)
# define COLOR_BACKGROUND COLOR_WHITE
# define COLOR_TEXTHIGH COLOR_BLACK
# define COLOR_TEXTLOW COLOR_GRAY1
# define COLOR_FRAMEHIGH COLOR_BLACK
# define COLOR_FRAMELOW COLOR_GRAY2
#else
# if defined(COLOR_GRAY3)
# define COLOR_BORDER COLOR_BLACK
# define COLOR_BACKGROUND COLOR_BLACK
# define COLOR_TEXTHIGH COLOR_WHITE
# define COLOR_TEXTLOW COLOR_GRAY3
# define COLOR_FRAMEHIGH COLOR_WHITE
# define COLOR_FRAMELOW COLOR_GRAY3
# else
# if defined(__APPLE2__)
# define COLOR_BORDER COLOR_BLACK
# define COLOR_BACKGROUND COLOR_BLACK
# define COLOR_TEXTHIGH COLOR_BLACK
# define COLOR_TEXTLOW COLOR_BLACK
# define COLOR_FRAMEHIGH COLOR_BLACK
# define COLOR_FRAMELOW COLOR_BLACK
# else
# define COLOR_BORDER COLOR_BLACK
# define COLOR_BACKGROUND COLOR_BLACK
# define COLOR_TEXTHIGH COLOR_WHITE
# define COLOR_TEXTLOW COLOR_WHITE
# define COLOR_FRAMEHIGH COLOR_WHITE
# define COLOR_FRAMELOW COLOR_WHITE
# endif
# endif
#endif
#ifndef COLOR_BLACK
# define COLOR_BLACK 0
#endif
#ifndef COLOR_WHITE
# define COLOR_WHITE 1
#endif
/* Screen definitions */
#if defined(__CBM610__)
# define BIGSCREEN
# define MAX_X 80
# define MAX_Y 25
# define DUMP_BYTES 16
#elif defined(__APPLE2__) || defined(__ATARI__)
# define MAX_X 40
# define MAX_Y 24
# define DUMP_BYTES 8
#else
# define MAX_X 40
# define MAX_Y 25
# define DUMP_BYTES 8
#endif
/* Replacement key definitions */
#ifndef CH_DEL
# define CH_DEL ('H' - 'A' + 1) /* Ctrl+H */
#endif
/* Replacement char definitions */
#ifndef CH_ULCORNER
# define CH_ULCORNER '+'
#endif
#ifndef CH_URCORNER
# define CH_URCORNER '+'
#endif
#ifndef CH_LLCORNER
# define CH_LLCORNER '+'
#endif
#ifndef CH_LRCORNER
# define CH_LRCORNER '+'
#endif
#ifndef CH_TTEE
# define CH_TTEE '+'
#endif
#ifndef CH_LTEE
# define CH_LTEE '+'
#endif
#ifndef CH_RTEE
# define CH_RTEE '+'
#endif
#ifndef CH_BTEE
# define CH_BTEE '+'
#endif
#ifndef CH_CROSS
# define CH_CROSS '+'
#endif
/* Defines for opcodes */
#define OPC_BRK 0x00
#define OPC_BPL 0x10
#define OPC_JSR 0x20
#define OPC_BMI 0x30
#define OPC_RTI 0x40
#define OPC_JMP 0x4C
#define OPC_BVC 0x50
#define OPC_RTS 0x60
#define OPC_JMPIND 0x6C
#define OPC_BVS 0x70
#define OPC_BCC 0x90
#define OPC_BCS 0xB0
#define OPC_BNE 0xD0
#define OPC_BEQ 0xF0
/* Register values that are used also in the assembler stuff */
extern unsigned char DbgSP; /* Stack pointer */
extern unsigned DbgCS; /* C stack pointer */
extern unsigned DbgHI; /* High 16 bit of primary reg */
/* Descriptor for one text line */
typedef struct {
unsigned char x;
unsigned char y;
const char* text;
} TextDesc;
/* Window descriptor */
typedef struct {
unsigned char fd_tl; /* Top left char */
unsigned char fd_tr; /* Top right char */
unsigned char fd_bl; /* Bottom left char */
unsigned char fd_br; /* Bottom right char */
unsigned char fd_x1, fd_y1; /* Upper left corner */
unsigned char fd_x2, fd_y2; /* Lower right corner */
unsigned char fd_width, fd_height; /* Redundant but faster */
unsigned char fd_visible; /* Is the window currently visible? */
char (*fd_func) (void); /* Handler function */
unsigned char fd_textcount; /* Number of text lines to print */
const TextDesc* fd_text; /* Static text in the window */
} FrameDesc;
/* Texts for the windows */
static const TextDesc RegText [] = {
{ 1, 0, "PC" },
{ 1, 1, "SR" },
{ 1, 2, "A" },
{ 1, 3, "X" },
{ 1, 4, "Y" },
{ 1, 5, "SP" },
{ 1, 6, "CS" },
{ 1, 7, "HI" }
};
static const TextDesc HelpText [] = {
{ 1, 0, "F1, ? Help" },
{ 1, 1, "F2, t Toggle breakpoint" },
{ 1, 2, "F3, u Run until subroutine returns" },
{ 1, 3, "F4, h Run to cursor" },
{ 1, 4, "F7, space Step into" },
{ 1, 5, "F8, enter Step over" },
{ 1, 6, "1-5 Select active window" },
{ 1, 7, "+ Page down" },
{ 1, 8, "- Page up" },
{ 1, 9, "Cursor Move up/down" },
{ 1, 10, "a/z Move up/down" },
{ 1, 11, "c Continue" },
{ 1, 12, "f Follow instruction" },
{ 1, 13, "o Goto origin" },
{ 1, 14, "p Use as new PC value" },
{ 1, 15, "q Quit" },
{ 1, 16, "r Redraw screen" },
{ 1, 17, "s Skip next instruction" },
};
/* Window data */
static const FrameDesc AsmFrame = {
CH_ULCORNER, CH_TTEE, CH_LTEE, CH_CROSS,
0, 0, MAX_X - 10, 15,
MAX_X - 11, 14,
1,
AsmHandler,
0, 0
};
static const FrameDesc RegFrame = {
CH_TTEE, CH_URCORNER, CH_LTEE, CH_RTEE,
MAX_X - 10, 0, MAX_X - 1, 9,
8, 8,
1,
RegHandler,
sizeof (RegText) / sizeof (RegText [0]), RegText
};
static const FrameDesc StackFrame = {
CH_LTEE, CH_RTEE, CH_CROSS, CH_RTEE,
MAX_X - 10, 9, MAX_X - 1, 15,
8, 5,
1,
StackHandler,
0, 0
};
static const FrameDesc CStackFrame = {
CH_CROSS, CH_RTEE, CH_BTEE, CH_LRCORNER,
MAX_X - 10, 15, MAX_X - 1, MAX_Y - 1,
8, MAX_Y - 17,
1,
CStackHandler,
0, 0
};
static const FrameDesc DumpFrame = {
CH_LTEE, CH_CROSS, CH_LLCORNER, CH_BTEE,
0, 15, MAX_X - 10, MAX_Y-1,
MAX_X - 11, MAX_Y - 17,
1,
DumpHandler,
0, 0
};
static const FrameDesc HelpFrame = {
CH_ULCORNER, CH_URCORNER, CH_LLCORNER, CH_LRCORNER,
0, 0, MAX_X - 1, MAX_Y-1,
MAX_X - 2, MAX_Y - 2,
0,
HelpHandler,
sizeof (HelpText) / sizeof (HelpText [0]), HelpText
};
static const FrameDesc* const Frames [] = {
&AsmFrame,
&RegFrame,
&StackFrame,
&CStackFrame,
&DumpFrame,
&HelpFrame
};
/* Number of active frame, -1 = none */
static int ActiveFrame = -1;
/* Window names */
#define WIN_ASM 0
#define WIN_REG 1
#define WIN_STACK 2
#define WIN_CSTACK 3
#define WIN_DUMP 4
#define WIN_HELP 5
/* Other window data */
static unsigned AsmAddr; /* Start address of output */
static unsigned DumpAddr; /* Start address of output */
static unsigned CStackAddr; /* Start address of output */
static unsigned char StackAddr; /* Start address of output */
/* Prompt line data */
static const char* ActivePrompt = 0; /* Last prompt line displayed */
static char PromptColor; /* Color behind prompt */
static char PromptLength; /* Length of current prompt string */
/* Values for the bk_use field of struct BreakPoint */
#define BRK_EMPTY 0x00
#define BRK_USER 0x01
#define BRK_TMP 0x80
/* Structure describing a breakpoint */
typedef struct {
unsigned bk_addr; /* Address, 0 if unused */
unsigned char bk_opc; /* Opcode */
unsigned char bk_use; /* 1 if in use, 0 otherwise */
} BreakPoint;
/* Temporary breakpoints - also accessed from the assembler source */
#define MAX_USERBREAKS 10
unsigned char DbgBreakCount = 0;
BreakPoint DbgBreaks [MAX_USERBREAKS+2];
/*****************************************************************************/
/* Forwards for functions in the assembler source */
/*****************************************************************************/
BreakPoint* DbgGetBreakSlot (void);
/* Search for a free breakpoint slot. Return a pointer to the slot or 0 */
BreakPoint* DbgIsBreak (unsigned Addr);
/* Check if there is a user breakpoint at the given address, if so, return
** a pointer to the slot, else return 0.
*/
/*****************************************************************************/
/* Frame/window drawing code */
/*****************************************************************************/
static void DrawFrame (register const FrameDesc* F, char Active)
/* Draw one window frame */
{
const TextDesc* T;
unsigned char Count;
unsigned char tl, tr, bl, br;
unsigned char x1, y1, width;
unsigned char OldColor;
/* Determine the characters for the corners, set frame color */
if (Active) {
OldColor = textcolor (COLOR_FRAMEHIGH);
tl = CH_ULCORNER;
tr = CH_URCORNER;
bl = CH_LLCORNER;
br = CH_LRCORNER;
} else {
OldColor = textcolor (COLOR_FRAMELOW);
tl = F->fd_tl;
tr = F->fd_tr;
bl = F->fd_bl;
br = F->fd_br;
}
/* Get the coordinates into locals for faster access */
x1 = F->fd_x1;
y1 = F->fd_y1;
width = F->fd_width;
/* Top line */
cputcxy (x1, y1, tl);
chline (width);
cputc (tr);
/* Left line */
cvlinexy (x1, ++y1, F->fd_height);
/* Bottom line */
cputc (bl);
chline (width);
cputc (br);
/* Right line */
cvlinexy (F->fd_x2, y1, F->fd_height);
/* If the window has static text associated, print the text */
(void) textcolor (COLOR_TEXTLOW);
Count = F->fd_textcount;
T = F->fd_text;
while (Count--) {
cputsxy (x1 + T->x, y1 + T->y, T->text);
++T;
}
/* Set the old color */
(void) textcolor (OldColor);
}
static void DrawFrames (void)
/* Draw all frames */
{
unsigned char I;
const FrameDesc* F;
/* Build the frame layout of the screen */
for (I = 0; I < sizeof (Frames) / sizeof (Frames [0]); ++I) {
F = Frames [I];
if (F->fd_visible) {
DrawFrame (F, 0);
}
}
}
static void ActivateFrame (int Num, unsigned char Clear)
/* Activate a new frame, deactivate the old one */
{
unsigned char y;
register const FrameDesc* F;
if (ActiveFrame != Num) {
/* Deactivate the old one */
if (ActiveFrame >= 0) {
DrawFrame (Frames [ActiveFrame], 0);
}
/* Activate the new one */
if ((ActiveFrame = Num) >= 0) {
F = Frames [ActiveFrame];
/* Clear the frame if requested */
if (Clear) {
for (y = F->fd_y1+1; y < F->fd_y2; ++y) {
cclearxy (F->fd_x1+1, y, F->fd_width);
}
}
DrawFrame (F, 1);
}
/* Redraw the current prompt line */
DisplayPrompt (ActivePrompt);
}
}
/*****************************************************************************/
/* Prompt line */
/*****************************************************************************/
static void DisplayPrompt (const char* s)
/* Display a prompt */
{
unsigned char OldColor;
/* Remember the current color */
OldColor = textcolor (COLOR_TEXTHIGH);
/* Clear the old prompt if there is one */
if (ActivePrompt) {
(void) textcolor (PromptColor);
chlinexy ((MAX_X - PromptLength) / 2, MAX_Y-1, PromptLength);
}
/* Get the new prompt data */
ActivePrompt = s;
PromptColor = OldColor;
PromptLength = strlen (ActivePrompt);
/* Display the new prompt */
(void) textcolor (COLOR_TEXTHIGH);
cputsxy ((MAX_X - PromptLength) / 2, MAX_Y-1, ActivePrompt);
/* Restore the old color */
(void) textcolor (PromptColor);
}
static void HelpPrompt (void)
/* Display a prompt line mentioning the help key */
{
DisplayPrompt ("Press F1 for help");
}
static void AnyKeyPrompt (void)
{
DisplayPrompt ("Press any key to continue");
}
static char IsAbortKey (char C)
/* Return true if C is an abort key */
{
#if defined(CH_ESC)
if (C == CH_ESC) {
return 1;
}
#endif
#if defined(CH_STOP)
if (C == CH_STOP) {
return 1;
}
#endif
#if !defined(CH_ESC) && !defined(CH_STOP)
/* Avoid compiler warning about unused parameter */
(void) C;
#endif
return 0;
}
static char Input (char* Prompt, char* Buf, unsigned char Count)
/* Read input from the user, return 1 on success, 0 if aborted */
{
int Frame;
unsigned char OldColor;
unsigned char OldCursor;
unsigned char x1;
unsigned char i;
unsigned char done;
char c;
/* Clear the current prompt line */
cclearxy (0, MAX_Y-1, MAX_X);
/* Display the new prompt */
OldColor = textcolor (COLOR_TEXTHIGH);
cputsxy (0, MAX_Y-1, Prompt);
(void) textcolor (COLOR_TEXTLOW);
/* Remember where we are, enable the cursor */
x1 = wherex ();
OldCursor = cursor (1);
/* Get input and handle it */
i = done = 0;
do {
c = cgetc ();
if (isalnum (c) && i < Count) {
Buf [i] = c;
cputcxy (x1 + i, MAX_Y-1, c);
++i;
} else if (i > 0 && c == CH_DEL) {
--i;
cputcxy (x1 + i, MAX_Y-1, ' ');
gotoxy (x1 + i, MAX_Y-1);
} else if (c == '\n') {
Buf [i] = '\0';
done = 1;
} else if (IsAbortKey (c)) {
/* Abort */
done = 2;
}
} while (!done);
/* Reset settings, display old prompt line */
cursor (OldCursor);
(void) textcolor (OldColor);
DrawFrames ();
Frame = ActiveFrame;
ActiveFrame = -1;
ActivateFrame (Frame, 0);
return (done == 1);
}
static char InputHex (char* Prompt, unsigned* Val)
/* Prompt for a hexadecimal value. Return 0 on failure. */
{
char Buf [5];
char* P;
char C;
unsigned V;
/* Read input from the user (4 digits max), check input */
if (Input (Prompt, Buf, sizeof (Buf)-1) && isxdigit (Buf [0])) {
/* Check the characters and convert to hex */
P = Buf;
V = 0;
while ((C = *P) && isxdigit (C)) {
V <<= 4;
if (isdigit (C)) {
C -= '0';
} else {
C = toupper (C) - ('A' - 10);
}
V += C;
++P;
}
/* Assign the value */
*Val = V;
/* Success */
return 1;
} else {
/* Failure */
return 0;
}
}
static void ErrorPrompt (const char* Msg)
/* Display an error message and wait for a key */
{
/* Save the current prompt */
const char* OldPrompt = ActivePrompt;
/* Display the new one */
DisplayPrompt (Msg);
/* Wait for a key and discard it */
cgetc ();
/* Restore the old prompt */
DisplayPrompt (OldPrompt);
}
static char InputGoto (unsigned* Addr)
/* Prompt "Goto" and read an address. Print an error and return 0 on failure. */
{
char Ok;
Ok = InputHex ("Goto: ", Addr);
if (!Ok) {
ErrorPrompt ("Invalid input - press a key");
}
return Ok;
}
static void BreakInRomError (void)
/* Print an error message if we cannot set a breakpoint */
{
ErrorPrompt ("Cannot set breakpoint - press a key");
}
/*****************************************************************************/
/* Breakpoint handling */
/*****************************************************************************/
static void DbgSetTmpBreak (unsigned Addr)
/* Set a breakpoint */
{
BreakPoint* B = DbgGetBreakSlot ();
B->bk_addr = Addr;
B->bk_use = BRK_TMP;
}
static void DbgToggleUserBreak (unsigned Addr)
/* Set a breakpoint */
{
register BreakPoint* B = DbgIsBreak (Addr);
if (B) {
/* We have a breakpoint, remove it */
B->bk_use = BRK_EMPTY;
--DbgBreakCount;
} else {
/* We don't have a breakpoint, set one */
if (DbgBreakCount >= MAX_USERBREAKS) {
ErrorPrompt ("Too many breakpoints - press a key");
} else {
/* Test if we can set a breakpoint at that address */
if (!DbgIsRAM (Addr)) {
BreakInRomError ();
} else {
/* Set the breakpoint */
B = DbgGetBreakSlot ();
B->bk_addr = Addr;
B->bk_use = BRK_USER;
++DbgBreakCount;
}
}
}
}
static void DbgResetTmpBreaks (void)
/* Reset all temporary breakpoints */
{
unsigned char i;
BreakPoint* B = DbgBreaks;
for (i = 0; i < MAX_USERBREAKS; ++i) {
if (B->bk_use == BRK_TMP) {
B->bk_use = BRK_EMPTY;
}
++B;
}
}
static unsigned char DbgTmpBreaksOk (void)
/* Check if the temporary breakpoints can be set, if so, return 1, if not,
** reset them all and return 0.
*/
{
unsigned char i;
BreakPoint* B = DbgBreaks;
for (i = 0; i < MAX_USERBREAKS; ++i) {
if (B->bk_use == BRK_TMP && !DbgIsRAM (B->bk_addr)) {
BreakInRomError ();
DbgResetTmpBreaks ();
return 0;
}
++B;
}
return 1;
}
/*****************************************************************************/
/* Assembler window stuff */
/*****************************************************************************/
static unsigned AsmBack (unsigned mem, unsigned char lines)
/* Go back in the assembler window the given number of lines (calculate
** new start address).
*/
{
unsigned cur;
unsigned adr [32];
unsigned char in;
unsigned offs = 6;
while (1) {
in = 0;
cur = mem - (lines * 3) - offs;
while (1) {
cur += DbgDisAsmLen (cur);
adr [in] = cur;
in = (in + 1) & 0x1F;
if (cur >= mem) {
if (cur == mem || offs == 12) {
/* Found */
return adr [(in - lines - 1) & 0x1F];
} else {
/* The requested address is inside an instruction, go back
** one more byte and try again.
*/
++offs;
break;
}
}
}
}
}
static unsigned UpdateAsm (void)
/* Update the assembler window starting at the given address */
{
char buf [MAX_X];
unsigned char len;
unsigned char y;
unsigned char width = AsmFrame.fd_width;
unsigned char x = AsmFrame.fd_x1 + 1;
unsigned m = AsmBack (AsmAddr, 2);
for (y = AsmFrame.fd_y1+1; y < AsmFrame.fd_y2; ++y) {
len = DbgDisAsm (m, buf, width);
if (m == brk_pc) {
buf [4] = '-';
buf [5] = '>';
}
if (DbgIsBreak (m)) {
buf [5] = '*';
}
if (m == AsmAddr) {
revers (1);
cputsxy (1, y, buf);
revers (0);
} else {
cputsxy (1, y, buf);
}
m += len;
}
return m;
}
static unsigned AsmArg16 (void)
/* Return a 16 bit argument */
{
return *(unsigned*)(AsmAddr+1);
}
static void AsmFollow (void)
/* Follow the current instruction */
{
switch (*(unsigned char*) AsmAddr) {
case OPC_JMP:
case OPC_JSR:
AsmAddr = AsmArg16 ();
break;
case OPC_JMPIND:
AsmAddr = *(unsigned*)AsmArg16 ();
break;
case OPC_BPL:
case OPC_BMI:
case OPC_BVC:
case OPC_BVS:
case OPC_BCC:
case OPC_BCS:
case OPC_BNE:
case OPC_BEQ:
AsmAddr = AsmAddr + 2 + *(signed char*)(AsmAddr+1);
break;
case OPC_RTS:
AsmAddr = (*(unsigned*) (DbgSP + 0x101) + 1);
break;
case OPC_RTI:
AsmAddr = *(unsigned*) (DbgSP + 0x102);
break;
}
}
static void AsmHome (void)
/* Set the cursor to home position */
{
AsmAddr = brk_pc;
}
static void InitAsm (void)
/* Initialize the asm window */
{
AsmHome ();
UpdateAsm ();
}
static char AsmHandler (void)
/* Get characters and handle them */
{
char c;
unsigned Last;
while (1) {
/* Update the window contents */
Last = UpdateAsm ();
/* Read and handle input */
switch (c = GetKeyUpdate ()) {
case '+':
AsmAddr = Last;
break;
case '-':
AsmAddr = AsmBack (AsmAddr, AsmFrame.fd_height);
break;
case 't':
#ifdef CH_F2
case CH_F2:
#endif
DbgToggleUserBreak (AsmAddr);
break;
case 'f':
AsmFollow ();
break;
case 'g':
InputGoto (&AsmAddr);
break;
case 'o':
AsmHome ();
break;
case 'p':
brk_pc = AsmAddr;
break;
case 'a':
#ifdef CH_CURS_UP
case CH_CURS_UP:
#endif
AsmAddr = AsmBack (AsmAddr, 1);
break;
case 'z':
#ifdef CH_CURS_DOWN
case CH_CURS_DOWN:
#endif
AsmAddr += DbgDisAsmLen (AsmAddr);
break;
default:
return c;
}
}
}
/*****************************************************************************/
/* Register window stuff */
/*****************************************************************************/
static unsigned UpdateReg (void)
/* Update the register window */
{
unsigned char x1 = RegFrame.fd_x1 + 5;
unsigned char x2 = x1 + 2;
unsigned char y = RegFrame.fd_y1;
/* Print the register contents */
gotoxy (x1, ++y); cputhex16 (brk_pc);
gotoxy (x2, ++y); cputhex8 (brk_sr);
gotoxy (x2, ++y); cputhex8 (brk_a);
gotoxy (x2, ++y); cputhex8 (brk_x);
gotoxy (x2, ++y); cputhex8 (brk_y);
gotoxy (x2, ++y); cputhex8 (DbgSP);
gotoxy (x1, ++y); cputhex16 (DbgCS);
gotoxy (x1, ++y); cputhex16 (DbgHI);
/* Not needed */
return 0;
}
static void InitReg (void)
/* Initialize the register window */
{
UpdateReg ();
}
static char RegHandler (void)
/* Get characters and handle them */
{
return GetKeyUpdate ();
}
/*****************************************************************************/
/* Stack window stuff */
/*****************************************************************************/
static unsigned UpdateStack (void)
/* Update the stack window */
{
unsigned char mem = StackAddr;
unsigned char x1 = StackFrame.fd_x1 + 1;
unsigned char x2 = x1 + 6;
unsigned char y;
for (y = StackFrame.fd_y2-1; y > StackFrame.fd_y1; --y) {
gotoxy (x1, y);
cputhex8 (mem);
gotoxy (x2, y);
cputhex8 (* (unsigned char*) (mem + 0x100));
++mem;
}
return mem;
}
static void StackHome (void)
/* Set the cursor to home position */
{
StackAddr = DbgSP + 1;
}
static void InitStack (void)
/* Initialize the stack window */
{
StackHome ();
UpdateStack ();
}
static char StackHandler (void)
/* Get characters and handle them */
{
char c;
unsigned char BytesPerPage = StackFrame.fd_height;
while (1) {
/* Read and handle input */
switch (c = GetKeyUpdate ()) {
case '+':
StackAddr += BytesPerPage;
break;
case '-':
StackAddr -= BytesPerPage;
break;
case 'o':
StackHome ();
break;
case 'a':
#ifdef CH_CURS_UP
case CH_CURS_UP:
#endif
--StackAddr;
break;
case 'z':
#ifdef CH_CURS_DOWN
case CH_CURS_DOWN:
#endif
++StackAddr;
break;
default:
return c;
}
/* Update the window contents */
UpdateStack ();
}
}
/*****************************************************************************/
/* C Stack window stuff */
/*****************************************************************************/
static unsigned UpdateCStack (void)
/* Update the C stack window */
{
unsigned mem = CStackAddr;
unsigned char x = CStackFrame.fd_x1 + 5;
unsigned char y;
for (y = CStackFrame.fd_y2-1; y > CStackFrame.fd_y1; --y) {
gotoxy (x, y);
cputhex16 (* (unsigned*)mem);
mem += 2;
}
cputsxy (CStackFrame.fd_x1+1, CStackFrame.fd_y2-1, "->");
return mem;
}
static void CStackHome (void)
/* Set the cursor to home position */
{
CStackAddr = DbgCS;
}
static void InitCStack (void)
/* Initialize the C stack window */
{
CStackHome ();
UpdateCStack ();
}
static char CStackHandler (void)
/* Get characters and handle them */
{
char c;
unsigned char BytesPerPage = CStackFrame.fd_height * 2;
while (1) {
/* Read and handle input */
switch (c = GetKeyUpdate ()) {
case '+':
CStackAddr += BytesPerPage;
break;
case '-':
CStackAddr -= BytesPerPage;
break;
case 'o':
CStackHome ();
break;
case 'a':
#ifdef CH_CURS_UP
case CH_CURS_UP:
#endif
CStackAddr -= 2;
break;
case 'z':
#ifdef CH_CURS_DOWN
case CH_CURS_DOWN:
#endif
CStackAddr += 2;
break;
default:
return c;
}
/* Update the window contents */
UpdateCStack ();
}
}
/*****************************************************************************/
/* Dump window stuff */
/*****************************************************************************/
static unsigned UpdateDump (void)
/* Update the dump window */
{
char Buf [MAX_X];
unsigned char y;
unsigned mem = DumpAddr;
unsigned char x = DumpFrame.fd_x1 + 1;
unsigned char* p = (unsigned char*) mem;
for (y = DumpFrame.fd_y1+1; y < DumpFrame.fd_y2; ++y) {
cputsxy (x, y, DbgMemDump (mem, Buf, DUMP_BYTES));
mem += DUMP_BYTES;
}
return mem;
}
static void DumpHome (void)
/* Set the cursor to home position */
{
DumpAddr = 0;
}
static char DumpHandler (void)
/* Get characters and handle them */
{
char c;
unsigned BytesPerPage = DumpFrame.fd_height * 8;
while (1) {
/* Read and handle input */
switch (c = GetKeyUpdate ()) {
case '+':
DumpAddr += BytesPerPage;
break;
case '-':
DumpAddr -= BytesPerPage;
break;
case 'g':
InputGoto (&DumpAddr);
break;
case 'o':
DumpHome ();
break;
case 'a':
#ifdef CH_CURS_UP
case CH_CURS_UP:
#endif
DumpAddr -= 8;
break;
case 'z':
#ifdef CH_CURS_DOWN
case CH_CURS_DOWN:
#endif
DumpAddr += 8;
break;
default:
return c;
}
/* Update the window contents */
UpdateDump ();
}
}
/*****************************************************************************/
/* Help window stuff */
/*****************************************************************************/
static char HelpHandler (void)
/* Get characters and handle them */
{
/* Activate the frame */
int OldActive = ActiveFrame;
ActivateFrame (WIN_HELP, 1);
/* Say that we're waiting for a key */
AnyKeyPrompt ();
/* Get a character and discard it */
cgetc ();
/* Redraw the old stuff */
Redraw (OldActive);
/* Done, return no char */
return 0;
}
/*****************************************************************************/
/* Singlestep */
/*****************************************************************************/
static unsigned GetArg16 (void)
/* Read an argument */
{
return *(unsigned*)(brk_pc+1);
}
static unsigned GetStack16 (unsigned char Offs)
/* Fetch a 16 bit value from stack top */
{
return *(unsigned*)(DbgSP+Offs+0x101);
}
static void SetRTSBreak (void)
/* Set a breakpoint at the return target */
{
DbgSetTmpBreak (GetStack16 (0) + 1);
}
static void SingleStep (char StepInto)
{
signed char Offs;
switch (*(unsigned char*) brk_pc) {
case OPC_JMP:
/* Set breakpoint at target */
DbgSetTmpBreak (GetArg16 ());
return;
case OPC_JMPIND:
/* Indirect jump, ignore CPU error when crossing page */
DbgSetTmpBreak (*(unsigned*)GetArg16 ());
return;
case OPC_BPL:
case OPC_BMI:
case OPC_BVC:
case OPC_BVS:
case OPC_BCC:
case OPC_BCS:
case OPC_BNE:
case OPC_BEQ:
/* Be sure not to set the breakpoint twice if this is a jump to
** the following instruction.
*/
Offs = ((signed char*)brk_pc)[1];
if (Offs) {
DbgSetTmpBreak (brk_pc + Offs + 2);
}
break;
case OPC_RTS:
/* Set a breakpoint at the return target */
SetRTSBreak ();
return;
case OPC_RTI:
/* Set a breakpoint at the return target */
DbgSetTmpBreak (GetStack16 (1));
return;
case OPC_JSR:
if (StepInto) {
/* Set breakpoint at target */
DbgSetTmpBreak (GetArg16 ());
return;
}
break;
}
/* Place a breakpoint behind the instruction */
DbgSetTmpBreak (brk_pc + DbgDisAsmLen (brk_pc));
}
/*****************************************************************************/
/* High level window handling */
/*****************************************************************************/
static void RedrawStatic (char Frame)
/* Redraw static display stuff */
{
/* Reset the active frame */
ActiveFrame = -1;
/* Clear the screen hide the cursor */
(void) bordercolor (COLOR_BORDER);
(void) bgcolor (COLOR_BACKGROUND);
clrscr ();
cursor (0);
/* Build the frame layout of the screen */
(void) textcolor (COLOR_FRAMELOW);
DrawFrames ();
/* Draw the prompt line */
HelpPrompt ();
/* Activate the active frame */
ActivateFrame (Frame, 0);
}
static void Redraw (char Frame)
/* Redraw the display in case it's garbled */
{
/* Redraw the static stuff */
RedrawStatic (Frame);
/* Init the window contents */
UpdateAsm ();
UpdateReg ();
UpdateStack ();
UpdateCStack ();
UpdateDump ();
}
static char GetKeyUpdate (void)
/* Wait for a key updating the windows in the background */
{
static unsigned char Win;
/* While there are no keys... */
while (!kbhit ()) {
switch (Win) {
case 0:
UpdateAsm ();
break;
case 1:
UpdateStack ();
break;
case 2:
UpdateCStack ();
break;
case 3:
UpdateDump ();
break;
}
Win = (Win + 1) & 0x03;
}
/* We have a key - return it */
return cgetc ();
}
/*****************************************************************************/
/* Externally visible functions */
/*****************************************************************************/
void DbgEntry (void)
/* Start up the debugger */
{
static unsigned char FirstTime = 1;
char c;
char done;
/* If this is the first call, setup the display */
if (FirstTime) {
FirstTime = 0;
/* Draw the window, default active frame is ASM frame */
RedrawStatic (WIN_ASM);
InitAsm ();
InitReg ();
InitStack ();
InitCStack ();
UpdateDump ();
}
/* Only initialize variables here, don't do a display update. The actual
** display update will be done while waiting for user input.
*/
AsmHome ();
UpdateReg (); /* Must update this (static later) */
StackHome ();
CStackHome ();
/* Wait for user input */
done = 0;
while (!done) {
c = Frames [ActiveFrame]->fd_func ();
switch (c) {
case '1':
case '2':
case '3':
case '4':
case '5':
ActivateFrame (c - '1', 0);
break;
case '?':
#ifdef CH_F1
case CH_F1:
#endif
HelpHandler ();
break;
case 'u':
#ifdef CH_F3
case CH_F3:
#endif
/* Go until return */
SetRTSBreak ();
done = 1;
break;
case 'h':
#ifdef CH_F4
case CH_F4:
#endif
/* Go to cursor, only possible if cursor not at current PC */
if (AsmAddr != brk_pc) {
DbgSetTmpBreak (AsmAddr);
done = 1;
}
break;
case ' ':
#ifdef CH_F7
case CH_F7:
#endif
SingleStep (1);
if (DbgTmpBreaksOk ()) {
/* Could set breakpoints */
done = 1;
}
break;
case '\n':
#ifdef CH_F8
case CH_F8:
#endif
SingleStep (0);
if (DbgTmpBreaksOk ()) {
/* Could set breakpoints */
done = 1;
}
break;
case 'c':
case 0:
done = 1;
break;
case 's':
/* Skip instruction */
brk_pc += DbgDisAsmLen (brk_pc);
InitAsm ();
break;
case 'r':
/* Redraw screen */
Redraw (ActiveFrame);
break;
case 'q':
/* Quit program */
clrscr ();
/* Exit intentionally with error because one may
** say that DbgEntry is always abnormal.
*/
exit (EXIT_FAILURE);
}
}
}