| home
| contents
| previous
| next page
| send comment
| send link
| add bookmark |
EXC_DBUG.CPP
debugger
/*-------------------------------------------------------------------*
Routines for the interactive debugger
File: exc_dbug.cpp
Module: interpreter
by: Stephen R. Schmitt
*-------------------------------------------------------------------*/
#include "tpl_data.h"
#include <conio.h>
#include <dos.h>
#include <stdio.h>
#include <string.h>
#define MILLISECS 500
union scancode
{
int ch;
char c[2];
};
struct source_file
{
char name[FILE_NAME_LENGTH];
line *first;
line *srce;
int srce_line_num;
line *top;
int top_line_num;
line *last;
int last_line_num;
};
/*
* External variables
*/
/* in tpl_main.cpp */
extern char *Debug_screen;
/*
* Global declarations
*/
bool Step_flag; // true for single step
bool Trace_flag; // controls step over
bool Set_no_trace; // sets trace flag
int Break_count; // count of breakpoints
int Break_list[MAX_BREAKS]; // list of breakpoints
int Exec_line; // current execution line
char File_name[FILE_NAME_LENGTH];
source_file File[MAX_FILES]; // source file structures
int Exec_srce; // current source file
int Lin; // current screen line
int Col; // current screen column
int Top = SCREEN_TOP; // top line of screen
int Bottom = SCREEN_BOTTOM; // bottom line of screen
int Right = 80; // right column of screen
int Left = 1; // left column of screen
int Tab_size = 4; // default tab size
int Msg_col = 70; // column for messages
int Msg_lin = 1; // line for messages
/*-------------------------------------------------------------------*
Code Section
*-------------------------------------------------------------------*/
/*
* "init_debugger" initializes the interactive debugger
*
* returns: nothing
*/
void init_debugger()
{
int i;
// initialize the debugger's globals
Trace_flag = true;
Set_no_trace = false;
Step_flag = true;
Break_count = 0;
for (i = 0; i < MAX_BREAKS; i++)
Break_list[i] = 0;
for (i = 0; i < MAX_FILES; i++)
memset(File[i].name, '\0', FILE_NAME_LENGTH);
}
/*
* "read_debugger_command" reads a debugging command from the console
*
* returns: nothing
*/
void read_debugger_command()
{
union scancode command;
bool command_flag = true;
while (command_flag == true)
{
command.ch = read_key(); // get a command
switch (command.c[0])
{
case 0: // function key
command_flag = execute_debugger_command(command.c[1]);
break;
case 27: // Esc key
exit_interpreter();
break;
case 32: // space bar
command_flag = false; // do next step
break;
default:
break;
}
}
}
/*
* "execute_debugger_command" processes a debugging command
*
* returns: signal to resume or wait
*/
bool execute_debugger_command(unsigned char function)
{
bool command_flag = true;
// select debugger action
switch (function)
{
case 63: /* F5 key - trace (auto step) */
Step_flag = false;
command_flag = false; // do next step
break;
case 65: /* F7 key */
set_breakpoint();
break;
case 67: /* F9 key - step over routine */
Set_no_trace = true;
command_flag = false; // do next step
break;
case 68: /* F10 key */
help();
break;
case 72: /* up arrow key */
up_arrow();
break;
case 73: /* Page Up key */
page_up();
break;
case 80: /* down arrow key */
down_arrow();
break;
case 81: /* Page Down key */
page_down();
break;
case 100: /* Ctrl + F7 keys */
remove_breakpoint();
break;
case 110: /* Alt + F7 keys */
clear_breakpoints();
break;
case 118: /* Ctrl + Page Down keys */
goto_end_of_file();
break;
case 132: /* Ctrl + Page Up key */
goto_start_of_file();
break;
default:
break;
}
return command_flag;
}
/*
* "trace_statement_execution" traces the execution of a statement
*
* returns: nothing
*/
void trace_statement_execution(char *filename, // source file name
int line_num) // source file line
{
Exec_line = line_num;
if (Break_count > 0)
{
// check for breakpoint
for (int i = 0; i < Break_count; i++)
{
if (line_num == Break_list[i])
{
// found a breakpoint
goto_next_statement(line_num);
read_debugger_command();
break; // exit for loop
}
}
}
// show each step
goto_next_source(filename);
goto_next_statement(line_num);
if (Step_flag == true) // single step
read_debugger_command();
else // trace (auto step)
{
delay(MILLISECS);
if (kbhit()) // this stops auto step
Step_flag = true;
}
}
/*
* "set_breakpoint" sets a breakpoint at the cursor location
*
* returns: nothing
*/
void set_breakpoint()
{
if (Break_count < MAX_BREAKS)
{
// add breakpoint to the end of the list
Break_list[Break_count] = File[Exec_srce].srce_line_num;
Break_count++;
write_source_screen(File[Exec_srce].top, File[Exec_srce].top_line_num);
}
else
{
gotoxy(Left, Bottom);
textattr(RED*16 + WHITE);
clreol();
cputs("Break list is full.");
textattr(BLACK*16 + WHITE);
}
}
/*
* "remove_breakpoint" removes a specific breakpoint
*
* returns: nothing
*/
void remove_breakpoint()
{
for (int i = 0; i < Break_count; i++)
{
if (Break_list[i] == File[Exec_srce].srce_line_num)
{
// remove point and condense the list
Break_list[i] = 0;
Break_count--;
for (int j = i; j < Break_count; j++)
Break_list[j] = Break_list[j + 1];
Break_list[j + 1] = 0;
write_source_screen(File[Exec_srce].top, File[Exec_srce].top_line_num);
}
}
}
/*
* "clear_breakpoints" removes all current breakpoints
*
* returns: nothing
*/
void clear_breakpoints()
{
union scancode key; // keyboard input
// create a message window
gettext(1, 1, 80, 25, Debug_screen);
single_box_window(24, 20, 54, 22, RED, WHITE);
gotoxy(4, 2);
cputs("Clear breakpoints(y|n)?");
key.ch = read_key(); // if yes, clear list
if ((key.c[0] == 'y') || (key.c[0] == 'Y'))
{
for (int i = 0; i < MAX_BREAKS; i++)
Break_list[i] = 0;
Break_count = 0;
restore_debug_window();
write_source_screen(File[Exec_srce].top, File[Exec_srce].top_line_num);
}
else
restore_debug_window();
}
/*
* "write_watch" sends watch expression value to screen
*
* returns: nothing
*/
void write_watch(char *result)
{
gotoxy(Left, Bottom);
textattr(GREEN*16 + WHITE);
clreol();
cputs(" ");
cputs(result);
if (Step_flag == true) // single step
read_key();
else // trace (auto step)
{
delay(MILLISECS * 2);
if (kbhit()) // this stops auto step
Step_flag = true;
}
textattr(BLACK*16 + WHITE);
}
/*
* "help" displays list of debugger commands
*
* returns: nothing
*/
void help()
{
char *msg[] = {
"Space step into",
"Esc exit interpreter",
"F5 set automatic stepping mode",
"F7 set breakpoint at current line",
"Ctrl+F7 remove breakpoint from current line",
"Alt+F7 clear all breakpoints",
"F9 step over",
"F10 help (this screen)"};
gettext(1, 1, 80, 25, Debug_screen);
_setcursortype(_NOCURSOR);
single_box_window(15, 5, 65, 16, LIGHTGRAY, BLACK);
gotoxy(18, 1);
cputs(" Debug commands ");
// write text
for (int i = 0; i < 8; i++)
{
gotoxy(4, 3 + i);
cputs(msg[i]);
}
// wait for any key then return
read_key();
restore_debug_window();
}
/*-------------------------------------------------------------------*
Screen functions for debugger
*-------------------------------------------------------------------*/
/*
* "read_key" obtains a two byte character code from the keyboard
*
* returns: the scancode of the key
*/
int read_key()
{
union REGS r;
r.h.ah = 0;
return int86(0x16, &r, &r);
}
/*
* "goto_next_source" sets display for current source file
*
* returns: nothing
*/
void goto_next_source(char *filename)
{
int i;
for (i = 0; i < MAX_FILES; i++)
{
if (strcmp(filename, File[i].name) == 0)
{
// already opened
Exec_srce = i;
strcpy(File_name, filename);
write_source_screen(File[Exec_srce].top, File[Exec_srce].top_line_num);
break;
}
else if (File[i].name[0] == NULL)
{
// new file
Exec_srce = i;
strcpy(File_name, filename);
strcpy(File[i].name, filename);
load_source_file();
break;
}
}
if (i == MAX_FILES - 1)
runtime_error(M_FILE, M_OPEN, M_FAILED, M_0);
}
/*
* "goto_next_statement" sets display for current source line
*
* returns: nothing
*/
void goto_next_statement(int line_num) // source line
{
int offset = 8;
// find new top of screen
File[Exec_srce].srce = File[Exec_srce].top;
File[Exec_srce].srce_line_num = File[Exec_srce].top_line_num;
if ((line_num < File[Exec_srce].top_line_num + 2 )||
(line_num > File[Exec_srce].top_line_num + 18))
{
File[Exec_srce].srce = File[Exec_srce].first;
File[Exec_srce].srce_line_num = 1;
while (File[Exec_srce].srce_line_num < (line_num - offset))
{
File[Exec_srce].srce = File[Exec_srce].srce->next;
File[Exec_srce].srce_line_num++;
}
}
// set new top line and re-write screen
File[Exec_srce].top = File[Exec_srce].srce;
File[Exec_srce].top_line_num = File[Exec_srce].srce_line_num;
write_source_screen(File[Exec_srce].top,
File[Exec_srce].top_line_num);
// find execution line
Lin = Top;
while (File[Exec_srce].srce_line_num != line_num)
{
File[Exec_srce].srce = File[Exec_srce].srce->next;
File[Exec_srce].srce_line_num++;
Lin++;
}
// reset cursor
Col = Left;
gotoxy(Col, Lin);
}
/*
* "write_source_line" refreshes a line by clearing the entire line
* and then re-writing it
*
* returns: nothing
*/
void write_source_line(line *text_line, // ptr to line struct
int screen_line) // where to write
{
char buffer[MAX_STRING_LENGTH];
gotoxy(Left, screen_line); // left edge of window
clreol();
if (text_line->string != NULL) // end of file?
{
for (int i = 0; i < Right - Left + 1; i++)
{
buffer[i] = text_line->string[i];
if (text_line->string[i] == NULL)
break;
}
cputs(buffer);
}
}
/*
* "write_source_screen" refreshes the screen by clearing each line
* and then re-writing it
*
* returns: nothing
*/
void write_source_screen(line *text_line, // top text line
int text_line_num) // top line number
{
int i, j, k;
line *save_text = text_line; // at top
int save_text_num = text_line_num; // at top
// update top status line
_setcursortype(_NOCURSOR);
textattr(BLACK*16 + LIGHTGRAY);
gotoxy(Left, Top - 1);
clreol();
cprintf(" %d:%d ", Exec_line, 1);
gotoxy(30, Top - 1);
cputs(File_name);
gotoxy(Msg_col, Msg_lin);
cprintf("F10 Help ");
textattr(BLACK*16 + WHITE);
// write screen to buffer then put on screen
char attribute = BLACK*16 + WHITE;
for (i = 0; i < 4000; i += 2) // clear buffer
{
Debug_screen[i] = ' ';
Debug_screen[i + 1] = attribute;
}
j = Top;
while (j <= Bottom) // write text to buffer
{
k = (j - Top) * (Right - Left + 1) * 2;
for (i = Left - 1; i < Right; i++)
{
if (text_line->string[i] == NULL)
break;
else
Debug_screen[k] = text_line->string[i];
k += 2;
}
text_line = text_line->next;
text_line_num++;
j++;
if (text_line->string == NULL) // end of file?
break;
}
puttext(Left, Top, Right, Bottom, Debug_screen);
// highlight break lines
if (Break_count > 0) // find break lines
{
text_line = save_text;
text_line_num = save_text_num;
j = Top;
while (j <= Bottom)
{
for (i = 0; i < Break_count; i++)
{
if (text_line_num == Break_list[i])
{
textattr(RED*16 + WHITE);
write_source_line(text_line, j);
textattr(BLACK*16 + WHITE);
break;
}
}
if (text_line_num < File[Exec_srce].last_line_num)
{
text_line = text_line->next;
text_line_num++;
j++;
}
else
break;
}
}
// highlight current execution line
text_line = save_text;
text_line_num = save_text_num;
j = Top; // from the top
while (j <= Bottom) // find execution line
{
if (text_line_num == Exec_line)
{
textattr(LIGHTGRAY*16 + BLACK);
write_source_line(text_line, j);
textattr(BLACK*16 + WHITE);
break;
}
text_line = text_line->next;
text_line_num++;
j++;
if (text_line->string == NULL) // end of file?
break;
}
_setcursortype(_NORMALCURSOR);
gotoxy(Col, Lin);
}
/*
* "load_source_file" loads a source file from disk storage.
* The new file is made the current file.
*
* returns: nothing
*/
void load_source_file()
{
char buffer[MAX_STRING_LENGTH];
// initialize text line data structure
File[Exec_srce].first = new line;
File[Exec_srce].first->string = NULL;
File[Exec_srce].last = new line;
File[Exec_srce].last->string = NULL;
File[Exec_srce].srce_line_num = 1;
File[Exec_srce].last_line_num = 1;
File[Exec_srce].srce = File[Exec_srce].first;
// initialize linked list pointers
File[Exec_srce].first->prev = NULL;
File[Exec_srce].first->next = File[Exec_srce].last;
File[Exec_srce].last->prev = File[Exec_srce].first;
File[Exec_srce].last->next = NULL;
// load the source file
FILE *program = fopen(File_name, "r"); // read only
if (program == NULL)
runtime_error(M_FILE, M_OPEN, M_FAILED, M_0);
int i = 0; // first char number
int k = 1; // first line number
char ch = fgetc(program); // first character
while (1)
{
if ((ch == EOF ) || (ch == '\n') || (i > MAX_STRING_LENGTH - 1))
{
// null terminate current buffer and copy to line structure
buffer[i] = NULL;
int length = strlen(buffer);
File[Exec_srce].srce->string = new char[length + 1];
strcpy(File[Exec_srce].srce->string, buffer);
// add a new line of text after current text line
line *new_line = new line;
if (new_line == NULL)
runtime_error(M_OUT, M_OF, M_MEMORY, M_0);
File[Exec_srce].srce->next = new_line;
new_line->prev = File[Exec_srce].srce;
new_line->next = File[Exec_srce].last;
File[Exec_srce].last->prev = new_line;
File[Exec_srce].srce = new_line;
File[Exec_srce].srce->string = NULL;
i = 0; // reset char number
k++; // increment line number
if (ch == EOF)
break;
}
else // update current line
{
// replace tabs with Tab_size spaces
if (ch == '\t')
{
for (int j = 0; j < Tab_size; j++)
{
buffer[i] = ' ';
i++;
}
}
else
{
buffer[i] = ch;
i++;
}
}
ch = fgetc(program); // get next character
}
fclose(program);
// open text window
window(1, 1, 80, 25);
textattr(BLACK*16 + LIGHTGRAY);
clrscr();
_setcursortype(_NORMALCURSOR);
// update display with new file name
gotoxy(1, 1);
cprintf(" %d:%d ", File[Exec_srce].srce_line_num, 1);
gotoxy(30, 1);
cputs(File_name);
gotoxy(Msg_col, Msg_lin);
cprintf("F10 Help ");
textcolor(WHITE);
// write file on the screen
Lin = Top;
Col = Left;
File[Exec_srce].srce = File[Exec_srce].first;
for (i = Top; i <= Bottom; i++)
{
if (File[Exec_srce].srce->string == NULL)
break;
else
{
write_source_line(File[Exec_srce].srce, Lin);
File[Exec_srce].srce = File[Exec_srce].srce->next;
File[Exec_srce].srce_line_num++;
Lin++;
}
}
// set editing parameters
File[Exec_srce].srce_line_num = 1;
File[Exec_srce].top_line_num = 1;
File[Exec_srce].last_line_num = k;
File[Exec_srce].srce = File[Exec_srce].first;
File[Exec_srce].top = File[Exec_srce].first;
Lin = Top;
Col = Left;
gotoxy(Col, Lin);
}
/*
* "single_box_window" creates a window with a single box frame
*
* returns: nothing
*/
void single_box_window(int left, int top, int right, int bottom,
int background, int foreground)
{
register int i;
window(left, top, right, bottom);
textattr(background*16 + foreground);
clrscr();
gotoxy(2, 1);
putch(218); // left, top corner
for (i = 3; i < right - left; i++)
putch(196); // horizontal
putch(191); // right, top corner
for (i = 2; i < bottom - top + 1; i++)
{
gotoxy(2, i);
putch(179); // left vertical
gotoxy(right - left, i);
putch(179); // right vertical
}
gotoxy(2, bottom - top + 1);
putch(192); // left, bottom corner
for (i = 3; i < right - left; i++)
putch(196); // horizontal
putch(217); // right, bottom corner
}
/*
* "restore_debug_window" restores original editing window saved
* in Screen_buffer on call to pull down menu function
*
* returns: nothing
*/
void restore_debug_window()
{
window(1, 1, 80, 25);
textattr(BLACK*16 + WHITE);
clrscr();
_setcursortype(_NORMALCURSOR);
puttext(1, 1, 80, 25, Debug_screen);
gotoxy(Col, Lin);
}
/*
* "up_arrow" moves the cursor up
*
* returns: nothing
*/
void up_arrow()
{
if (File[Exec_srce].srce_line_num == 1)
/* do nothing */;
else if (Lin == 2)
{
// move screen down
movetext(1, 2, 80, 24, 1, 3);
File[Exec_srce].srce_line_num--;
File[Exec_srce].srce = File[Exec_srce].srce->prev;
gotoxy(1, 2);
write_source_line(File[Exec_srce].srce, Lin);
}
else
{
Lin--;
File[Exec_srce].srce_line_num--;
File[Exec_srce].srce = File[Exec_srce].srce->prev;
}
gotoxy(Col, Lin);
}
/*
* "page_up" moves the screen down by 20 lines toward the beginning
* of the file
*
* returns: nothing
*/
void page_up()
{
// find text line which is to be at top of text screen
int top_line = File[Exec_srce].srce_line_num - (Lin - Top);
int next_top_line = top_line - 20;
if (next_top_line <= 1)
{
next_top_line = 1;
File[Exec_srce].srce = File[Exec_srce].first;
File[Exec_srce].srce_line_num = 1;
}
else
{
while (next_top_line != File[Exec_srce].srce_line_num)
{
File[Exec_srce].srce = File[Exec_srce].srce->prev;
File[Exec_srce].srce_line_num--;
}
}
// re-write the text screen
write_source_screen(File[Exec_srce].srce,
File[Exec_srce].srce_line_num);
// reset editor
int j = Top;
while (j != Lin)
{
if (File[Exec_srce].srce_line_num == File[Exec_srce].last_line_num)
{
Lin = j;
break;
}
else
{
File[Exec_srce].srce = File[Exec_srce].srce->next;
File[Exec_srce].srce_line_num++;
j++;
}
}
gotoxy(Col, Lin);
}
/*
* "down_arrow" moves the cursor down
*
* returns: nothing
*/
void down_arrow()
{
if (File[Exec_srce].srce_line_num == File[Exec_srce].last_line_num)
/* do nothing */;
else if (Lin == 25)
{
// move screen up
movetext(1, 3, 80, 25, 1, 2);
File[Exec_srce].srce_line_num++;
File[Exec_srce].srce = File[Exec_srce].srce->next;
gotoxy(1, 25);
write_source_line(File[Exec_srce].srce, Lin);
}
else
{
Lin++;
File[Exec_srce].srce_line_num++;
File[Exec_srce].srce = File[Exec_srce].srce->next;
}
gotoxy(Col, Lin);
}
/*
* "page_down" moves the screen up by 20 lines
*
* returns: nothing
*/
void page_down()
{
// find text line which is to be at top of text screen
int top_line = File[Exec_srce].srce_line_num - (Lin - Top);
int next_top_line = top_line + 20;
if (next_top_line < File[Exec_srce].last_line_num)
{
if (next_top_line > File[Exec_srce].srce_line_num)
{
while (next_top_line != File[Exec_srce].srce_line_num)
{
File[Exec_srce].srce = File[Exec_srce].srce->next;
File[Exec_srce].srce_line_num++;
}
}
else
{
while (next_top_line != File[Exec_srce].srce_line_num)
{
File[Exec_srce].srce = File[Exec_srce].srce->prev;
File[Exec_srce].srce_line_num--;
}
}
write_source_screen(File[Exec_srce].srce, File[Exec_srce].srce_line_num);
// reset editor
int j = Top;
while (j != Lin)
{
if (File[Exec_srce].srce_line_num == File[Exec_srce].last_line_num)
{
Lin = j;
break;
}
else
{
File[Exec_srce].srce = File[Exec_srce].srce->next;
File[Exec_srce].srce_line_num++;
j++;
}
}
gotoxy(Col, Lin);
}
}
/*
* "goto_end_of_file" sets the editor to the end of the file
*
* returns: nothing
*/
void goto_end_of_file()
{
// find text line which is to be at top of text screen
int next_top_line = File[Exec_srce].last_line_num - 20;
if (next_top_line < 1)
next_top_line = 1;
if (next_top_line > File[Exec_srce].srce_line_num)
{
while (next_top_line != File[Exec_srce].srce_line_num)
{
File[Exec_srce].srce = File[Exec_srce].srce->next;
File[Exec_srce].srce_line_num++;
}
}
else
{
while (next_top_line != File[Exec_srce].srce_line_num)
{
File[Exec_srce].srce = File[Exec_srce].srce->prev;
File[Exec_srce].srce_line_num--;
}
}
write_source_screen(File[Exec_srce].srce, File[Exec_srce].srce_line_num);
// reset editor
Lin = Top;
Col = Left;
gotoxy(Col, Lin);
}
/*
* "goto_start_of_file" sets editor to the beginning of the file
*
* returns: nothing
*/
void goto_start_of_file()
{
File[Exec_srce].srce = File[Exec_srce].first;
File[Exec_srce].srce_line_num = 1;
write_source_screen(File[Exec_srce].srce, File[Exec_srce].srce_line_num);
// reset editor
Lin = Top;
Col = Left;
gotoxy(Col, Lin);
}
| home
| contents
| previous
| next page
| send comment
| send link
| add bookmark |
Copyright © 2004, Stephen R. Schmitt