home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
PROG_GEN
/
FSDB091A.ZIP
/
FULLSCR.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-03-14
|
57KB
|
2,149 lines
/* ---------------------------------------------------------------------- */
/* FULL SCREEN DEBUGGER */
/* */
/* Copyright 1994 by Morten Welinder, terra@diku.dk */
/* ---------------------------------------------------------------------- */
/*
This debugger is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This debugger is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with djgpp; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ---------------------------------------------------------------------- */
#define FULLSCR_VERSION 0.91
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <pc.h>
#include <dos.h>
#include <dpmi.h>
#include <go32.h>
#include <keys.h>
#define K_BackTab 0x10f
#define K_ELeft 0x24b
#include <setjmp.h>
#define far
#include "ed.h"
#define A_black 0
#include "syms.h"
#include "paging.h"
#include "unassmbl.h"
#include "npx.h"
/* ---------------------------------------------------------------------- */
/* Information about the initialization state of the debugger. Actually
I'm not sure that longjmp should ever be permitted. */
int can_longjmp = 0;
jmp_buf debugger_jmpbuf;
/* Display information. */
static int debug_screen_p;
static char *user_screen_save, *debug_screen_save;
static unsigned char screen_attr;
static unsigned char screen_attr_normal;
static unsigned char screen_attr_source;
static unsigned char screen_attr_focus;
static unsigned char screen_attr_break;
static unsigned char screen_attr_message;
static unsigned char screen_attr_error;
static int cols;
static int rows;
static int toplines, bottomlines;
/* Information about panes. */
#define PANECOUNT 9
static int pane, pane_positions[PANECOUNT], pane_pos;
static word32 data_dump_origin, data_dump_last, data_dump_size;
static word32 code_dump_origin, code_dump_last;
static word32 *code_pane_pos, *stack_dump_pos;
static int stack_dump_origin, stack_dump_last, stack_dump_more;
static int breakpoint_origin;
static char **whereis_pane_text;
static int whereis_text_count, whereis_origin;
static int code_pane_active;
static int npx_pane_active;
static int stack_pane_active;
static int info_pane_active;
static int whereis_pane_active;
/* Odds and ends. */
#define MAXINSTLEN 16
static int first_step;
static word32 main_entry;
static char *read_buffer;
NPX npx; /* non-static because declared so in "npx.h" */
static void redraw (int);
static char hexchars[] = "0123456789abcdef";
/* ---------------------------------------------------------------------- */
/* The presentation order of registers in the register pane. The three
tables must of course match. */
static char *regs_names[] = {
"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
"cs", "ds", "es", "fs", "gs", "ss",
"eip", "flg" };
static word32 *regs_addr[] = {
&a_tss.tss_eax, &a_tss.tss_ebx, &a_tss.tss_ecx, &a_tss.tss_edx,
&a_tss.tss_esi, &a_tss.tss_edi, &a_tss.tss_ebp, &a_tss.tss_esp,
(word32 *)&a_tss.tss_cs, (word32 *)&a_tss.tss_ds,
(word32 *)&a_tss.tss_es, (word32 *)&a_tss.tss_fs,
(word32 *)&a_tss.tss_gs, (word32 *)&a_tss.tss_ss,
&a_tss.tss_eip, &a_tss.tss_eflags };
/* g: general, !: special, s: segment, f: flags, \0: end-of-table */
static char regs_type[] = "ggggggg!ssssss!f";
/* ---------------------------------------------------------------------- */
/* Breakpoint data. When breakpoints are actually put into the cpu debug
registers, data breakpoints have first priority. Any code breakpoints
left over are set by patching the code to contain "Int 3". */
typedef enum { BP_Code = 0, BP_Write = 1, BP_Read = 3 } BP_TYPE;
typedef struct
{
word32 addr;
BP_TYPE type;
unsigned char length; /* 1, 2, or 4 bytes */
unsigned char saved;
unsigned char savedbyte; /* Patched-away byte. */
} BP_ENTRY;
static int breakpoint_count;
static BP_ENTRY *breakpoint_table;
static unsigned char int03 = 0xcc;
/* ---------------------------------------------------------------------- */
/* Store the contents of the NPX in the global variable `npx'. */
static void
save_npx (void)
{
asm( "\n\
inb $0xa0,%al \n\
testb $0x20,%al \n\
jz Lfclex_done \n\
xorl %eax,%eax \n\
outb %al,$0xf0 \n\
movb $0x20,%al \n\
outb %al,$0xa0 \n\
outb %al,$0x20 \n\
Lfclex_done: \n\
movl $_npx, %eax \n\
fnsave (%eax) \n\
fwait "
);
}
/* ---------------------------------------------------------------------- */
/* Reload the contents of the NPX from the global variable `npx'. */
static void
load_npx (void)
{
asm( "\n\
movl $_npx, %eax \n\
/* movb $0,4(%eax) */ /* clear pending exceptions */ \n\
frstor (%eax) "
);
/* At least on my 486 there is no need to clear pending exceptions. */
}
/* ---------------------------------------------------------------------- */
inline static void
put (int x, int y, unsigned char *txt)
{
unsigned char *p
= (unsigned char *)(debug_screen_save + 3) + 2 * (cols * y + x);
while (*txt)
*p++ = *txt++,
*p++ = screen_attr;
}
/* ---------------------------------------------------------------------- */
inline static void
putl (int x, int y, int l, unsigned char *txt)
{
unsigned char *p
= (unsigned char *)(debug_screen_save + 3) + 2 * (cols * y + x);
while (*txt && l > 0)
*p++ = *txt++,
*p++ = screen_attr,
l--;
while (l-- > 0)
*p++ = ' ',
*p++ = screen_attr;
}
/* ---------------------------------------------------------------------- */
inline static void
draw (int x, int y, unsigned char ch, int delta, int count)
{
short unsigned *p
= (short unsigned *)(debug_screen_save + 3) + (cols * y + x);
short unsigned attrch = ((unsigned)screen_attr << 8) + ch;
while (count--)
*p = attrch,
p += delta;
}
/* ---------------------------------------------------------------------- */
static void
highlight (int x, int y, int len)
{
unsigned short *p
= (unsigned short *)(debug_screen_save + 4) + (cols * y + x);
while (len--)
*(unsigned char *)p = screen_attr,
p++;
}
/* ---------------------------------------------------------------------- */
static void
frame (int x1, int y1, int x2, int y2)
{
draw (x1 + 1, y1, '─', 1, x2 - x1 - 1);
draw (x1 + 1, y2, '─', 1, x2 - x1 - 1);
draw (x1, y1 + 1, '│', cols, y2 - y1 - 1);
draw (x2, y1 + 1, '│', cols, y2 - y1 - 1);
put (x1, y1, "┌");
put (x2, y1, "┐");
put (x1, y2, "└");
put (x2, y2, "┘");
}
/* ---------------------------------------------------------------------- */
static void
put_screen (char *screen)
{
switch (*screen++)
{
case 0:
/* Text screen. */
ScreenSetCursor (screen[0], screen[1]);
ScreenUpdate (screen + 2);
break;
}
}
/* ---------------------------------------------------------------------- */
static char *
get_screen ()
{
char *p;
int r, c;
p = malloc (cols * rows * 2 + 3);
ScreenGetCursor (&r, &c);
p[0] = 0;
p[1] = r;
p[2] = c;
ScreenRetrieve (p + 3);
return p;
}
/* ---------------------------------------------------------------------- */
/* Display the debugger screen (if not already displayed). */
static void
debug_screen ()
{
if (!debug_screen_p)
{
user_screen_save = get_screen ();
put_screen (debug_screen_save);
debug_screen_p = 1;
}
}
/* ---------------------------------------------------------------------- */
/* Display the user screen (if not already displayed). */
static void
user_screen ()
{
if (debug_screen_p)
{
put_screen (user_screen_save);
free (user_screen_save);
debug_screen_p = 0;
}
}
/* ---------------------------------------------------------------------- */
/* Reportedly, `sleep' & `gettimeofday' are buggy under Dpmi -- emulate. */
static int
mysleep (int secs)
{
struct time now;
unsigned now100, then100;
gettime (&now);
then100
= ((now.ti_hour * 60 + now.ti_min) * 60 + now.ti_sec + secs) * 100
+ now.ti_hund;
do
{
gettime (&now);
now100 = ((now.ti_hour * 60 + now.ti_min) * 60 + now.ti_sec) * 100
+ now.ti_hund;
if (now100 < 100 || bioskey (1))
break; /* Day turnover */
}
while (now100 < then100);
return 0;
}
/* ---------------------------------------------------------------------- */
/* Display a message in CLASS using FMT and ... as printf parameters.
The class determines the colour of the message and for how long time
it is displayed. */
typedef enum { CL_Info, CL_Msg, CL_Error } CL_TYPE;
static void
message (CL_TYPE class, char *fmt, ...)
{
char *save, *buf = alloca (cols);
unsigned char saveattr = screen_attr;
int len, y = rows / 2;
vsprintf (buf, fmt, (&fmt) + 1);
len = strlen (buf);
save = debug_screen_save;
debug_screen_save = get_screen ();
switch (class)
{
case CL_Error:
screen_attr = screen_attr_error;
break;
default:
screen_attr = screen_attr_message;
}
frame (1, y - 1, cols - 2, y + 1);
draw (2, y, ' ', 1, cols - 4);
put ((cols - len) / 2, y, buf);
screen_attr = saveattr;
put_screen (debug_screen_save);
switch (class)
{
case CL_Info:
mysleep (2);
break;
case CL_Msg:
case CL_Error:
(void) getxkey ();
}
free (debug_screen_save);
debug_screen_save = save;
put_screen (debug_screen_save);
}
/* ---------------------------------------------------------------------- */
/* Read a string from the keyboard to `read_buffer'. The entry is started
with STARTTEXT. */
static int
read_string (char *starttext)
{
char *save;
int key, esc, pos, leave = 0, y = rows / 2;
save = debug_screen_save;
debug_screen_save = get_screen ();
strcpy (read_buffer, starttext);
pos = strlen (read_buffer);
frame (1, y - 1, cols - 2, y + 1);
do
{
draw (2, y, ' ', 1, cols - 4);
put (3, y, read_buffer);
pos = strlen (read_buffer);
put_screen (debug_screen_save);
ScreenSetCursor (y, 3 + pos);
key = getxkey ();
switch (key)
{
case K_Return:
leave = 1;
esc = 0;
break;
case K_Escape:
leave = 1;
esc = 1;
read_buffer[0] = '\0';
break;
case K_BackSpace:
if (pos > 0)
read_buffer[--pos] = '\0';
break;
default:
if (key >= ' ' && key <= 0xff && pos <= cols - 7)
{
read_buffer[pos++] = key & 0xff;
read_buffer[pos] = '\0';
}
}
}
while (!leave);
free (debug_screen_save);
debug_screen_save = save;
put_screen (debug_screen_save);
return esc;
}
/* ---------------------------------------------------------------------- */
static int
eval (char *expr, int *okp)
{
word32 sym;
sym = syms_name2val (expr);
if (undefined_symbol)
{
message (CL_Error, "Expression not understood");
return *okp = 0;
}
else
{
*okp = 1;
return (int) sym;
}
}
/* ---------------------------------------------------------------------- */
static int
read_eval (int *okp, char *starttext)
{
*okp = !read_string (starttext);
if (*okp && read_buffer[0] != '\0')
return eval (read_buffer, okp);
else
return *okp = 0;
}
/* ---------------------------------------------------------------------- */
static int
valid_addr (word32 vaddr, int len)
{
int a;
if (vaddr >= 0xffffffffl - len)
return 0;
len--;
for (a = 0; a < MAX_AREA; a++)
if ((vaddr + len <= areas[a].last_addr) && (vaddr >= areas[a].first_addr))
return 1;
return 0;
}
/* ---------------------------------------------------------------------- */
inline static int
valid_instaddr (word32 vaddr)
{
return valid_addr (vaddr, MAXINSTLEN);
}
/* ---------------------------------------------------------------------- */
/* Set physical breakpoint registers from virtual ones. */
static void
activate_breakpoints (void)
{
int b, no;
BP_ENTRY *bep;
no = 0;
edi.dr[7] = 0;
/* First handle data breakpoints. */
for (b = 0, bep = breakpoint_table; b < breakpoint_count; b++, bep++)
if (no <= 3 && bep->type != BP_Code)
{
bep->saved = 0;
edi.dr[7] |= ((bep->type + ((bep->length - 1) << 2)) << (16 + 4 * no)
| (2 << (2 * no)));
edi.dr[no] = bep->addr + edi.app_base;
no++;
}
/* Now handle code breakpoint. */
for (b = 0, bep = breakpoint_table; b < breakpoint_count; b++, bep++)
if (bep->type == BP_Code)
if (no <= 3)
{
bep->saved = 0;
edi.dr[7] |= ((BP_Code << (16 + 4 * no)) | (2 << (2 * no)));
edi.dr[no] = bep->addr + edi.app_base;
no++;
edi.dr[7] |= 0x00000300L; /* For 386s we set GE & LE bits. */
}
else
{
bep->saved = valid_addr (bep->addr, 1);
if (bep->saved)
{
read_child (bep->addr, &bep->savedbyte, 1);
write_child (bep->addr, &int03, 1);
}
}
}
/* ---------------------------------------------------------------------- */
/* Un-patch code. */
static void
deactivate_breakpoints (void)
{
int b;
BP_ENTRY *bep;
for (b = 0, bep = breakpoint_table; b < breakpoint_count; b++, bep++)
if (bep->saved)
write_child (bep->addr, &bep->savedbyte, 1);
}
/* ---------------------------------------------------------------------- */
static int
get_breakpoint (BP_TYPE type, int length, word32 addr)
{
int b;
for (b = 0; b < breakpoint_count; b++)
if (breakpoint_table[b].type == type
&& breakpoint_table[b].length == length
&& breakpoint_table[b].addr == addr)
return b;
return -1;
}
/* ---------------------------------------------------------------------- */
static void
reset_breakpoint (int b)
{
breakpoint_table[b] = breakpoint_table[--breakpoint_count];
breakpoint_table
= realloc (breakpoint_table, breakpoint_count * sizeof (BP_ENTRY));
}
/* ---------------------------------------------------------------------- */
static int
set_breakpoint (BP_TYPE type, int length, word32 addr)
{
int b;
b = breakpoint_count;
breakpoint_table
= realloc (breakpoint_table, ++breakpoint_count * sizeof (BP_ENTRY));
breakpoint_table[b].addr = addr;
breakpoint_table[b].type = type;
breakpoint_table[b].length = length;
return b;
}
/* ---------------------------------------------------------------------- */
/* From ORIGIN skip COUNT instructions forward (positive count) or
backwards (negative count). Backwards skipping cannot be assumed to
be working 100% perfectly, but in the presense of debug information
it is probably very, very close.
This function works poorly close to the extremes of segments. */
static int
code_skip (int origin, int count)
{
int len, *next, i, j, k, instcount, done, leave;
char *state, *inst, *source;
if (count >= 0)
{
while (count-- > 0)
if (valid_instaddr (origin))
{
(void) unassemble_proper (origin, &len);
origin += len;
}
else
origin ++;
return origin;
}
else
{
count = -count;
instcount = MAXINSTLEN * (count + 16) + 1;
next = alloca (instcount * sizeof (int));
memset (next, 0, instcount * sizeof (int));
state = alloca (instcount * sizeof (char));
memset (state, 0, instcount * sizeof (char));
done = 0;
do
{
for (i = 0; i < 2 * count; i++)
{
done++;
j = origin - done;
if (valid_instaddr (j))
{
inst = unassemble_proper (j, &len);
source = unassemble_source (j);
next[done] = j + len;
if (source)
{
leave = 0;
k = done;
while (j < origin && state[k] == 0 && !leave)
{
state[j++,k--] = 2;
while (len-- > 1)
state[j++,k--] = 3;
/* Since code and data is "never" mixed in 32 bit
code we don't need this. */
#if 0
leave = (strncmp (inst, "jmp", 3) == 0
|| strncmp (inst, "ret", 3) == 0
|| strncmp (inst, "iret", 4) == 0);
#endif
if (!leave)
inst = unassemble_proper (j, &len);
}
}
}
else
{
state[done] = 2;
next[done] = j + 1;
}
}
j = 1;
k = count;
while (k > 0 && j <= done && state[j] >= 2)
if (state[j++] == 2)
k--;
}
while (k > 0 && done + 2 * count <= instcount);
if (k == 0)
return origin - j + 1;
else
{
i = origin;
k = 0;
while (count > 0 && k <= done)
{
leave = 0;
j = MAXINSTLEN;
while (!leave && j > 1)
if (j + k <= done && next[j + k] == i)
leave = 1;
else
j--;
if (!leave)
i--, k++;
else
i -= j, k += j;
count--;
}
return i;
}
}
}
/* ---------------------------------------------------------------------- */
static void
code_pane_goto (word32 v)
{
int i = 0;
if (v >= code_dump_origin && v <= code_dump_last)
{
while (code_pane_pos[i] < v)
i++;
if (code_pane_pos [i] == code_pane_pos[i+1])
i++;
}
else
{
code_dump_origin = v;
if (valid_instaddr (v) && unassemble_source (v) != NULL)
i++;
}
if (pane == 0)
pane_pos = i;
else
pane_positions[0] = i;
}
/* ---------------------------------------------------------------------- */
inline static void
go (int bp)
{
if (bp) activate_breakpoints ();
run_child ();
if (bp) deactivate_breakpoints ();
}
/* ---------------------------------------------------------------------- */
static void
step (int kind)
{
int i, b, len, terminated, int03hit, refmem;
char *inst;
switch (kind)
{
case 0:
inst = unassemble_proper (a_tss.tss_eip, &len);
if (strcmp (inst, "popf") == 0 || strcmp (inst, "pushf") == 0)
kind = 1; /* Push the right value of eflags (no trace flag). */
break;
case 1:
if (first_step)
kind = 3;
else
{
inst = unassemble_proper (a_tss.tss_eip, &len);
if (strncmp (inst, "loop", 4)
&& strncmp (inst, "call", 4)
&& strncmp (inst, "int", 3) )
kind = 0;
break;
}
}
switch (kind)
{
case 0: /* "Trace" */
a_tss.tss_eflags |= 0x0100;
edi.dr[7] = 0;
refmem = strchr (inst, '[') != 0;
if (refmem)
/* Assume that all access to code and stack segments are safe.
This should hold unless you do something extra-ordinary dirty. */
if (((a_tss.tss_ds == a_tss.tss_cs) || (a_tss.tss_ds == a_tss.tss_ss))
&&
((a_tss.tss_es == a_tss.tss_cs) || (a_tss.tss_es == a_tss.tss_ss))
&&
((a_tss.tss_fs == a_tss.tss_cs) || (a_tss.tss_fs == a_tss.tss_ss)
|| (strstr (inst, "fs:") == 0))
&&
((a_tss.tss_gs == a_tss.tss_cs) || (a_tss.tss_gs == a_tss.tss_ss)
|| (strstr (inst, "gs:") == 0)))
refmem = 0;
if (refmem)
user_screen ();
go (0);
a_tss.tss_eflags &= ~0x0100;
break;
case 1: /* "Step Over" */
user_screen ();
b = set_breakpoint (BP_Code, 0, a_tss.tss_eip + len);
go (1);
reset_breakpoint (b);
break;
case 2: /* "Run" */
user_screen ();
go (1);
break;
case 3: /* "Run to `_main'" */
b = set_breakpoint (BP_Code, 0, main_entry);
user_screen ();
go (1);
reset_breakpoint (b);
}
first_step = 0;
i = a_tss.tss_irqn;
terminated = (i == 0x21) && (a_tss.tss_eax & 0xff00) == 0x4c00;
int03hit = (i == 0x03) && get_breakpoint (BP_Code, 0, a_tss.tss_eip) != -1;
if (terminated)
a_tss.tss_eip -= 2; /* point back to Int 21h */
else if (int03hit)
a_tss.tss_eip--; /* point back to Int 3 */
code_pane_goto (a_tss.tss_eip);
debug_screen ();
redraw (0);
if (terminated)
message (CL_Msg, "Program terminated normally, exit code is %d",
(word8)a_tss.tss_eax);
else if (i == 1 || int03hit)
{
int b, no;
no = -1;
for (b = 0; no == -1 && b <= 3; b++)
if ((edi.dr[6] & (1 << b)) && (edi.dr[7] & (3 << (b * 2))))
no = b;
if (no != -1)
no = get_breakpoint ((edi.dr[7] >> (16 + 4 * no)) & 3,
((edi.dr[7] >> (18 + 4 * no)) & 3) + 1,
edi.dr[no] - edi.app_base);
else if (int03hit)
no = get_breakpoint (BP_Code, 0, a_tss.tss_eip);
if (no != -1)
switch (breakpoint_table[no].type)
{
case BP_Code:
message (CL_Info, "Code breakpoint hit");
break;
case BP_Write:
message (CL_Msg, "Data write breakpoint at 0x%08lx triggered by previous instruction",
breakpoint_table[no].addr);
break;
case BP_Read:
message (CL_Msg, "Data read/write breakpoint at 0x%08lx triggered by previous instruction",
breakpoint_table[no].addr);
break;
}
}
else if (i == 0x79)
message (CL_Info, "Keyboard interrupt");
else if (i == 0x75)
{
char *reason;
save_npx ();
if ((npx.status & 0x0241) == 0x0241)
reason = "stack overflow";
else if ((npx.status & 0x0241) == 0x0041)
reason = "stack underflow";
else if (npx.status & 1)
reason = "invalid operation";
else if (npx.status & 2)
reason = "denormal operand";
else if (npx.status & 4)
reason = "divide by zero";
else if (npx.status & 8)
reason = "overflow";
else if (npx.status & 16)
reason = "underflow";
else if (npx.status & 32)
reason = "loss of precision";
else
reason = "?";
message (CL_Error,
"Numeric Exception (%s) at eip=0x%08lx", reason, npx.eip);
load_npx ();
}
else if (i == 8 || (i >= 10 && i <= 14))
message (CL_Error, "Exception %d (0x%02x) occurred, error code=%#lx",
i, i, a_tss.tss_error);
else
message (CL_Error, "Exception %d (0x%02x) occurred", i, i);
}
/* ---------------------------------------------------------------------- */
static void
redraw (int first)
{
char *buf = alloca (cols);
debug_screen ();
screen_attr = ScreenAttrib = screen_attr_normal;
if (first)
{
int y = toplines + 1, x1 = cols - 18, x2 = cols - 5, x3 = 46;
frame (0, 0, cols - 1, rows - 1);
frame (x2, 0, cols - 1, y);
frame (x1, 0, x2, y);
frame (x3, y, cols - 1, rows - 1);
frame (0, y, x3, rows - 1);
put (x2, 0, "┬");
put (x2, y, "┴");
put (0, y, "├");
put (cols - 1, y, "┤");
put (x1, 0, "┬");
put (x1, y, "┴");
put (x3, y, "┬");
put (x3, rows - 1, "┴");
}
{
/* Show register status */
int reg, x = cols - 17, y = 1, width = 12;
for (reg = 0; regs_type[reg]; reg++)
{
if (regs_type[reg] == 's')
sprintf (buf, " %s %04x",
regs_names[reg],
*(unsigned short *)(regs_addr[reg]));
else
sprintf (buf, "%s %08lx",
regs_names[reg],
*(regs_addr[reg]));
putl (x, y++, width, buf);
}
while (y <= toplines)
putl (x, y++, width, "");
}
{
/* Show flags status */
int f, x = cols - 4, y = 1, width = 3;
static char flags[] = "c?p?a?zn?ido????";
for (f = 0; flags[f]; f++)
if (flags[f] != '?')
{
sprintf (buf, "%c=%d", flags[f], (int)((a_tss.tss_eflags >> f) & 1));
put (x, y++, buf);
}
while (y <= toplines)
putl (x, y++, width, "");
}
{
/* Show breakpoints */
int b, x = 47, y = toplines + 2, width = cols - 48;
char *name;
int32 delta;
for (b = breakpoint_origin;
b < breakpoint_origin + bottomlines / 2 && b < breakpoint_count;
b++)
{
switch (breakpoint_table[b].type)
{
case BP_Code:
sprintf (buf, "Execute: %08lx",
breakpoint_table[b].addr);
break;
case BP_Write:
sprintf (buf, "Data write: %08lx, %d",
breakpoint_table[b].addr, breakpoint_table[b].length);
break;
case BP_Read:
sprintf (buf, "Data read: %08lx, %d",
breakpoint_table[b].addr, breakpoint_table[b].length);
break;
}
putl (x, y++, width, buf);
name = syms_val2name (breakpoint_table[b].addr, &delta);
if (name[0] != '0')
if (delta && strlen (name) < width)
sprintf (buf, " %s+%#lx", name, delta);
else
sprintf (buf, " %-*s", width, name);
else
buf[0] = '\0';
putl (x, y++, width , buf);
}
if (breakpoint_count == 0)
putl (x, y++, width, "No breakpoints");
while (y < rows - 1)
putl (x, y++, width, "");
}
{
/* Show data dump */
word32 p = data_dump_origin;
int b, x, y = toplines + 2, ok, width = 45;
unsigned char data[8], xpos[8];
for (x = 0; x < 8; x++)
xpos[x] = 10
+ ((data_dump_size == 1) ? (x * 3) :
((data_dump_size == 2) ? ((x / 2) * 5 + (~x & 1) * 2) :
((x / 4) * 9 + (~x & 3) * 2)));
while (y < rows - 1)
{
if ((ok = valid_addr (p, 8)))
read_child (p, data, 8);
sprintf (buf, "%08lx: %35s", p, "");
for (x = 0; x < 8; x++)
if (ok || valid_addr (p + x, 1))
{
if (!ok) read_child (p + x, data + x, 1);
buf[xpos[x]] = hexchars[data[x] >> 4];
buf[xpos[x] + 1] = hexchars[data[x] & 0xf];
buf[37 + x] = (data[x] ? data[x] : '.');
}
else
buf[xpos[x]] = buf[xpos[x + 1]] = buf[37 + x] = '?';
putl (1, y, width, buf);
screen_attr = screen_attr_break;
for (x = 0; x < 8; x++)
for (b = 0; b < breakpoint_count; b++)
if (breakpoint_table[b].type != BP_Code
&& p + x >= breakpoint_table[b].addr
&& p + x < (breakpoint_table[b].addr
+ breakpoint_table[b].length))
{
highlight (xpos[x] + 1, y, 2);
highlight (38 + x, y, 1);
}
screen_attr = screen_attr_normal;
p += 8;
y++;
}
data_dump_last = p - 1;
}
if (code_pane_active)
{
/* Show code dump */
word32 p = code_dump_origin, width = cols - 19;
int y = 1, len, source = 0;
char *txt;
while (y <= toplines)
{
source = !source;
code_pane_pos[y - 1] = p;
if (source)
{
txt = unassemble_source (p);
if (txt)
{
screen_attr = screen_attr_source;
putl (1, y++, width, txt);
}
}
else
{
if (valid_instaddr (p))
txt = unassemble_proper (p, &len);
else
txt = "?", len = 1;
sprintf (buf, "%08lx%c%s",
p,
p == a_tss.tss_eip ? 16 : ' ',
txt);
screen_attr =
(get_breakpoint (BP_Code, 0, p) == -1
? screen_attr_normal : screen_attr_break);
putl (1, y++, width, buf);
p += len;
}
}
code_pane_pos[y - 1] = p + 1;
code_dump_last = p - 1;
screen_attr = screen_attr_normal;
}
if (npx_pane_active)
{
/* Show npx dump */
int i, y = 1, width = cols - 19;
static char *rtype[] = { "Near", "-Inf", "+Inf", "Zero" };
save_npx ();
sprintf (buf,
"Control: %04lx PR=%s UN=%s OV=%s ZD=%s DN=%s IV=%s Rnd=%s",
npx.control & 0xffff,
(npx.control & (1 << 5)) ? "N" : "Y",
(npx.control & (1 << 4)) ? "N" : "Y",
(npx.control & (1 << 3)) ? "N" : "Y",
(npx.control & (1 << 2)) ? "N" : "Y",
(npx.control & (1 << 1)) ? "N" : "Y",
(npx.control & (1 << 0)) ? "N" : "Y",
rtype[(npx.control >> 10) & 3]);
putl (1, y++, width, buf);
sprintf (buf,
"Status: %04lx PR=%s UN=%s OV=%s ZD=%s DN=%s IV=%s ST=%s",
npx.status & 0xffff,
(npx.status & (1 << 5)) ? "Y" : "N",
(npx.status & (1 << 4)) ? "Y" : "N",
(npx.status & (1 << 3)) ? "Y" : "N",
(npx.status & (1 << 2)) ? "Y" : "N",
(npx.status & (1 << 1)) ? "Y" : "N",
(npx.status & (1 << 0)) ? "Y" : "N",
(npx.status & (1 << 6)) ? "Y" : "N");
putl (1, y++, width, buf);
sprintf (buf, "%19sC3=%d C2=%d C1=%d C0=%d",
"",
(npx.status & (1 << 14)) != 0,
(npx.status & (1 << 10)) != 0,
(npx.status & (1 << 9)) != 0,
(npx.status & (1 << 8)) != 0);
putl (1, y++, width, buf);
for (i = 0; i < 8; i++)
{
/* We assume that `long double' is the same type as npx.reg[i].
It would be sensible to check that the sizes match, but they
don't! For alignment reasons, `sizeof (long double)' is 12. */
long double d;
int tag;
int tos = (npx.status >> 11) & 7;
int exp = (int)npx.reg[i].exponent - 16382;
char *dstr = alloca (30);
dstr[0] = (npx.reg[i].sign) ? '-' : '+';
dstr[1] = '\0';
tag = (npx.tag >> (((i + tos) & 7) * 2)) & 3;
switch (tag)
{
case 0:
if (abs (exp) < 1000)
{
d = *((long double*)(npx.reg + i));
/* sprintf does not (djgpp 1.11m3) handle long doubles. */
sprintf(dstr,"%+.16g", (double) d);
}
else
sprintf (dstr, "Valid, %s, and %s",
npx.reg[i].sign ? "negative" : "positive",
exp > 0 ? "huge" : "tiny");
break;
case 1:
strcat (dstr, "Zero");
break;
case 2:
if (npx.reg[i].exponent == 0x7fff)
if (npx.reg[i].sig3 == 0x8000
&& npx.reg[i].sig2 == 0x0000
&& npx.reg[i].sig1 == 0x0000
&& npx.reg[i].sig0 == 0x0000)
strcat (dstr, "Inf");
else
strcat (dstr, "NaN");
else
dstr = "Special";
break;
case 3:
dstr = "Empty";
break;
}
sprintf (buf, "st(%d): %d %04x %04x%04x%04x%04x %s",
i,
npx.reg[i].sign, npx.reg[i].exponent,
npx.reg[i].sig3, npx.reg[i].sig2,
npx.reg[i].sig1, npx.reg[i].sig0,
dstr);
putl (1, y++, width, buf);
}
while (y <= toplines)
putl (1, y++, width, "");
load_npx ();
}
if (stack_pane_active)
{
/* Show stack dump */
int line, y = 1, no = -stack_dump_origin, width = cols - 19;
unsigned char eipcode[4];
word32 delta, v = a_tss.tss_ebp;
word32 vaddr = a_tss.tss_eip;
stack_dump_more = 0;
while (valid_instaddr (vaddr))
{
if (no++ >= 0)
if (y > toplines)
{
stack_dump_more = 1;
break;
}
else
{
char *symaddr, *sourceaddr, *sourcefile;
int len;
symaddr = syms_val2name (vaddr, &delta);
sourcefile = syms_val2line (vaddr, &line, 0);
if (sourcefile)
sprintf (sourceaddr = alloca (cols + strlen (sourcefile)),
", line %d in file %s", line, sourcefile);
else if (delta)
sprintf (sourceaddr = alloca (cols), "%+ld", (long) delta);
else
sourceaddr = "";
len = 15 + strlen (symaddr) + strlen (sourceaddr);
if (len >= cols)
buf = alloca (len);
sprintf (buf, "%08lx: %s%s", vaddr, symaddr, sourceaddr);
stack_dump_pos[stack_dump_last = y - 1] = vaddr;
putl (1, y++, width, buf);
}
read_child (vaddr, eipcode, 3);
/* We look directly at the bit pattern instead of using disassembly;
the bit patterns are those generated by gcc. In general we
cannot expect to be able to follow a non-gcc stack anyway. */
if ((eipcode[0] == 0x55 && eipcode[1] == 0x89 && eipcode[2] == 0xe5)
|| eipcode[0] == 0xc3)
{
/* In this case where we are looking at `Push Ebp//Mov Ebp,Esp'
or `Ret', only the return address is on the stack; I believe
this only to happen in the innermost activation record. */
if (valid_addr (a_tss.tss_esp, 4))
read_child (a_tss.tss_esp, &vaddr, 4);
else
break;
}
else
{
if (eipcode[0] == 0x89 && eipcode[1] == 0xe5)
/* When looking at `Mov Esp,Ebp' the next stack frame is on
the stack and pointed to by the stack pointer. We are
actually in the same situation after `Mov Esp,Ebp' which
is generated by gcc with the -m486 option. This case is,
however, handled perfectly by using the base pointer. */
v = a_tss.tss_esp;
if (v >= a_tss.tss_esp && v < 0x80000000L && valid_addr (v, 8))
{
read_child (v + 4, &vaddr, 4);
read_child (v, &v, 4);
}
else
break;
}
}
while (y <= toplines)
putl (1, y++, width, "");
}
if (info_pane_active)
{
/* Show info */
int y = 1, width = cols - 19;
long ul;
char *s;
_go32_dpmi_meminfo info;
_go32_dpmi_registers regs;
sprintf (buf, "Debugger version ............: %.2f %s",
FULLSCR_VERSION,
FULLSCR_VERSION < 1.00 ? "beta" : "");
putl (1, y++, width, buf);
_go32_dpmi_get_free_memory_information (&info);
switch (_go32_info_block.run_mode)
{
case _GO32_RUN_MODE_RAW:
s = "Raw";
break;
case _GO32_RUN_MODE_XMS:
s = "Xms";
break;
case _GO32_RUN_MODE_VCPI:
s = "Vcpi";
break;
case _GO32_RUN_MODE_DPMI:
s = alloca (20);
sprintf (s, "Dpmi %d.%02x",
_go32_info_block.run_mode_info >> 8,
_go32_info_block.run_mode_info & 0xff);
break;
default:
s = "Unknown";
}
sprintf (buf, "Running mode ................: %s", s);
putl (1, y++, width, buf);
putl (1, y++, width, "");
ul = info.total_physical_pages;
if (ul > 0)
{
ul <<= 12;
sprintf (buf, "Total physical memory .......: %lu KB", ul >> 10);
putl (1, y++, width, buf);
}
ul = info.available_physical_pages
? info.available_physical_pages << 12
: info.available_memory;
sprintf (buf, "Remaining physical memory ...: %lu KB", ul >> 10);
putl (1, y++, width, buf);
ul = info.available_memory;
sprintf (buf, "Remaining virtual memory ....: %lu KB", ul >> 10);
putl (1, y++, width, buf);
ul = info.free_linear_space << 12;
if (ul >= 0)
{
sprintf (buf, "Free linear space ...........: %ld KB", ul >> 10);
putl (1, y++, width, buf);
}
/* Remember that Dos memory is only made available of direct request;
using 0xffff does not count as a request. */
regs.h.ah = 0x48;
regs.x.bx = 0xffff;
regs.x.ss = regs.x.sp = 0;
_go32_dpmi_simulate_int (0x21, ®s);
ul = regs.x.bx << 4;
sprintf (buf, "Free dos memory .............: %ld %s",
ul > 8192 ? ul >> 10 : ul,
ul > 8192 ? "KB" : "Bytes");
putl (1, y++, width, buf);
putl (1, y++, width, "");
sprintf (buf, "Ctrl-C checking .............: %s",
getcbrk () ? "On" : "Off");
putl (1, y++, width, buf);
putl (1, y++, width, "");
sprintf (buf, "Program text ................: %08lx - %08lx",
areas[A_text].first_addr, areas[A_text].last_addr);
putl (1, y++, width, buf);
sprintf (buf, "Program data ................: %08lx - %08lx",
areas[A_data].first_addr, areas[A_data].last_addr);
putl (1, y++, width, buf);
sprintf (buf, "Program bss .................: %08lx - %08lx",
areas[A_bss].first_addr, areas[A_bss].last_addr);
putl (1, y++, width, buf);
sprintf (buf, "Program stack ...............: %08lx - %08lx",
areas[A_stack].first_addr, areas[A_stack].last_addr);
putl (1, y++, width, buf);
while (y <= toplines)
putl (1, y++, width, "");
}
if (whereis_pane_active)
{
/* Show result of last whereis command */
int y = 1, width = cols - 19, i = whereis_origin;
while (y <= toplines)
if (i < whereis_text_count)
putl (1, y++, width, whereis_pane_text[i++]);
else
putl (1, y++, width, "");
}
{
/* Highlight focus */
screen_attr = screen_attr_focus;
switch (pane)
{
case 6: /* Stack */
if (pane_pos > stack_dump_last)
pane_pos = stack_dump_last;
/* Fall through */
case 0: /* Code */
case 5: /* NPX */
highlight (1, pane_pos + 1, cols - 19);
break;
case 1: /* Registers */
highlight (cols - 17, pane_pos + 1, 12);
break;
case 2: /* Flags */
highlight (cols - 4, pane_pos + 1, 3);
break;
case 3: /* Breakpoints */
if (breakpoint_origin >= breakpoint_count)
breakpoint_origin = breakpoint_count ? breakpoint_count - 1 : 0;
if (breakpoint_origin + pane_pos >= breakpoint_count)
pane_pos = breakpoint_count - breakpoint_origin;
highlight (47, toplines + 2 + 2 * pane_pos, cols - 48);
highlight (47, toplines + 3 + 2 * pane_pos, cols - 48);
break;
case 4: /* Data */
{
int x = pane_pos & 7, y = pane_pos >> 3;
highlight (38 + x, toplines + 2 + y, data_dump_size);
switch (data_dump_size)
{
case 1:
highlight (11 + 3 * x, toplines + 2 + y, 2);
break;
case 2:
highlight (11 + 5 * (x >> 1), toplines + 2 + y, 4);
break;
case 4:
highlight (11 + 9 * (x >> 2), toplines + 2 + y, 8);
break;
}
}
break;
case 8: /* Whereis */
if (whereis_text_count)
highlight (1, pane_pos + 1, cols - 19);
break;
}
}
put_screen (debug_screen_save);
}
/* ---------------------------------------------------------------------- */
static void
initialize ()
{
int i;
debug_screen_p = 0;
cols = ScreenCols ();
rows = ScreenRows ();
if (cols < 80 || rows < 25)
{
fprintf (stderr, "\nDebugger error:\n\
There are only %d columns and %d rows\n\
in this display mode.\n\
The debugger needs at least\n\
80 columns and 25 rows.\n",
cols, rows);
exit (1);
}
toplines = (rows / 2) + 4;
bottomlines = rows - 3 - toplines;
read_buffer = malloc (cols + 10);
code_pane_pos = malloc ((toplines + 2) * sizeof (word32));
stack_dump_pos = malloc ((toplines + 2) * sizeof (word32));
debug_screen_save = get_screen ();
debug_screen_save[1] = debug_screen_save[2] = 0; /* Patch cursor pos. */
pane = 0;
pane_pos = 0;
for (i = 0; i < PANECOUNT; i++) pane_positions[i] = 0;
data_dump_origin = areas[A_data].first_addr;
data_dump_size = 1;
code_dump_origin = a_tss.tss_eip;
stack_dump_origin = 0;
breakpoint_origin = 0;
breakpoint_count = 0;
breakpoint_table = malloc (breakpoint_count * sizeof (BP_ENTRY));
whereis_origin = 0;
whereis_text_count = 0;
whereis_pane_text = malloc (whereis_text_count * sizeof (char *));
code_pane_active = 1;
npx_pane_active = stack_pane_active = info_pane_active
= whereis_pane_active = 0;
switch (ScreenMode ())
{
case 2:
case 7:
/* Mono */
screen_attr_normal = (A_black << 4) + A_grey;
screen_attr_source = screen_attr_normal;
screen_attr_focus = (A_grey << 4) + A_black;
screen_attr_break = (A_black << 4) + A_white;
screen_attr_message = (A_grey << 4) + A_white;
screen_attr_error = (A_grey << 4) + A_white;
break;
default:
/* Colour */
screen_attr_normal = (A_cyan << 4) + A_blue;
screen_attr_source = (A_cyan << 4) + A_black;
screen_attr_focus = (A_blue << 4) + A_white;
screen_attr_break = (A_red << 4) + A_white;
screen_attr_message = (A_green << 4) + A_white;
screen_attr_error = (A_red << 4) + A_white;
}
redraw (1);
message (CL_Info, "Sally Full Screen Debugger");
}
/* ---------------------------------------------------------------------- */
static void
code_pane_command (int key)
{
int b;
switch (key)
{
case K_Up:
case K_EUp:
if (pane_pos > 0)
pane_pos--;
else
code_pane_goto (code_skip (code_dump_origin, -1));
break;
case K_Down:
case K_EDown:
if (pane_pos < toplines - 1)
pane_pos++;
else
code_dump_origin =
code_pane_pos[0] == code_pane_pos[1]
? code_pane_pos[2] : code_pane_pos[1];
break;
case K_PageUp:
case K_EPageUp:
code_dump_origin = code_skip (code_dump_origin, -toplines);
break;
case K_PageDown:
case K_EPageDown:
code_dump_origin = code_dump_last + 1;
break;
case K_Control_Left:
case K_Control_ELeft:
code_dump_origin--;
break;
case K_Control_Right:
case K_Control_ERight:
code_dump_origin++;
break;
case K_F2:
b = get_breakpoint (BP_Code, 0, code_pane_pos[pane_pos]);
if (b != -1)
reset_breakpoint (b);
else
set_breakpoint (BP_Code, 0, code_pane_pos[pane_pos]);
break;
case K_F4:
b = set_breakpoint (BP_Code, 0, code_pane_pos[pane_pos]);
step (2);
reset_breakpoint (b);
break;
case K_Control_O:
code_pane_goto (a_tss.tss_eip);
break;
case K_Control_N:
a_tss.tss_eip = code_pane_pos[pane_pos];
break;
case K_Control_G:
{
int ok, res;
res = read_eval (&ok, "");
if (ok)
code_pane_goto (res);
break;
}
}
redraw (0);
}
/* ---------------------------------------------------------------------- */
static void
register_pane_command (int key)
{
switch (key)
{
case K_Left:
case K_ELeft:
case K_Up:
case K_EUp:
if (pane_pos > 0) pane_pos--;
break;
case K_Right:
case K_ERight:
case K_Down:
case K_EDown:
if (regs_type[pane_pos + 1]) pane_pos++;
break;
default:
if (key >= ' ' && key < 127)
{
int res, ok;
char s[2];
s[0] = key; s[1] = '\0';
res = read_eval (&ok, key == '=' ? "" : s);
if (ok)
switch (regs_type[pane_pos])
{
case 's':
*(unsigned short *)(regs_addr[pane_pos]) = res & 0xffff;
break;
case 'f':
a_tss.tss_eflags = (a_tss.tss_eflags & ~0xed5) | (res & 0xed5);
break;
default:
*regs_addr[pane_pos] = res;
}
}
}
redraw (0);
}
/* ---------------------------------------------------------------------- */
static void
flag_pane_command (int key)
{
static unsigned flagbits[] =
{ 0x0001, 0x0004, 0x0010, 0x0040, 0x0080, 0x0200, 0x0400, 0x0800 };
switch (key)
{
case K_Space:
case '+':
case '-':
case 't':
case 'T':
a_tss.tss_eflags ^= flagbits[pane_pos];
break;
case '1':
case 's':
case 'S':
a_tss.tss_eflags |= flagbits[pane_pos];
break;
case '0':
case 'r':
case 'R':
a_tss.tss_eflags &= ~flagbits[pane_pos];
break;
case K_Left:
case K_ELeft:
case K_Up:
case K_EUp:
if (pane_pos > 0) pane_pos--;
break;
case K_Right:
case K_ERight:
case K_Down:
case K_EDown:
if (pane_pos < 7) pane_pos++;
break;
}
redraw (0);
}
/* ---------------------------------------------------------------------- */
static void
breakpoint_pane_command (int key)
{
int b = breakpoint_count ? pane_pos + breakpoint_origin : -1;
int last = breakpoint_count && (b == breakpoint_count - 1);
switch (key)
{
case K_Delete:
case K_EDelete:
case K_Control_C:
if (b != -1)
reset_breakpoint (b);
if (!last)
break;
/* else fall through */
case K_Left:
case K_ELeft:
case K_Up:
case K_EUp:
if (pane_pos > 0)
pane_pos--;
else
if (breakpoint_origin > 0)
breakpoint_origin--;
break;
case K_Right:
case K_ERight:
case K_Down:
case K_EDown:
if (!last)
if (pane_pos < bottomlines / 2 - 1)
pane_pos++;
else
breakpoint_origin++;
break;
case K_Control_G:
case K_Return:
if (b != -1)
if (breakpoint_table[b].type == BP_Code)
code_pane_goto (breakpoint_table[b].addr);
break;
}
redraw (0);
}
/* ---------------------------------------------------------------------- */
static void
data_pane_command (int key)
{
word32 v = data_dump_origin + pane_pos;
BP_TYPE t;
int b;
switch (key)
{
case K_Up:
case K_EUp:
if (pane_pos >= 8)
pane_pos -= 8;
else
data_dump_origin -= 8;
break;
case K_Down:
case K_EDown:
if (v + 8 <= data_dump_last)
pane_pos += 8;
else
data_dump_origin += 8;
break;
case K_Left:
case K_ELeft:
if (pane_pos > 0)
pane_pos -= data_dump_size;
else
data_dump_origin -= 8, pane_pos = 8 - data_dump_size;
break;
case K_Right:
case K_ERight:
if (v + data_dump_size <= data_dump_last)
pane_pos += data_dump_size;
else
data_dump_origin += 8, pane_pos -= (8 - data_dump_size);
break;
case K_PageUp:
case K_EPageUp:
data_dump_origin -= (data_dump_last + 1 - data_dump_origin);
break;
case K_PageDown:
case K_EPageDown:
data_dump_origin = data_dump_last + 1;
break;
case K_Control_Left:
case K_Control_ELeft:
data_dump_origin--;
break;
case K_Control_Right:
case K_Control_ERight:
data_dump_origin++;
break;
case K_Control_S:
data_dump_origin = a_tss.tss_esp;
pane_pos = 0;
break;
case K_Control_C:
data_dump_origin = a_tss.tss_eip;
data_dump_size = 1;
pane_pos = 0;
break;
case K_Control_B:
data_dump_size = 1;
break;
case K_Control_W:
data_dump_size = 2;
break;
case K_Control_L:
data_dump_size = 4;
break;
case K_Alt_F2:
case K_F2:
t = key == K_F2 ? BP_Write : BP_Read;
b = get_breakpoint (t, data_dump_size, v);
if (b == -1)
set_breakpoint (t, data_dump_size, v);
else
reset_breakpoint (b);
break;
case K_Control_G:
{
int ok, res;
res = read_eval (&ok, "");
if (ok)
{
data_dump_origin = res;
pane_pos = 0;
}
break;
}
default:
if (key >= ' ' && key < 127)
{
int res, ok, bad = 0;
char s[2], *p, *p0, q;
s[0] = key; s[1] = '\0';
if (!read_string (key == '=' ? "" : s))
{
p = read_buffer;
do
{
while (*p == ' ') p++;
switch (*p)
{
case '\0':
break;
case '\'':
case '"':
if (data_dump_size != 1)
{
*p = 0;
message (CL_Error,
"Strings must be entered in byte mode");
break;
}
q = *p++;
p0 = p;
while (*p != q && *p) p++;
if (*p)
{
while (p0 != p)
{
if (valid_addr (v, 1))
write_child (v, p0, 1);
else
bad = 1;
p0++, v++;
}
if (q == '"')
{
if (valid_addr (v, 1))
write_child (v, s + 1, 1);
else
bad = 1;
v++;
}
p++;
}
else
message (CL_Error, "String constant not terminated");
break;
default:
p0 = p;
while (*p != ',' && *p) p++;
q = *p;
*p = 0;
res = eval (p0, &ok);
if (ok)
{
*p = q;
if (valid_addr (v, data_dump_size))
write_child (v, &res, data_dump_size);
else
bad = 1;
v += data_dump_size;
}
}
if (*p == ',') p++;
}
while (*p);
if (bad)
message (CL_Error, "Part of the data could not be written");
}
}
}
pane_pos &= ~(data_dump_size - 1);
redraw (0);
}
/* ---------------------------------------------------------------------- */
static void
npx_pane_command (int key)
{
int reg = pane_pos - 3, rotreg, tag;
int regp = (reg >= 0);
save_npx ();
if (regp)
{
rotreg = (reg + (npx.status >> 11)) & 7;
tag = (npx.tag >> (rotreg * 2)) & 3;
}
switch (key)
{
case K_Left:
case K_ELeft:
case K_Up:
case K_EUp:
if (pane_pos > 0) pane_pos--;
break;
case K_Right:
case K_ERight:
case K_Down:
case K_EDown:
if (pane_pos < 10) pane_pos++;
break;
case K_Control_C:
case K_Control_E:
case K_Delete:
case K_EDelete:
if (regp)
{
tag = 3;
memset (&npx.reg[reg], 0, sizeof (NPXREG));
}
break;
case K_Control_Z:
if (regp)
{
tag = 1;
memset (&npx.reg[reg], 0, sizeof (NPXREG));
}
break;
case K_Control_N:
if (regp)
npx.reg[reg].sign = !npx.reg[reg].sign;
break;
default:
if (regp && key >= ' ' && key < 127)
{
char s[2], *endp, *p;
double d;
s[0] = key; s[1] = '\0';
if (!read_string (key == '=' ? "" : s) && read_buffer[0] != '\0')
{
p = read_buffer;
while (*p == ' ')
p++;
if (*p == '\0')
break;
strlwr (p);
if (strcmp (p, "+inf") == 0
|| strcmp (p, "inf") == 0
|| strcmp (p, "-inf") == 0)
{
tag = 2;
npx.reg[reg].exponent = 0x7fff;
npx.reg[reg].sig3 = 0x8000;
npx.reg[reg].sig2
= npx.reg[reg].sig1
= npx.reg[reg].sig0 = 0;
npx.reg[reg].sign = (*p == '-');
}
else
{
d = strtod (p, &endp);
if (*p != '\0' && *endp)
message (CL_Error, "Expression not understood");
else
{
tag = (d == 0.0);
*((long double *)(npx.reg + reg)) = (long double) d;
npx.reg[reg].sign = (*p == '-'); /* for -Zero */
}
}
}
}
}
if (regp)
npx.tag = (npx.tag & ~(3 << (rotreg * 2))) | (tag << (rotreg * 2));
load_npx ();
redraw (0);
}
/* ---------------------------------------------------------------------- */
static void
stack_pane_command (int key)
{
switch (key)
{
case K_Left:
case K_ELeft:
case K_Up:
case K_EUp:
if (pane_pos > 0)
pane_pos--;
else
if (stack_dump_origin)
stack_dump_origin--;
break;
case K_Right:
case K_ERight:
case K_Down:
case K_EDown:
if (pane_pos < stack_dump_last)
pane_pos++;
else
if (stack_dump_more)
stack_dump_origin++;
break;
case K_Return:
code_pane_goto (stack_dump_pos[pane_pos]);
stack_pane_active = 0;
code_pane_active = 1;
pane = 0;
break;
}
redraw (0);
}
/* ---------------------------------------------------------------------- */
static void
info_pane_command (int key)
{
/* No keys recognized. */
redraw (0);
}
/* ---------------------------------------------------------------------- */
static void
fullscr_listwild_handler (word32 addr, char typ, char *name,
char *filename, int linenum)
{
char *s;
s = alloca (cols + strlen (name) + (filename ? strlen (filename) : 0));
sprintf (s, "0x%08lx %c %s", addr, typ, name);
if (filename)
sprintf (s + strlen (s), ", line %d of %s", linenum, filename);
whereis_pane_text = realloc (whereis_pane_text,
(++whereis_text_count) * sizeof (char *));
whereis_pane_text[whereis_text_count - 1] = strdup (s);
}
static void
whereis_pane_command (int key)
{
int no = whereis_origin + pane_pos;
switch (key)
{
case K_Left:
case K_ELeft:
case K_Up:
case K_EUp:
if (pane_pos > 0)
pane_pos--;
else
if (whereis_origin)
whereis_origin--;
break;
case K_Right:
case K_ERight:
case K_Down:
case K_EDown:
if (no < whereis_text_count - 1)
if (pane_pos < toplines - 1)
pane_pos++;
else
whereis_origin++;
break;
case K_Home:
case K_EHome:
pane_pos = whereis_origin = 0;
break;
case K_End:
case K_EEnd:
if (whereis_text_count > toplines)
{
whereis_origin = whereis_text_count - toplines;
pane_pos = toplines - 1;
}
else
{
whereis_origin = 0;
pane_pos = whereis_text_count ? whereis_text_count - 1 : 0;
}
break;
case K_PageDown:
case K_EPageDown:
if (whereis_origin + toplines < whereis_text_count)
{
whereis_origin += toplines;
if (whereis_origin + pane_pos >= whereis_text_count)
pane_pos = whereis_text_count - 1 - whereis_origin;
}
break;
case K_PageUp:
case K_EPageUp:
if (whereis_origin > toplines)
whereis_origin -= toplines;
else
whereis_origin = pane_pos = 0;
break;
case K_Return:
if (whereis_text_count)
{
char *endp, typ;
unsigned long ul;
typ = whereis_pane_text[no][11];
ul = strtoul (whereis_pane_text[no], &endp, 16);
switch (toupper (typ))
{
case 'T':
code_pane_goto (ul);
whereis_pane_active = 0;
code_pane_active = 1;
pane = 0;
break;
case 'B':
case 'D':
data_dump_origin = ul;
pane_positions[4] = 0;
break;
}
}
break;
default:
if (key >= ' ' && key < 127)
{
char s[2], *p;
s[0] = key; s[1] = '\0';
if (!read_string (key == '=' ? "" : s) && read_buffer[0] != '\0')
{
while (whereis_text_count > 0)
free (whereis_pane_text[--whereis_text_count]);
whereis_pane_text = realloc (whereis_pane_text, 0);
p = read_buffer;
while (*p == ' ') p++;
syms_listwild (p, &fullscr_listwild_handler);
pane_pos = whereis_origin = 0;
if (whereis_text_count >= 10)
message (CL_Info, "There were %d symbols that matched",
whereis_text_count);
}
}
}
redraw (0);
}
/* ---------------------------------------------------------------------- */
void
debugger(void)
{
int oldpane;
static void (*keyhandlers[PANECOUNT])(int) =
{
&code_pane_command, /* 0 */
®ister_pane_command, /* 1 */
&flag_pane_command, /* 2 */
&breakpoint_pane_command, /* 3 */
&data_pane_command, /* 4 */
&npx_pane_command, /* 5 */
&stack_pane_command, /* 6 */
&info_pane_command, /* 7 */
&whereis_pane_command /* 8 */
};
main_entry = syms_name2val ("_main");
first_step = !undefined_symbol;
initialize ();
can_longjmp = 1;
setjmp (debugger_jmpbuf);
oldpane = -1;
while (1)
{
int key;
if (pane < 1 || pane > 4)
pane = code_pane_active ? 0
: (npx_pane_active ? 5
: (stack_pane_active ? 6
: (info_pane_active ? 7
: (whereis_pane_active ? 8
: (abort (), -1)))));
if (pane == oldpane)
pane_positions[pane] = pane_pos;
else
{
pane_pos = pane_positions[pane];
oldpane = pane;
redraw (0);
}
key = getxkey ();
switch (key)
{
case K_Tab:
if (pane < 1 || pane > 4)
pane = 1;
else
pane++;
break;
case K_BackTab:
if (pane < 1 || pane > 4)
pane = 4;
else
pane--;
break;
case K_Alt_C:
case K_Alt_I:
case K_Alt_N:
case K_Alt_S:
case K_Alt_W:
code_pane_active = (key == K_Alt_C);
info_pane_active = (key == K_Alt_I);
npx_pane_active = (key == K_Alt_N);
stack_pane_active = (key == K_Alt_S);
whereis_pane_active = (key == K_Alt_W);
oldpane = -1; /* Force redraw. */
break;
case K_Alt_X:
user_screen ();
can_longjmp = 0;
return;
case K_Alt_F5:
user_screen ();
(void) getxkey ();
debug_screen ();
break;
case K_Alt_E:
{
int res, ok;
res = read_eval (&ok, "");
if (ok)
message (CL_Msg, "\"%s\" -> %d (0x%08lx)",
read_buffer, res, (word32) res);
break;
}
case K_F7:
step (0);
break;
case K_F8:
step (1);
break;
case K_F9:
step (2);
break;
default:
keyhandlers[pane] (key);
}
}
}
/* ----------------------------------------------------------------------
It's a mystery to me --- the game commences
for the usual fee --- plus expenses
confidential information --- it's in a diary
this is my investigation --- it's not a public inquiry
-- Mark Knopfler, "Private Investigations"
---------------------------------------------------------------------- */