| 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