3598 lines
118 KiB
C++
3598 lines
118 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// BOCHS ENHANCED DEBUGGER Ver 1.2
|
|
// (C) Chourdakis Michael, 2008
|
|
// http://www.turboirc.com
|
|
//
|
|
// Modified by Bruce Ewing
|
|
//
|
|
// Copyright (C) 2008-2017 The Bochs Project
|
|
|
|
#include "config.h"
|
|
|
|
#if BX_DEBUGGER && BX_DEBUGGER_GUI
|
|
|
|
#include <math.h>
|
|
|
|
#include "bochs.h"
|
|
#include "cpu/cpu.h"
|
|
#include "disasm/disasm.h"
|
|
|
|
#include "enh_dbg.h"
|
|
|
|
// Match stuff
|
|
#define MATCH_TRUE 1
|
|
#define MATCH_FALSE 0
|
|
#define MATCH_ABORT -1
|
|
|
|
#define NEGATE_CLASS
|
|
#define OPTIMIZE_JUST_STAR
|
|
|
|
// get a "class" to access the disassebler
|
|
// Note; any instance has access to all the member functions -- that is enough!
|
|
// -- i.e. No further initialization necessary.
|
|
static disassembler bx_disassemble;
|
|
|
|
const char* DC0txt[2] = {"P.Address","L.Address"}; // DumpMode definitions in text
|
|
|
|
const char* BTxt[6] = {
|
|
"Continue [c]",
|
|
"Step [s]",
|
|
"Step N [s ###]",
|
|
"Refresh",
|
|
"Break [^C]",
|
|
"Break All"};
|
|
|
|
int BtnLkup[6] = {
|
|
CMD_CONT, CMD_STEP1, CMD_STEPN, CMD_RFRSH, CMD_BREAK
|
|
};
|
|
|
|
#ifdef WIN32
|
|
int useCR = 1; // Win32 needs CRLF pairs for an EOL
|
|
bx_bool NeedSysRresize = TRUE; // use Sys Reg to help autosize Reg "hex" column
|
|
#else
|
|
int useCR = 0;
|
|
bx_bool NeedSysRresize = FALSE; // use Sys Reg to help autosize Reg "hex" column
|
|
#endif
|
|
|
|
bx_bool SeeReg[8] = {
|
|
TRUE, // in 64bit mode, show 32bit versions of registers also (EAX, ...)
|
|
TRUE, // show segment registers (CS, ...)
|
|
TRUE, // show GDTR, IDTR, LDTR, Task Reg
|
|
TRUE, // show control register (CR0, ...)
|
|
FALSE, // show FPU (STi) / MMX registers
|
|
FALSE, // show XMM registers
|
|
FALSE, // show the Debug Registers (DR0, ...)
|
|
FALSE // Test Registers not yet supported in bochs
|
|
};
|
|
|
|
bx_bool SingleCPU = FALSE; // Display all SMP CPUs
|
|
bx_bool ShowIOWindows = TRUE; // Display the Input and Output Internal Debugger windows
|
|
bx_bool ShowButtons = TRUE; // Display the top-row Step/Continue pushbuttons
|
|
bx_bool SeeRegColors = TRUE; // Display registers with background color "groups"
|
|
bx_bool ignoreNxtT = TRUE; // Do not show "Next at t=" output lines
|
|
bx_bool ignSSDisasm = TRUE; // Do not show extra disassembly line at each break
|
|
int UprCase = 0; // 1 = convert all Asm, Register names, Register values to uppercase
|
|
int DumpInAsciiMode = 3; // bit 1 = show ASCII in dumps, bit 2 = show hex, value=0 is illegal
|
|
int DumpWSIndex = 0; // word size index for memory dump
|
|
bx_bool LogView = 0; // Send log to output window
|
|
|
|
bx_bool isLittleEndian = TRUE;
|
|
int DefaultAsmLines = 512; // default # of asm lines disassembled and "cached"
|
|
int bottommargin = 6; // ASM autoscroller tries to leave this many lines below
|
|
int topmargin = 3; // autoscroller tries to leave this many lines above
|
|
// Note: topmargin must be less than bottommargin
|
|
|
|
// How to use DockOrder: the Register list is window 1, ASM is window 2, MemDump is window 3
|
|
// Create the hex value with the digits in the order you want the windows to be.
|
|
// 0x312 would have MemDump on the left, Register in the middle, ASM on the right
|
|
short DockOrder = 0x123; // set the default List "docking" (Reg, ASM, Dump)
|
|
|
|
bx_bool SA_valid = FALSE;
|
|
Bit64u SelectedDataAddress = 0;
|
|
Bit64u CurrentAsmLA = 0; // = EIP/RIP -- for highlighting in ASM window
|
|
Bit64u BottomAsmLA; // beginning and end addrs on ASM window
|
|
Bit64u TopAsmLA;
|
|
|
|
Bit32u PrevStepNSize = 50; // cpu_loop control variables
|
|
unsigned TotCPUs; // # of CPUs in a multi-CPU simulation
|
|
unsigned CpuSupportSSE = 0; // cpu supports SSE
|
|
unsigned CurrentCPU; // cpu that is being displayed
|
|
|
|
struct ASKTEXT ask_str;
|
|
|
|
// window resizing/docking stuff
|
|
int OneCharWide; // average width of a char in current font (pixels)
|
|
int Sizing = 0; // current "resizing/docking mode"
|
|
int Resize_HiX; // horizontal limits of the current resize operation (pixels)
|
|
int Resize_LoX;
|
|
unsigned ListWidthPix[3] = {5,7,8}; // set initial proportions of Reg, Asm, Dump windows
|
|
int CurCenterList = 0;
|
|
bx_bool DumpHasFocus = FALSE;
|
|
// BarClix holds the x-axis position (in pixels or logical units) of the two resizing bars,
|
|
// in parent coordinates (ie. any window that contains the lists)
|
|
unsigned short BarClix[2];
|
|
|
|
bx_bool AtBreak = FALSE; // Status indicators
|
|
bx_bool CpuModeChange;
|
|
bx_bool StatusChange = TRUE;
|
|
|
|
bx_bool In64Mode = FALSE; // CPU modes
|
|
bx_bool In32Mode = FALSE;
|
|
unsigned CpuMode = 0;
|
|
Bit32u InPaging = 0; // Storage for the top bit of CR0, unmodified
|
|
|
|
bx_bool doOneTimeInit = TRUE; // Internal flag #1
|
|
bx_bool doSimuInit; // Internal flag #2
|
|
bx_bool ResizeColmns; // address/value column autosize flag
|
|
bx_bool FWflag = FALSE; // friendly warning has been shown to user once already
|
|
|
|
static char *PrevStack; // buffer for testing changes in stack values
|
|
Bit64u PStackLA = 0; // to calculate alignment between prev and current stack
|
|
bx_bool StackEntChg[STACK_ENTRIES]; // flag for "change detected" on each stack line
|
|
|
|
// only pay special attention to registers up to EFER
|
|
static const char* RegLCName[EFER_Rnum + 1] = {
|
|
"rax","rbx","rcx","rdx","rsi","rdi","rbp","rsp","rip",
|
|
"r8","r9","r10","r11","r12","r13","r14","r15",
|
|
"eflags","eax","ebx","ecx","edx","esi","edi","ebp","esp","eip",
|
|
"cs","ds","es","ss","fs","gs",
|
|
"gdtr","idtr","ldtr","tr","cr0","cr2","cr3","cr4","efer"
|
|
};
|
|
static char* RDispName[EFER_Rnum + 1];
|
|
static bx_param_num_c *RegObject[BX_MAX_SMP_THREADS_SUPPORTED][TOT_REG_NUM + EXTRA_REGS];
|
|
Bit64u rV[EFER_Rnum + 1]; // current values of registers
|
|
Bit64u PV[EFER_Rnum + 1]; // previous values of registers
|
|
Bit32s GDT_Len; // "limits" (= bytesize-1) for GDT and IDT
|
|
Bit32s IDT_Len;
|
|
Bit8u RegColor[TOT_REG_NUM]; // specifies foreground and background color of registers
|
|
// Text color is red if the upper bit is set. Background is set according to ColorList.
|
|
int RitemToRnum[TOT_REG_NUM]; // mapping from Reg List Item# to register number
|
|
|
|
Bit64u ladrmin = 0; // bochs linear addressing access variables
|
|
Bit64u ladrmax = 0;
|
|
Bit64u l_p_offset;
|
|
|
|
bx_bool DumpInitted = FALSE; // has the MemDump window ever been filled with data?
|
|
int DumpAlign = 1;
|
|
int PrevDAD; // saves "previous DumpAlign value" (forces column autosize)
|
|
char *DataDump;
|
|
Bit64u DumpStart = 0; // current emulated address (lin or phys) of DataDump
|
|
bx_bool doDumpRefresh;
|
|
int DViewMode = VIEW_MEMDUMP;
|
|
bx_bool LinearDump = TRUE; // FALSE = memdump uses physical addressing
|
|
|
|
char *tmpcb; // 512b is allocated in bigbuf
|
|
char *CurStack; // Stack workspace (400b usually)
|
|
char AsciiHex[512]; // Unsigned char to printable hex xlat table
|
|
static char UCtable[256];
|
|
|
|
char bigbuf[outbufSIZE]; // 40K preallocated storage for all char buffers (see DoAllInit)
|
|
char *DbgAppendPtr = bigbuf;
|
|
char *OutWindow; // buffer for the Output window
|
|
int OutWinCnt = OutWinSIZE; // available size of OutWindow buffer
|
|
int PO_Tdelay = 0; // delay before displaying partial output lines
|
|
|
|
int AsmLineCount = 1; // # of disassembled asm lines loaded
|
|
int AsmPgSize = 0;
|
|
int ListLineRatio; // number of vertical pixels in a ListView Item
|
|
int ListVerticalPix; // number of vertical pixels in each List
|
|
Bit64u AsmLA[MAX_ASM]; // linear address of each disassembled ASM line
|
|
|
|
// Command stuff
|
|
int CommandHistoryIdx = 0;
|
|
char *CmdHistory[CmdHistorySize]; // 64 command History storage (fixed 80b each)
|
|
int CmdHInsert = 0; // index of next history entry to store
|
|
|
|
int SizeList = 0;
|
|
Bit32s xClick = -1; // halfway through a mouseclick flag + location
|
|
Bit32s yClick = 0; // values are in Listview coordinates
|
|
|
|
static const char* GDTt2[8] = {
|
|
"16-bit code",
|
|
"64-bit code",
|
|
"32-bit code",
|
|
"16-bit data",
|
|
"64-bit data",
|
|
"32-bit data",
|
|
"Illegal",
|
|
"Unused"
|
|
};
|
|
|
|
static const char* GDTsT[] = { "","Available 16bit TSS","LDT","Busy 16bit TSS","16bit Call Gate",
|
|
"Task Gate","16bit Interrupt Gate","16bit Trap Gate","Reserved",
|
|
"Available 32bit TSS","Reserved","Busy 32bit TSS","32bit Call Gate",
|
|
"Reserved","32bit Interrupt Gate","32bit Trap Gate"
|
|
};
|
|
|
|
// Register hex display formats -- index by UprCase
|
|
static const char* Fmt64b[2] = { FMT_ADDRX64, "%016llX" };
|
|
static const char* Fmt32b[2] = { "%08x", "%08X" };
|
|
static const char* Fmt16b[2] = { "%04x", "%04X" };
|
|
static const char* xDT64Fmt[2] = { FMT_ADDRX64 " (%4x)", "%016llX (%4X)" };
|
|
static const char* xDT32Fmt[2] = { "%08x (%4x)", "%08X (%4X)" };
|
|
|
|
static const char *BrkName[5] = {
|
|
"Linear Breakpt",
|
|
"Physical Breakpt",
|
|
"Virtual Breakpt",
|
|
"Write Watchpoint",
|
|
"Read Watchpoint",
|
|
};
|
|
|
|
bx_address BrkLAddr[BX_DBG_MAX_LIN_BPOINTS];
|
|
unsigned BrkIdx[BX_DBG_MAX_LIN_BPOINTS];
|
|
int BreakCount = 0;
|
|
|
|
// Breakpoint Dump Window stuff
|
|
unsigned short BrkpIDMap[256];
|
|
unsigned short WWP_BaseEntry;
|
|
unsigned short RWP_BaseEntry;
|
|
unsigned short EndLinEntry;
|
|
unsigned short EndPhyEntry;
|
|
unsigned short WWPSnapCount;
|
|
unsigned short RWPSnapCount;
|
|
bx_phy_address WWP_Snapshot[16];
|
|
bx_phy_address RWP_Snapshot[16];
|
|
|
|
char *debug_cmd;
|
|
bx_bool debug_cmd_ready;
|
|
bx_bool vgaw_refresh;
|
|
|
|
static bxevent_handler old_callback = NULL;
|
|
static void *old_callback_arg = NULL;
|
|
|
|
short nDock[36] = { // lookup table for alternate DockOrders
|
|
0x231, 0x312, 0x231, 0x213, 0x132, 0x132,
|
|
0x213, 0x321, 0x123, 0x123, 0x321, 0x312,
|
|
0x213, 0x213, 0x123, 0x312, 0x321, 0x312,
|
|
0x132, 0x123, 0x132, 0x321, 0x231, 0x231,
|
|
0x312, 0x312, 0x231, 0x213, 0x132, 0x213,
|
|
0x132, 0x123, 0x321, 0x321, 0x123, 0x231
|
|
};
|
|
|
|
void MakeXlatTables()
|
|
{
|
|
char *p, c;
|
|
int i = 256;
|
|
while (--i >= 0) // make an upper case translation table
|
|
UCtable[i]= toupper(i);
|
|
p = AsciiHex; // then also make a "hex" table
|
|
for ( i = 0; i < 256; i++)
|
|
{
|
|
c = i >> 4;
|
|
if (c > 9)
|
|
c += 'A' - 10; // do all hex in uppercase
|
|
else
|
|
c += '0';
|
|
*(p++)= c;
|
|
c = i & 0xf;
|
|
if (c > 9)
|
|
c += 'A' - 10;
|
|
else
|
|
c += '0';
|
|
*(p++)= c;
|
|
}
|
|
}
|
|
|
|
int DoMatch(const char *text, const char *p, bx_bool IsCaseSensitive)
|
|
{
|
|
// probably the MOST DIFFICULT FUNCTION in TurboIRC
|
|
// Thanks to BitchX for copying this function
|
|
//int last;
|
|
int matched;
|
|
//int reverse;
|
|
int pT = 0;
|
|
int pP = 0;
|
|
|
|
for (; p[pP] != '\0'; pP++, pT++)
|
|
{
|
|
if (text[pT] == '\0' && p[pP] != '*')
|
|
return MATCH_ABORT;
|
|
switch (p[pP])
|
|
{
|
|
// case '\\': // Match with following char
|
|
// pP++;
|
|
// NO BREAK HERE
|
|
|
|
default:
|
|
if (IsCaseSensitive)
|
|
{
|
|
if (text[pT] != p[pP])
|
|
return MATCH_FALSE;
|
|
else
|
|
continue;
|
|
}
|
|
if (UCtable[(int) text[pT]] != UCtable[(int) p[pP]])
|
|
return MATCH_FALSE;
|
|
continue;
|
|
|
|
case '?':
|
|
continue;
|
|
|
|
case '*':
|
|
if (p[pP] == '*')
|
|
pP++;
|
|
if (p[pP] == '\0')
|
|
return MATCH_TRUE;
|
|
while (text[pT] != FALSE)
|
|
{
|
|
matched = DoMatch(text + pT++, p + pP, FALSE);
|
|
if (matched != MATCH_FALSE)
|
|
return matched;
|
|
}
|
|
return MATCH_ABORT;
|
|
|
|
}
|
|
}
|
|
return (text[pT] == '\0');
|
|
}
|
|
|
|
// This will be called from the other funcs
|
|
int VMatching(const char *text, const char *p, bx_bool IsCaseSensitive)
|
|
{
|
|
#ifdef OPTIMIZE_JUST_STAR
|
|
if (p[0] == '*' && p[1] == '\0')
|
|
return MATCH_TRUE;
|
|
#endif
|
|
return (DoMatch(text, p, IsCaseSensitive) == MATCH_TRUE);
|
|
}
|
|
|
|
int IsMatching(const char *text, const char *p, bx_bool IsCaseSensitive)
|
|
{
|
|
return VMatching(text, p, IsCaseSensitive);
|
|
}
|
|
|
|
// utility function for list resizing operation -- set LoX and HiX
|
|
// the resize operation exits if the mouse moves beyond LoX or HiX
|
|
void SetHorzLimits()
|
|
{
|
|
int i;
|
|
if (Sizing == -2) // is it the left or right bar?
|
|
{
|
|
Resize_LoX = OneCharWide << 2; // set horizontal limits
|
|
i = ListWidthPix[(DockOrder >> 8) -1]; // col1 width
|
|
// calculate end of col2 - 4 charwidths in parent coordinates
|
|
Resize_HiX = i + ListWidthPix[CurCenterList] - (OneCharWide << 2);
|
|
Sizing = 1;
|
|
}
|
|
else
|
|
{
|
|
i = ListWidthPix[(DockOrder >> 8) -1]; // col1 width
|
|
Resize_LoX = i + (OneCharWide << 2); // set horizontal limits
|
|
// calculate total width - 4 charwidths in parent coordinates
|
|
i = ListWidthPix[REG_WND] + ListWidthPix[ASM_WND] + ListWidthPix[DUMP_WND];
|
|
Resize_HiX = i - (OneCharWide << 2);
|
|
Sizing = 2;
|
|
}
|
|
}
|
|
|
|
void DockResize (int DestIdx, Bit32u ParentX)
|
|
{
|
|
if (Sizing >= 10) // dock operation
|
|
{
|
|
int Siz = Sizing - 10; // calculate which list initiated dock = moving window
|
|
if (Siz != DestIdx) // moving window = destination window is a no-op
|
|
{
|
|
// Convert Sizing and DestIdx into a table lookup index (j)
|
|
// -- otherwise, the "algorithm" to compute new DockOrder is annoying
|
|
int j = (Siz*2 + ((Siz | DestIdx) & 1)) *6;
|
|
if (Siz == 1)
|
|
j = (Siz*4 + (DestIdx & 2)) *3;
|
|
|
|
// convert current DockOrder to a number from 0 to 5, add to j
|
|
j += ((DockOrder >> 7) - 2) &6;
|
|
if (((DockOrder >> 4) &3) > (DockOrder & 3))
|
|
j += 1;
|
|
DockOrder = nDock[j];
|
|
MoveLists();
|
|
}
|
|
}
|
|
else // resize operation
|
|
{
|
|
int idx, totpix;
|
|
if (Sizing == 1)
|
|
{
|
|
idx = (DockOrder >> 8) -1; // sizing the left bar
|
|
totpix = ListWidthPix[idx] + ListWidthPix[CurCenterList];
|
|
ListWidthPix[idx] = ParentX;
|
|
ListWidthPix[CurCenterList] = totpix - ParentX; // reset the widths of the left and center windows
|
|
}
|
|
else
|
|
{
|
|
ParentX -= ListWidthPix[(DockOrder >> 8) -1]; // caclulate new width of center window
|
|
idx = (DockOrder & 3) -1;
|
|
totpix = ListWidthPix[idx] + ListWidthPix[CurCenterList];
|
|
ListWidthPix[CurCenterList] = ParentX;
|
|
ListWidthPix[idx] = totpix - ParentX; // reset the widths of the right and center windows
|
|
}
|
|
|
|
MoveLists();
|
|
}
|
|
}
|
|
|
|
// Convert a string (except for the 0x in a hex number) to uppercase
|
|
void upr(char* d)
|
|
{
|
|
char *p;
|
|
p = d;
|
|
while (*p != 0)
|
|
{
|
|
if (*p == '0' && p[1] == 'x')
|
|
p += 2;
|
|
else
|
|
{
|
|
*p = UCtable[(int) *p]; // use the lookup table created by MakeXlatTables
|
|
++p;
|
|
}
|
|
}
|
|
}
|
|
|
|
// create EFLAGS display for Status line
|
|
void ShowEflags(char *buf)
|
|
{
|
|
static const char *EflBName[16] = {
|
|
"cf", "pf", "af", "zf", "sf", "tf", "if", "df", "of", "nt", "rf", "vm", "ac", "vif", "vip", "id"
|
|
};
|
|
static const int EflBNameLen[16] = {
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,2
|
|
};
|
|
static const int EflBitVal[16] = {
|
|
1, 4, 0x10, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x4000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000
|
|
};
|
|
|
|
Bit32u Efl = (Bit32u) rV[EFL_Rnum];
|
|
int i = 16;
|
|
char *cp = buf + 6;
|
|
|
|
sprintf(buf,"IOPL=%1u", (Efl & 0x3000) >> 12);
|
|
while (--i >= 0)
|
|
{
|
|
*(cp++)= ' ';
|
|
strcpy (cp, EflBName[i]); // copy the name of the bitflag
|
|
if ((Efl & EflBitVal[i]) != 0) // if the bit is set, put the name in uppercase
|
|
upr(cp);
|
|
cp += EflBNameLen[i];
|
|
}
|
|
}
|
|
|
|
// change the display on the status line if anything has changed
|
|
void UpdateStatus()
|
|
{
|
|
static bx_bool PrevAtBreak = FALSE;
|
|
|
|
if (StatusChange == FALSE) return; // avoid sending unnecessary messages/invalidations
|
|
StatusChange = FALSE;
|
|
|
|
if (AtBreak != FALSE) // modify status line only during a break
|
|
{
|
|
char eflags_buf[80];
|
|
ShowEflags(eflags_buf); // prints out eflags
|
|
SetStatusText(3, eflags_buf); // display eflags
|
|
|
|
if (CpuModeChange != FALSE) // Did CR0 bits or EFER bits change value?
|
|
{
|
|
CpuModeChange = FALSE;
|
|
char mode[32];
|
|
switch (CpuMode) {
|
|
case BX_MODE_IA32_REAL:
|
|
if (In32Mode == FALSE)
|
|
strcpy (mode, "CPU: Real Mode 16");
|
|
else
|
|
strcpy (mode, "CPU: Real Mode 32");
|
|
break;
|
|
case BX_MODE_IA32_V8086:
|
|
strcpy (mode, "CPU: V8086 Mode");
|
|
break;
|
|
case BX_MODE_IA32_PROTECTED:
|
|
if (In32Mode == FALSE) {
|
|
if (InPaging != 0)
|
|
strcpy (mode, "CPU: Protected Mode 16 (PG)");
|
|
else
|
|
strcpy (mode, "CPU: Protected Mode 16");
|
|
}
|
|
else {
|
|
if (InPaging != 0)
|
|
strcpy (mode, "CPU: Protected Mode 32 (PG)");
|
|
else
|
|
strcpy (mode, "CPU: Protected Mode 32");
|
|
}
|
|
break;
|
|
case BX_MODE_LONG_COMPAT:
|
|
strcpy (mode, "CPU: Compatibility Mode");
|
|
break;
|
|
case BX_MODE_LONG_64:
|
|
strcpy (mode, "CPU: Long Mode");
|
|
break;
|
|
}
|
|
SetStatusText(1, mode); // display CPU mode in status col#1
|
|
}
|
|
}
|
|
|
|
if (AtBreak != PrevAtBreak)
|
|
// switch window color ("active"=white or gray), depending on AtBreak value
|
|
MakeListsGray();
|
|
PrevAtBreak = AtBreak;
|
|
}
|
|
|
|
// Read a copy of some emulated linear bochs memory
|
|
// Note: laddr + len must not cross a 4K boundary -- otherwise, there are no limits
|
|
bx_bool ReadBxLMem(Bit64u laddr, unsigned len, Bit8u *buf)
|
|
{
|
|
return bx_dbg_read_linear(CurrentCPU, laddr, len, buf);
|
|
}
|
|
|
|
// binary conversion (and validity testing) on hex/decimal char string inputs
|
|
Bit64u cvthex(char *p, Bit64u errval)
|
|
{
|
|
Bit64u ret = 0;
|
|
bx_bool end = FALSE;
|
|
while (end == FALSE)
|
|
{
|
|
if (*p >= '0' && *p <= '9') // test for digits
|
|
ret = (ret << 4) | (*(p++) - '0');
|
|
else if ((*p | 0x20) >= 'a' && (*p | 0x20) <= 'f') // test for hex letters
|
|
ret = (ret << 4) | ((*(p++) | 0x20) - 'a' + 10); // 0xA = 10, of course
|
|
else
|
|
end = TRUE;
|
|
}
|
|
if (*p != 0 && *p != ' ' && *p != '\t') // hex must end on a whitespace
|
|
return errval;
|
|
return ret;
|
|
}
|
|
|
|
Bit64u cvt64(char *nstr, bx_bool negok)
|
|
{
|
|
char *p, *s;
|
|
Bit64u ret = 0;
|
|
bx_bool neg = FALSE;
|
|
p= nstr;
|
|
while (*p==' ' || *p == '\t')
|
|
++p;
|
|
if (*p == '-' && negok != FALSE)
|
|
{
|
|
++p;
|
|
neg = TRUE;
|
|
}
|
|
if (*p == '0' && (p[1] | 0x20) == 'x' && neg == FALSE)
|
|
return cvthex (p+2, 0);
|
|
s = p;
|
|
while (*p >= '0' && *p <= '9')
|
|
ret = (ret * 10) + *(p++) - '0';
|
|
if ((*p | 0x20) >= 'a' && (*p | 0x20) <= 'f' && neg == FALSE)
|
|
return cvthex (s, ret);
|
|
if (neg != FALSE)
|
|
return 0 - ret;
|
|
return ret;
|
|
}
|
|
|
|
// "singlestep" disassembly lines from the internal debugger are sometimes ignored
|
|
bx_bool isSSDisasm(char *s)
|
|
{
|
|
if (ignSSDisasm == FALSE) // ignoring those lines?
|
|
return FALSE;
|
|
|
|
while (*s == ' ') // need to parse the line to see if it is ASM
|
|
++s;
|
|
if (*s != '(') // first char must be (
|
|
return FALSE;
|
|
while (*s != '[' && *s != 0) // then there must be a [
|
|
++s;
|
|
if (*s == 0)
|
|
return FALSE;
|
|
while (*s != 0 && (*s != ')' || s[1] != ':' || s[2] != ' '))
|
|
++s;
|
|
if (*s == 0)
|
|
return FALSE;
|
|
while (*s != ';' && *s != 0) // last, there must be a ;
|
|
++s;
|
|
if (*s == 0)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
// dump output from the bochs internal debugger to Output window
|
|
// Note: this routine may be called *DIRECTLY* from bochs!
|
|
void ParseIDText(const char *x)
|
|
{
|
|
int i = 0;
|
|
int overflow = 0;
|
|
while (*x !=0 && *x != '\r' && *x != '\n' && DbgAppendPtr < tmpcb)
|
|
*(DbgAppendPtr++)= *(x++); // append the chars from x into the bigbuf
|
|
if (DbgAppendPtr >= tmpcb) // overflow error?
|
|
{
|
|
DispMessage("Debugger output cannot be parsed -- line too long","Buffer overflow");
|
|
DbgAppendPtr = bigbuf; // throw away the line
|
|
return;
|
|
}
|
|
|
|
*DbgAppendPtr = 0;
|
|
if (*x == 0) // automatically process only complete lines further
|
|
{
|
|
PO_Tdelay = 2; // wait a half second, then force display of partial lines
|
|
return;
|
|
}
|
|
PO_Tdelay = 0; // line completed -- cancel any partial output time delay
|
|
|
|
// restart DbgAppendPtr at the beginning of a new line buffer
|
|
char *s = DbgAppendPtr = bigbuf; // s -> raw text line from debugger
|
|
if (ignoreNxtT != FALSE)
|
|
{
|
|
if (strncmp(s,"Next at t",9) == 0)
|
|
return;
|
|
}
|
|
if (isSSDisasm(s) != FALSE)
|
|
return;
|
|
|
|
while ((*s >= ' ' || *s == '\t') && i < 204) // scan out to eol, count chars
|
|
{
|
|
++i;
|
|
++s;
|
|
}
|
|
if (i > 203) // max out at 203 chars per line (should never happen)
|
|
{
|
|
i = 200;
|
|
overflow = 3;
|
|
}
|
|
char *p = OutWindow;
|
|
if ((i+overflow+2+useCR) > OutWinCnt) // amt needed vs. space available
|
|
{
|
|
s = OutWindow; // need to toss lines off beginning of OutWindow
|
|
int j = OutWinCnt - overflow - 2 - useCR;
|
|
while (j < i) // throw away one line at a time
|
|
{
|
|
// stop on any unprintable char < ' '
|
|
while ((unsigned char)*s >= ' ' || *s == '\t')
|
|
{
|
|
++s;
|
|
++j; // increase available space as chars are tossed
|
|
}
|
|
// in reality, s must be pointing at an EOL
|
|
s += 1 + useCR;
|
|
j += 1 + useCR;
|
|
}
|
|
OutWinCnt = j + overflow + 2 + useCR;
|
|
j = OutWinSIZE - OutWinCnt; // chars to copy, without the terminal zero
|
|
while (j-- > 0) // recopy the OutWindow buffer up
|
|
*(p++) = *(s++);
|
|
}
|
|
else
|
|
p = OutWindow + OutWinSIZE - OutWinCnt;
|
|
OutWinCnt -= i + overflow + 1 + useCR;
|
|
if (useCR != 0)
|
|
*(p++) = '\r'; // end of buf only had a 0 in it,
|
|
*(p++) = '\n'; // and needs an EOL to display properly
|
|
s = bigbuf;
|
|
while (i-- > 0) // copy the new output line onto the buffer
|
|
*(p++) = *(s++);
|
|
if (overflow != 0)
|
|
{
|
|
*(p++) = '.'; // just for fun, if the line overflows
|
|
*(p++) = '.';
|
|
*(p++) = '.';
|
|
}
|
|
*p = 0;
|
|
SetOutWinTxt(); // OS specific routine to replace Output window's text
|
|
}
|
|
|
|
// load appropriate register values from simulation into local rV[] array
|
|
void FillRegs()
|
|
{
|
|
int i = EFER_Rnum + 1; // EFER is the highest reg # in rV
|
|
while (--i >= 0)
|
|
{
|
|
if (RegObject[CurrentCPU][i] != NULL)
|
|
rV[i] = RegObject[CurrentCPU][i]->get64();
|
|
}
|
|
#if BX_SUPPORT_X86_64 == 0
|
|
// copy RIP, RSP from EIP, ESP -- so LAs for both are always easily available
|
|
rV[RIP_Rnum] = rV[EIP_Rnum];
|
|
rV[RSP_Rnum] = rV[ESP_Rnum];
|
|
#else
|
|
// copy the lower dwords of RAX - RIP to EAX - EIP (with 32bit truncation)
|
|
i = RIP_Rnum + 1;
|
|
while (--i >= 0)
|
|
rV[i + (EAX_Rnum - RAX_Rnum)] = GET32L(rV[i]);
|
|
#endif
|
|
if (RegObject[CurrentCPU][GDTR_Lim] != NULL) // get the limits on GDT and IDT
|
|
GDT_Len = RegObject[CurrentCPU][GDTR_Lim]->get();
|
|
if (RegObject[CurrentCPU][IDTR_Lim] != NULL)
|
|
IDT_Len = RegObject[CurrentCPU][IDTR_Lim]->get();
|
|
|
|
// Check CR0 bit 31 -- Paging bit
|
|
Bit32u NewPg = (Bit32u) rV[CR0_Rnum] & 0x80000000;
|
|
if (InPaging != NewPg)
|
|
{
|
|
GrayMenuItem ((int) NewPg, CMD_PAGEV);
|
|
StatusChange = TRUE;
|
|
}
|
|
InPaging = NewPg;
|
|
}
|
|
|
|
// grab linear breakpoints out of internal debugger's bx_guard structures, and sort them
|
|
void ParseBkpt()
|
|
{
|
|
extern bx_guard_t bx_guard;
|
|
int k;
|
|
int j = 0;
|
|
int i = bx_guard.iaddr.num_linear;
|
|
while (--i >= 0)
|
|
{
|
|
if (bx_guard.iaddr.lin[i].enabled != FALSE)
|
|
{
|
|
BrkLAddr[j] = bx_guard.iaddr.lin[i].addr;
|
|
BrkIdx[j] = bx_guard.iaddr.lin[i].bpoint_id;
|
|
++j;
|
|
}
|
|
}
|
|
BreakCount = i = j;
|
|
// sort the breakpoint list (linear sort), to make it faster to search
|
|
while (--i > 0)
|
|
{
|
|
j = k = i;
|
|
while (--j >= 0)
|
|
{
|
|
if (BrkLAddr[j] > BrkLAddr[k]) // find the next biggest
|
|
k = j;
|
|
}
|
|
if (k < i)
|
|
{
|
|
bx_address h = BrkLAddr[i]; // do the swap on BOTH arrays
|
|
j = BrkIdx[i];
|
|
BrkLAddr[i] = BrkLAddr[k];
|
|
BrkIdx[i] = BrkIdx[k];
|
|
BrkLAddr[k] = h;
|
|
BrkIdx[k] = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
// this routine is only called if debugger already knows SSE is supported
|
|
// -- but it might not be "turned on", either
|
|
int FillSSE(int LineCount)
|
|
{
|
|
#if BX_CPU_LEVEL >= 6
|
|
Bit64u val = 0;
|
|
bx_param_num_c *p;
|
|
char *cols[3];
|
|
char ssetxt[80];
|
|
|
|
if ((rV[CR0_Rnum] & 0xc) != 0) // TS or EM flags in CR0 temporarily disable SSE
|
|
{
|
|
cols[0] = ssetxt;
|
|
strcpy (ssetxt, "SSE-off");
|
|
InsertListRow(cols, 1, REG_WND, LineCount, 4);
|
|
RitemToRnum[LineCount] = XMM0_Rnum;
|
|
return ++LineCount;
|
|
}
|
|
|
|
// format: XMM[#] 00000000:00000000 (each 16 hex digits)
|
|
*ssetxt = 0;
|
|
cols[1] = ssetxt; // column 1 is being left blank
|
|
cols[0] = ssetxt + 1;
|
|
cols[2] = ssetxt + 10;
|
|
strcpy (ssetxt+1, "XMM[0]");
|
|
ssetxt[10] = '0'; // I'm putting a hex value in the decimal column -- more room there!
|
|
ssetxt[11] = 'x';
|
|
strcpy (ssetxt + 28, " : ");
|
|
for (int i = 0; i < /*BX_XMM_REGISTERS*/ 16; i++)
|
|
{
|
|
if (i >= 10)
|
|
{
|
|
ssetxt[5] = '1';
|
|
ssetxt[6] = i - 10 + '0';
|
|
ssetxt[7] = ']';
|
|
ssetxt[8] = 0;
|
|
}
|
|
else
|
|
ssetxt[5] = i + '0';
|
|
|
|
RitemToRnum[LineCount] = i + XMM0_Rnum;
|
|
p = RegObject[CurrentCPU][XMM0_hi + i];
|
|
if (p != NULL)
|
|
val = p->get64(); // get the value of "xmm(i)_hi" register
|
|
else
|
|
val = 0;
|
|
sprintf (ssetxt + 12,Fmt64b[UprCase],val);
|
|
p = RegObject[CurrentCPU][XMM0_Rnum + i];
|
|
if (p != NULL)
|
|
val = p->get64(); // "SSE.xmm[i]_lo"
|
|
else
|
|
val = 0;
|
|
sprintf (ssetxt + 31,Fmt64b[UprCase], val);
|
|
InsertListRow(cols, 3, REG_WND, LineCount, 4); // 3 cols, group 4
|
|
++LineCount;
|
|
}
|
|
#endif
|
|
return (LineCount);
|
|
}
|
|
|
|
// this routine is only called if debugger already knows FPU is supported
|
|
// -- but it might not be active
|
|
int FillMMX(int LineCount)
|
|
{
|
|
static double scale_factor = pow(2.0, -63.0);
|
|
int i;
|
|
Bit16u exp = 0;
|
|
Bit64u mmreg = 0;
|
|
bx_param_num_c *p;
|
|
unsigned short exponent[8];
|
|
char *cols[3];
|
|
char fputxt[60];
|
|
|
|
cols[0] = fputxt;
|
|
if ((rV[CR0_Rnum] & 0xc) != 0) // TS or EM flags in CR0 temporarily disable MMX/FPU/SSE
|
|
{
|
|
strcpy (fputxt, "FPU-off");
|
|
InsertListRow(cols, 1, REG_WND, LineCount, 3);
|
|
RitemToRnum[LineCount] = ST0_Rnum;
|
|
return ++LineCount;
|
|
}
|
|
|
|
// format: MM#|ST# 00000000:00000000 then FPU float value in "decimal" column
|
|
cols[1] = fputxt + 10;
|
|
cols[2] = fputxt + 32;
|
|
strcpy (fputxt, "MM0-ST0");
|
|
strcpy (fputxt + 18, " : ");
|
|
i = 7;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
fputxt[2] = i + '0';
|
|
fputxt[6] = i + '0';
|
|
RitemToRnum[LineCount] = i + ST0_Rnum;
|
|
p = RegObject[CurrentCPU][ST0_Rnum + i];
|
|
if (p != NULL)
|
|
mmreg = p->get64(); // get the value of "mmx(i)" register
|
|
else
|
|
mmreg = 0;
|
|
sprintf (fputxt + 10,Fmt32b[UprCase],GET32H(mmreg));
|
|
sprintf (fputxt + 21,Fmt32b[UprCase], GET32L(mmreg));
|
|
|
|
p = RegObject[CurrentCPU][ST0_exp + i];
|
|
if (p != NULL)
|
|
exp = (Bit16u) p->get64(); // get the exponent for this FPU register
|
|
else
|
|
exp = 0;
|
|
exponent[i] = exp; // save each one temporarily
|
|
double f = pow(2.0, ((0x7fff & exp) - 0x3fff));
|
|
if (exp & 0x8000)
|
|
f = -f;
|
|
#ifdef _MSC_VER
|
|
f *= (double)(signed __int64)(mmreg>>1) * scale_factor * 2;
|
|
#else
|
|
f *= mmreg*scale_factor;
|
|
#endif
|
|
sprintf (cols[2],"%.3e",f);
|
|
InsertListRow(cols, 3, REG_WND, LineCount, 3); // 3 cols, group 3
|
|
++LineCount;
|
|
}
|
|
strcpy (fputxt, "ST0.exp");
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
fputxt[2] = i + '0';
|
|
RitemToRnum[LineCount] = i + ST0_exp;
|
|
sprintf (fputxt+10,Fmt16b[UprCase], exponent[i]); // col1
|
|
sprintf (fputxt+32,"%u", exponent[i]); // col2
|
|
InsertListRow(cols, 3, REG_WND, LineCount, 3); // 3 cols, group 3
|
|
++LineCount;
|
|
}
|
|
return LineCount;
|
|
}
|
|
|
|
// get values of Debug registers from simulation
|
|
int FillDebugRegs(int itemnum)
|
|
{
|
|
bx_param_num_c *bxp;
|
|
Bit32u val;
|
|
unsigned int i;
|
|
char *cols[2];
|
|
char drtxt[20];
|
|
|
|
strcpy (drtxt,"dr0");
|
|
if (UprCase != FALSE)
|
|
{
|
|
*drtxt = 'D';
|
|
drtxt[1] = 'R';
|
|
}
|
|
cols[0] = drtxt;
|
|
cols[1] = drtxt + 4;
|
|
|
|
for(i = 0; i < 6; i++)
|
|
{
|
|
bxp = RegObject[CurrentCPU][DR0_Rnum + i];
|
|
val = 0;
|
|
if (bxp != NULL)
|
|
val = bxp->get();
|
|
RitemToRnum[itemnum] = i + DR0_Rnum;
|
|
sprintf(drtxt + 4,Fmt32b[UprCase],val);
|
|
|
|
InsertListRow(cols, 2, REG_WND, itemnum, 5); // 3 cols, group 5
|
|
++drtxt[2]; // change the name, the cheap way
|
|
if (i == 3) drtxt[2] += 2; // jump from "DR3" to "DR6"
|
|
++itemnum;
|
|
}
|
|
return itemnum;
|
|
}
|
|
|
|
#define BX_GUI_DB_ASM_DATA (4400)
|
|
|
|
// Disassemble a linear memory area, in a loop, loading text into ASM window
|
|
// completely update the ASM display with new data
|
|
void FillAsm(Bit64u LAddr, int MaxLines)
|
|
{
|
|
Bit64u ReadAddr = LAddr;
|
|
int BufLen = 0;
|
|
int i, len;
|
|
bx_bool BufEmpty;
|
|
bx_bool Go = TRUE;
|
|
char AsmData[BX_GUI_DB_ASM_DATA]; // 5K for binary disassembly data
|
|
char *s, *p = AsmData; // just to avoid a compiler warning
|
|
char *cols[3];
|
|
char asmtxt[200];
|
|
|
|
cols[0] = asmtxt;
|
|
cols[1] = asmtxt + 36;
|
|
cols[2] = asmtxt + 100;
|
|
AsmLineCount = 0; // initialize for disasm window update
|
|
StartListUpdate(ASM_WND);
|
|
if (MaxLines > MAX_ASM) // just for protection
|
|
MaxLines = MAX_ASM;
|
|
|
|
while (Go != FALSE)
|
|
{
|
|
// copydown buffer -- buffer size must be 4K + 16
|
|
s= AsmData;
|
|
i= BufLen; // BufLen is guaranteed < 16
|
|
while (i-- > 0)
|
|
*(s++)= *(p++);
|
|
// load buffer, up to the next 4k boundary
|
|
len = 4096 - (((int) ReadAddr) & 0xfff); // calculate read amount
|
|
Go = ReadBxLMem(ReadAddr, len, (Bit8u *) s);
|
|
BufLen += len;
|
|
ReadAddr += len;
|
|
if (Go == FALSE)
|
|
break;
|
|
BufEmpty = FALSE;
|
|
p= AsmData; // start at the beginning of the new buffer
|
|
while (AsmLineCount < MaxLines && BufEmpty == FALSE)
|
|
{
|
|
// disassemble 1 line with a direct call, into asmtxt
|
|
len = bx_disassemble.disasm(In32Mode, In64Mode, (bx_address) 0,
|
|
(bx_address) LAddr, (Bit8u *) p, cols[2]);
|
|
if (len <= BufLen) // disassembly was successful?
|
|
{
|
|
AsmLA[AsmLineCount] = LAddr; // save, and
|
|
if (In64Mode == FALSE) // "display" linear addy of the opcode
|
|
sprintf (asmtxt,Fmt32b[UprCase],LAddr);
|
|
else
|
|
sprintf (asmtxt,Fmt64b[UprCase],LAddr);
|
|
|
|
BufLen -= len; // used up len bytes from buffer
|
|
LAddr += len; // calculate next LAddr
|
|
|
|
// then build the "bytes" column entry
|
|
s = cols[1];
|
|
*(s++) = '('; // begin with the bytecount in parens
|
|
i = len;
|
|
if (len > 9)
|
|
{
|
|
*(s++)= '1'; // len < 16, so convert to decimal the easy way
|
|
i -= 10;
|
|
}
|
|
*(s++) = i + '0';
|
|
*(s++) = ')';
|
|
*(s++) = ' ';
|
|
while (len-- > 0)
|
|
{
|
|
i = (unsigned char) *(p++);
|
|
*(s++) = AsciiHex[2*i];
|
|
*(s++) = AsciiHex[2*i+1];
|
|
}
|
|
*s = 0; // zero terminate the "bytes" string
|
|
|
|
// then, finalize the ASM disassembly text
|
|
if (UprCase != FALSE) // do any requested uppercase conversion on the text
|
|
upr(cols[2]);
|
|
InsertListRow(cols, 3, ASM_WND, AsmLineCount, 8); // 3 cols, "no" group
|
|
++AsmLineCount;
|
|
}
|
|
else
|
|
BufEmpty = TRUE;
|
|
}
|
|
if (AsmLineCount >= MaxLines) // disassembled enough lines?
|
|
Go = FALSE;
|
|
}
|
|
if (ResizeColmns != FALSE)
|
|
RedrawColumns(ASM_WND);
|
|
EndListUpdate(ASM_WND);
|
|
}
|
|
|
|
|
|
// Reload the entire Register window with data
|
|
void LoadRegList()
|
|
{
|
|
int i, itemnum; // TODO: This routine needs a big rewrite to make it pretty
|
|
bx_bool showEreg = TRUE;
|
|
char *cols[3];
|
|
char regtxt[100];
|
|
StartListUpdate(REG_WND);
|
|
FillRegs(); // get new values for rV local register array
|
|
|
|
// Display GP registers -- 64 bit registers first, if they exist
|
|
cols[1] = regtxt;
|
|
cols[2] = regtxt + 40;
|
|
itemnum = 0;
|
|
#if BX_SUPPORT_X86_64
|
|
if (In64Mode)
|
|
{
|
|
showEreg = SeeReg[0]; // get user option setting for EAX, etc.
|
|
for (i = RAX_Rnum; i <= R15_Rnum; i++)
|
|
{
|
|
RitemToRnum[itemnum] = i; // always recreate the register -> itemnum mapping
|
|
sprintf(regtxt,Fmt64b[UprCase], rV[i]); // print the hex column
|
|
sprintf(cols[2], FMT_LL "d", rV[i]); // and decimal
|
|
cols[0] = RDispName[i];
|
|
InsertListRow(cols, 3, REG_WND, itemnum, 0); // 3 cols, group 0
|
|
++itemnum;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// then 32bit GP registers (if appropriate)
|
|
if (showEreg != FALSE)
|
|
{
|
|
for (i = EAX_Rnum; i <= EIP_Rnum; i++)
|
|
{
|
|
RitemToRnum[itemnum] = i;
|
|
sprintf(regtxt, Fmt32b[UprCase], (Bit32u)rV[i]); // print the hex column
|
|
sprintf(cols[2], FMT_LL "d", rV[i]); // and decimal
|
|
cols[0] = RDispName[i];
|
|
|
|
if (In32Mode == FALSE && i == 26) // Check for Real Mode (Pmode is TRUE in Long Mode)
|
|
{
|
|
rV[EIP_Rnum] &= 0xffff; // in Real Mode, mask IP to 2 bytes
|
|
++cols[0]; // and shorten name to 2 letters
|
|
}
|
|
InsertListRow(cols, 3, REG_WND, itemnum, 0); // 3 cols, group 0
|
|
++itemnum;
|
|
}
|
|
}
|
|
// always insert eflags next
|
|
RitemToRnum[itemnum] = EFL_Rnum;
|
|
sprintf(regtxt,Fmt32b[UprCase],(Bit32u)rV[EFL_Rnum]); // print the hex column
|
|
cols[0] = RDispName[EFL_Rnum];
|
|
InsertListRow(cols, 2, REG_WND, itemnum, 0); // 2 cols, group 0
|
|
++itemnum;
|
|
|
|
if (rV[EFL_Rnum] != PV[EFL_Rnum])
|
|
StatusChange = TRUE; // if eflags changed, force a status update
|
|
|
|
// display Segment registers (if requested)
|
|
if (SeeReg[1])
|
|
{
|
|
for(i = CS_Rnum; i <= GS_Rnum; i++) // segment registers
|
|
{
|
|
RitemToRnum[itemnum] = i;
|
|
sprintf(regtxt, Fmt16b[UprCase], rV[i] & 0xffff);
|
|
cols[0] = RDispName[i];
|
|
InsertListRow(cols, 2, REG_WND, itemnum, 1); // 2 cols, group 1
|
|
++itemnum;
|
|
}
|
|
}
|
|
// display System regsiters (if requested)
|
|
// displaying these once may be necessary for column resizing
|
|
if (SeeReg[2] || (ResizeColmns != FALSE && NeedSysRresize != FALSE))
|
|
{
|
|
int j = TRRnum;
|
|
if (In32Mode == FALSE) // don't show lgdt or tr in Real mode
|
|
j= IDTRnum;
|
|
for(i = GDTRnum; i <= j; i++)
|
|
{
|
|
RitemToRnum[itemnum] = i;
|
|
if (i == GDTRnum || i == IDTRnum)
|
|
{
|
|
Bit16u limit = GDT_Len;
|
|
if (i == IDTRnum)
|
|
limit = IDT_Len;
|
|
if (In64Mode == FALSE)
|
|
sprintf(regtxt,xDT32Fmt[UprCase],(Bit32u)rV[i],limit);
|
|
else
|
|
sprintf(regtxt,xDT64Fmt[UprCase],rV[i],limit);
|
|
|
|
}
|
|
else
|
|
sprintf(regtxt,Fmt16b[UprCase], rV[i] & 0xffff); // lgdt, tr
|
|
|
|
cols[0] = RDispName[i];
|
|
InsertListRow(cols, 2, REG_WND, itemnum, 1); // 2 cols, group 1
|
|
++itemnum;
|
|
}
|
|
}
|
|
// display Control Registers (if requested)
|
|
if (SeeReg[3])
|
|
{
|
|
for(i = CR0_Rnum; i <= EFER_Rnum; i++)
|
|
{
|
|
RitemToRnum[itemnum] = i;
|
|
|
|
if (In64Mode && (i == CR2_Rnum || i == CR3_Rnum))
|
|
sprintf(regtxt,Fmt64b[UprCase],rV[i]);
|
|
else
|
|
sprintf(regtxt,Fmt32b[UprCase],(Bit32u)rV[i]);
|
|
|
|
cols[0] = RDispName[i];
|
|
InsertListRow(cols, 2, REG_WND, itemnum, 2); // 2 cols, group 2
|
|
++itemnum;
|
|
}
|
|
}
|
|
|
|
// set the register background colors for rV
|
|
i = EFER_Rnum + 1; // total number of registers stored in rV
|
|
while (--i >= 0)
|
|
{
|
|
if (rV[i] != PV[i]) // set the "red" flag if value changed
|
|
RegColor[i] |= 0x80;
|
|
else
|
|
RegColor[i] &= 0x7f;
|
|
}
|
|
// Load any optional STi, MMX, SSE, DRx, TRx register info into the Register window
|
|
#if BX_SUPPORT_FPU
|
|
// MMX-FPU registers
|
|
if (SeeReg[4] != FALSE)
|
|
itemnum = FillMMX(itemnum);
|
|
#endif
|
|
|
|
// SSE registers
|
|
if (CpuSupportSSE && SeeReg[5] != FALSE)
|
|
itemnum = FillSSE(itemnum);
|
|
|
|
// Internal x86 Debug Registers
|
|
if (SeeReg[6] != FALSE)
|
|
itemnum = FillDebugRegs(itemnum);
|
|
|
|
RedrawColumns(REG_WND); // resize Hex Value column sometimes
|
|
EndListUpdate(REG_WND);
|
|
}
|
|
|
|
// scroll ASM window so that the current line is in the "middle"
|
|
void doAsmScroll()
|
|
{
|
|
int j;
|
|
int CurTopIdx = GetASMTopIdx();
|
|
int nli = -2;
|
|
// Can the current line be displayed at all?
|
|
if (CurrentAsmLA < *AsmLA || CurrentAsmLA > AsmLA[AsmLineCount-1])
|
|
return;
|
|
// convert from LA to a Line Index (nli) with a search
|
|
j = CurTopIdx; // try to start at CurTopIdx
|
|
if (AsmLA[j] > CurrentAsmLA)
|
|
j = 0;
|
|
while (nli < 0 && j < AsmLineCount && AsmLA[j] <= CurrentAsmLA)
|
|
{
|
|
if (AsmLA[j] == CurrentAsmLA)
|
|
nli = j;
|
|
++j;
|
|
}
|
|
// not found -> CurrentAsmLA is an illegal opcode address
|
|
if (nli < 0)
|
|
return;
|
|
// is the current line ALREADY in the middle of the window?
|
|
if (nli < CurTopIdx || nli >= CurTopIdx + AsmPgSize - bottommargin)
|
|
{
|
|
// need to scroll!
|
|
int ScrollLines = nli - CurTopIdx - topmargin;
|
|
j = AsmLineCount - CurTopIdx - AsmPgSize;
|
|
// limit ScrollLines by the theoretical max and min
|
|
if (ScrollLines > j)
|
|
ScrollLines = j + 1; // just a little extra to make sure
|
|
if (ScrollLines < -CurTopIdx)
|
|
ScrollLines = -CurTopIdx - 1; // just a little extra to make sure
|
|
// convert # of scroll lines to pixels
|
|
ScrollASM (ScrollLines * ListLineRatio);
|
|
}
|
|
Invalidate(ASM_WND); // "current opcode" in ASM window needs redrawing
|
|
}
|
|
|
|
// try to find a Linear Address to start a "pretty" autodisassembly
|
|
void CanDoLA(Bit64u *h)
|
|
{
|
|
int index;
|
|
if (TopAsmLA > *h || *h > AsmLA[AsmLineCount-1]) // is it hopeless?
|
|
return;
|
|
if (bottommargin > AsmLineCount)
|
|
index = 0;
|
|
else
|
|
index = AsmLineCount - bottommargin;
|
|
while (++index < AsmLineCount)
|
|
{
|
|
if (AsmLA[index] == *h)
|
|
{
|
|
*h = AsmLA[index - topmargin];
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InitRegObjects()
|
|
{
|
|
bx_list_c *cpu_list;
|
|
extern bx_list_c *root_param;
|
|
int cpu = BX_SMP_PROCESSORS;
|
|
// get the param tree interface objects for every single register on all CPUs
|
|
while (--cpu >= 0)
|
|
{
|
|
// RegObject[j]s are all initialized to NULL when allocated in the BSS area
|
|
// but it doesn't hurt anything to do it again, once
|
|
int i = TOT_REG_NUM + EXTRA_REGS;
|
|
while (--i >= 0)
|
|
RegObject[cpu][i] = (bx_param_num_c *) NULL;
|
|
|
|
char pname[16];
|
|
sprintf (pname,"bochs.cpu%d", cpu); // set the "cpu number" for cpu_list
|
|
cpu_list = (bx_list_c *) SIM->get_param(pname, root_param);
|
|
|
|
// TODO: in the next version, put all the names in an array, and loop
|
|
// -- but that method is not compatible with bochs 2.3.7 or earlier
|
|
#if BX_SUPPORT_X86_64 == 0
|
|
RegObject[cpu][EAX_Rnum] = SIM->get_param_num("EAX", cpu_list);
|
|
RegObject[cpu][EBX_Rnum] = SIM->get_param_num("EBX", cpu_list);
|
|
RegObject[cpu][ECX_Rnum] = SIM->get_param_num("ECX", cpu_list);
|
|
RegObject[cpu][EDX_Rnum] = SIM->get_param_num("EDX", cpu_list);
|
|
RegObject[cpu][ESI_Rnum] = SIM->get_param_num("ESI", cpu_list);
|
|
RegObject[cpu][EDI_Rnum] = SIM->get_param_num("EDI", cpu_list);
|
|
RegObject[cpu][EBP_Rnum] = SIM->get_param_num("EBP", cpu_list);
|
|
RegObject[cpu][ESP_Rnum] = SIM->get_param_num("ESP", cpu_list);
|
|
RegObject[cpu][EIP_Rnum] = SIM->get_param_num("EIP", cpu_list);
|
|
#else
|
|
RegObject[cpu][RAX_Rnum] = SIM->get_param_num("RAX", cpu_list);
|
|
RegObject[cpu][RBX_Rnum] = SIM->get_param_num("RBX", cpu_list);
|
|
RegObject[cpu][RCX_Rnum] = SIM->get_param_num("RCX", cpu_list);
|
|
RegObject[cpu][RDX_Rnum] = SIM->get_param_num("RDX", cpu_list);
|
|
RegObject[cpu][RSI_Rnum] = SIM->get_param_num("RSI", cpu_list);
|
|
RegObject[cpu][RDI_Rnum] = SIM->get_param_num("RDI", cpu_list);
|
|
RegObject[cpu][RBP_Rnum] = SIM->get_param_num("RBP", cpu_list);
|
|
RegObject[cpu][RSP_Rnum] = SIM->get_param_num("RSP", cpu_list);
|
|
RegObject[cpu][RIP_Rnum] = SIM->get_param_num("RIP", cpu_list);
|
|
RegObject[cpu][R8_Rnum] = SIM->get_param_num("R8", cpu_list);
|
|
RegObject[cpu][R9_Rnum] = SIM->get_param_num("R9", cpu_list);
|
|
RegObject[cpu][R10_Rnum] = SIM->get_param_num("R10", cpu_list);
|
|
RegObject[cpu][R11_Rnum] = SIM->get_param_num("R11", cpu_list);
|
|
RegObject[cpu][R12_Rnum] = SIM->get_param_num("R12", cpu_list);
|
|
RegObject[cpu][R13_Rnum] = SIM->get_param_num("R13", cpu_list);
|
|
RegObject[cpu][R14_Rnum] = SIM->get_param_num("R14", cpu_list);
|
|
RegObject[cpu][R15_Rnum] = SIM->get_param_num("R15", cpu_list);
|
|
#endif
|
|
RegObject[cpu][EFL_Rnum] = SIM->get_param_num("EFLAGS", cpu_list);
|
|
RegObject[cpu][CS_Rnum] = SIM->get_param_num("CS.selector", cpu_list);
|
|
RegObject[cpu][DS_Rnum] = SIM->get_param_num("DS.selector", cpu_list);
|
|
RegObject[cpu][ES_Rnum] = SIM->get_param_num("ES.selector", cpu_list);
|
|
RegObject[cpu][SS_Rnum] = SIM->get_param_num("SS.selector", cpu_list);
|
|
RegObject[cpu][FS_Rnum] = SIM->get_param_num("FS.selector", cpu_list);
|
|
RegObject[cpu][GS_Rnum] = SIM->get_param_num("GS.selector", cpu_list);
|
|
RegObject[cpu][GDTRnum] = SIM->get_param_num("GDTR.base", cpu_list);
|
|
RegObject[cpu][GDTR_Lim] = SIM->get_param_num("GDTR.limit", cpu_list);
|
|
RegObject[cpu][IDTRnum] = SIM->get_param_num("IDTR.base", cpu_list);
|
|
RegObject[cpu][IDTR_Lim] = SIM->get_param_num("IDTR.limit", cpu_list);
|
|
RegObject[cpu][LDTRnum] = SIM->get_param_num("LDTR.base", cpu_list);
|
|
RegObject[cpu][TRRnum] = SIM->get_param_num("TR.base", cpu_list);
|
|
RegObject[cpu][CR0_Rnum] = SIM->get_param_num("CR0", cpu_list);
|
|
RegObject[cpu][CR2_Rnum] = SIM->get_param_num("CR2", cpu_list);
|
|
RegObject[cpu][CR3_Rnum] = SIM->get_param_num("CR3", cpu_list);
|
|
#if BX_CPU_LEVEL >= 5
|
|
RegObject[cpu][CR4_Rnum] = SIM->get_param_num("CR4", cpu_list);
|
|
#endif
|
|
#if BX_CPU_LEVEL >= 6
|
|
RegObject[cpu][EFER_Rnum] = SIM->get_param_num("MSR.EFER", cpu_list);
|
|
#endif
|
|
#if BX_SUPPORT_FPU
|
|
RegObject[cpu][ST0_Rnum] = SIM->get_param_num("FPU.st0.fraction", cpu_list);
|
|
RegObject[cpu][ST1_Rnum] = SIM->get_param_num("FPU.st1.fraction", cpu_list);
|
|
RegObject[cpu][ST2_Rnum] = SIM->get_param_num("FPU.st2.fraction", cpu_list);
|
|
RegObject[cpu][ST3_Rnum] = SIM->get_param_num("FPU.st3.fraction", cpu_list);
|
|
RegObject[cpu][ST4_Rnum] = SIM->get_param_num("FPU.st4.fraction", cpu_list);
|
|
RegObject[cpu][ST5_Rnum] = SIM->get_param_num("FPU.st5.fraction", cpu_list);
|
|
RegObject[cpu][ST6_Rnum] = SIM->get_param_num("FPU.st6.fraction", cpu_list);
|
|
RegObject[cpu][ST7_Rnum] = SIM->get_param_num("FPU.st7.fraction", cpu_list);
|
|
RegObject[cpu][ST0_exp] = SIM->get_param_num("FPU.st0.exp", cpu_list);
|
|
RegObject[cpu][ST1_exp] = SIM->get_param_num("FPU.st1.exp", cpu_list);
|
|
RegObject[cpu][ST2_exp] = SIM->get_param_num("FPU.st2.exp", cpu_list);
|
|
RegObject[cpu][ST3_exp] = SIM->get_param_num("FPU.st3.exp", cpu_list);
|
|
RegObject[cpu][ST4_exp] = SIM->get_param_num("FPU.st4.exp", cpu_list);
|
|
RegObject[cpu][ST5_exp] = SIM->get_param_num("FPU.st5.exp", cpu_list);
|
|
RegObject[cpu][ST6_exp] = SIM->get_param_num("FPU.st6.exp", cpu_list);
|
|
RegObject[cpu][ST7_exp] = SIM->get_param_num("FPU.st7.exp", cpu_list);
|
|
#endif
|
|
|
|
#if BX_CPU_LEVEL >= 6
|
|
CpuSupportSSE = SIM->get_param("SSE", cpu_list) != NULL;
|
|
|
|
if (CpuSupportSSE) {
|
|
RegObject[cpu][XMM0_Rnum] = SIM->get_param_num("SSE.xmm00_0", cpu_list);
|
|
RegObject[cpu][XMM1_Rnum] = SIM->get_param_num("SSE.xmm01_0", cpu_list);
|
|
RegObject[cpu][XMM2_Rnum] = SIM->get_param_num("SSE.xmm02_0", cpu_list);
|
|
RegObject[cpu][XMM3_Rnum] = SIM->get_param_num("SSE.xmm03_0", cpu_list);
|
|
RegObject[cpu][XMM4_Rnum] = SIM->get_param_num("SSE.xmm04_0", cpu_list);
|
|
RegObject[cpu][XMM5_Rnum] = SIM->get_param_num("SSE.xmm05_0", cpu_list);
|
|
RegObject[cpu][XMM6_Rnum] = SIM->get_param_num("SSE.xmm06_0", cpu_list);
|
|
RegObject[cpu][XMM7_Rnum] = SIM->get_param_num("SSE.xmm07_0", cpu_list);
|
|
RegObject[cpu][XMM0_hi] = SIM->get_param_num("SSE.xmm00_1", cpu_list);
|
|
RegObject[cpu][XMM1_hi] = SIM->get_param_num("SSE.xmm01_1", cpu_list);
|
|
RegObject[cpu][XMM2_hi] = SIM->get_param_num("SSE.xmm02_1", cpu_list);
|
|
RegObject[cpu][XMM3_hi] = SIM->get_param_num("SSE.xmm03_1", cpu_list);
|
|
RegObject[cpu][XMM4_hi] = SIM->get_param_num("SSE.xmm04_1", cpu_list);
|
|
RegObject[cpu][XMM5_hi] = SIM->get_param_num("SSE.xmm05_1", cpu_list);
|
|
RegObject[cpu][XMM6_hi] = SIM->get_param_num("SSE.xmm06_1", cpu_list);
|
|
RegObject[cpu][XMM7_hi] = SIM->get_param_num("SSE.xmm07_1", cpu_list);
|
|
|
|
#if BX_SUPPORT_X86_64
|
|
RegObject[cpu][XMM8_Rnum] = SIM->get_param_num("SSE.xmm08_0", cpu_list);
|
|
RegObject[cpu][XMM9_Rnum] = SIM->get_param_num("SSE.xmm09_0", cpu_list);
|
|
RegObject[cpu][XMMA_Rnum] = SIM->get_param_num("SSE.xmm10_0", cpu_list);
|
|
RegObject[cpu][XMMB_Rnum] = SIM->get_param_num("SSE.xmm11_0", cpu_list);
|
|
RegObject[cpu][XMMC_Rnum] = SIM->get_param_num("SSE.xmm12_0", cpu_list);
|
|
RegObject[cpu][XMMD_Rnum] = SIM->get_param_num("SSE.xmm13_0", cpu_list);
|
|
RegObject[cpu][XMME_Rnum] = SIM->get_param_num("SSE.xmm14_0", cpu_list);
|
|
RegObject[cpu][XMMF_Rnum] = SIM->get_param_num("SSE.xmm15_0", cpu_list);
|
|
RegObject[cpu][XMM8_hi] = SIM->get_param_num("SSE.xmm08_1", cpu_list);
|
|
RegObject[cpu][XMM9_hi] = SIM->get_param_num("SSE.xmm09_1", cpu_list);
|
|
RegObject[cpu][XMMA_hi] = SIM->get_param_num("SSE.xmm10_1", cpu_list);
|
|
RegObject[cpu][XMMB_hi] = SIM->get_param_num("SSE.xmm11_1", cpu_list);
|
|
RegObject[cpu][XMMC_hi] = SIM->get_param_num("SSE.xmm12_1", cpu_list);
|
|
RegObject[cpu][XMMD_hi] = SIM->get_param_num("SSE.xmm13_1", cpu_list);
|
|
RegObject[cpu][XMME_hi] = SIM->get_param_num("SSE.xmm14_1", cpu_list);
|
|
RegObject[cpu][XMMF_hi] = SIM->get_param_num("SSE.xmm15_1", cpu_list);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
RegObject[cpu][DR0_Rnum] = SIM->get_param_num("DR0", cpu_list);
|
|
RegObject[cpu][DR1_Rnum] = SIM->get_param_num("DR1", cpu_list);
|
|
RegObject[cpu][DR2_Rnum] = SIM->get_param_num("DR2", cpu_list);
|
|
RegObject[cpu][DR3_Rnum] = SIM->get_param_num("DR3", cpu_list);
|
|
RegObject[cpu][DR6_Rnum] = SIM->get_param_num("DR6", cpu_list);
|
|
RegObject[cpu][DR7_Rnum] = SIM->get_param_num("DR7", cpu_list);
|
|
}
|
|
}
|
|
|
|
|
|
void doUpdate()
|
|
{
|
|
void FillStack();
|
|
if (doSimuInit != FALSE)
|
|
SpecialInit();
|
|
// begin an autoupdate of Register and Asm windows
|
|
LoadRegList(); // build and show ListView
|
|
ParseBkpt(); // get the linear breakpoint list
|
|
if (DViewMode == VIEW_STACK) // in stack view mode, keep the stack updated
|
|
FillStack();
|
|
CurrentAsmLA = BX_CPU(CurrentCPU)->get_laddr(BX_SEG_REG_CS, (bx_address) rV[RIP_Rnum]);
|
|
if (CurrentAsmLA < BottomAsmLA || CurrentAsmLA > TopAsmLA)
|
|
{
|
|
Bit64u h = CurrentAsmLA;
|
|
// generate a startLA (= h) that overlaps by topmargin, if possible
|
|
CanDoLA(&h);
|
|
|
|
FillAsm(h, DefaultAsmLines);
|
|
|
|
// Set the scroll position for the new ASM window
|
|
BottomAsmLA = *AsmLA;
|
|
int j = bottommargin; // try to use this bottom margin on ASM window
|
|
if (j > AsmLineCount)
|
|
j = AsmLineCount;
|
|
TopAsmLA = AsmLA[AsmLineCount - j]; //TopAsmLA is the scroll point
|
|
}
|
|
else
|
|
doAsmScroll(); // ASM window may need to scroll
|
|
ResizeColmns = FALSE; // done with reformatting, if it was needed
|
|
UpdateStatus(); // Mode and Eflags may have changed status
|
|
}
|
|
|
|
// Fill the GDT ListView, reading GDT data directly from bochs linear mem
|
|
void FillGDT()
|
|
{
|
|
unsigned int i, j, GroupId;
|
|
unsigned int k = (GDT_Len + 1) / 8;
|
|
Bit8u gdtbuf[8];
|
|
char *cols[18];
|
|
char gdttxt[90];
|
|
doDumpRefresh = FALSE;
|
|
|
|
Bit64u laddr = rV[GDTRnum];
|
|
StartListUpdate(DUMP_WND);
|
|
|
|
*gdttxt = 0;
|
|
cols[0]= gdttxt + 1;
|
|
cols[1]= gdttxt + 30;
|
|
cols[2]= gdttxt + 40;
|
|
cols[3]= gdttxt + 80;
|
|
cols[4]= gdttxt; // columns #5 to 17 are blank
|
|
cols[5]= gdttxt;
|
|
cols[6]= gdttxt;
|
|
cols[7]= gdttxt;
|
|
cols[8]= gdttxt;
|
|
cols[9]= gdttxt;
|
|
cols[10]= gdttxt;
|
|
cols[11]= gdttxt;
|
|
cols[12]= gdttxt;
|
|
cols[13]= gdttxt;
|
|
cols[14]= gdttxt;
|
|
cols[15]= gdttxt;
|
|
cols[16]= gdttxt;
|
|
|
|
for(i = 0; i < k; i++)
|
|
{
|
|
// read 2 dwords from bochs linear mem into "buffer"
|
|
sprintf(cols[0], "%02u (Selector 0x%04X)", i, i << 3);
|
|
if (ReadBxLMem(laddr, 8, gdtbuf) == FALSE) // abort the current GDT dump on a memory error
|
|
{
|
|
cols[1]= gdttxt; // ERROR - blank out cols #2 - 4 for this new row
|
|
cols[2]= gdttxt;
|
|
cols[3]= gdttxt;
|
|
cols[17] = gdttxt + 30;
|
|
strcpy (cols[17],"illegal address");
|
|
InsertListRow(cols, 18, DUMP_WND, i, 8); // 18 cols, "no group"
|
|
RedrawColumns(DUMP_WND);
|
|
EndListUpdate(DUMP_WND);
|
|
break;
|
|
}
|
|
laddr += 8;
|
|
|
|
// enforce proper littleendianness on the gdtbuf bytes
|
|
Bit32u limit = gdtbuf[0] | ((Bit32u) gdtbuf[1] << 8);
|
|
limit |= ((Bit32u)gdtbuf[6] & 0xf) << 16;
|
|
if ((gdtbuf[6] & 0x80) != 0) // 'Granularity' bit = 4K limit multiplier
|
|
limit = limit * 4096 + 4095; // and the bottom 12 bits aren't tested
|
|
|
|
GroupId = 8; // default to "blank" group
|
|
cols[17]= (char*)GDTsT[0]; // default info string is blank
|
|
j = 8; // default GDT type (group) is blank
|
|
if ((gdtbuf[5] & 0x10) == 0) // 'S' bit clear = System segment
|
|
{
|
|
GroupId = 1;
|
|
if (limit == 0 && (gdtbuf[5] & 0x80) == 0) // 'P' (present) bit
|
|
GroupId = 0;
|
|
// point to the approprate info string for the GDT system segment type
|
|
cols[17] = (char*)GDTsT[(int) (gdtbuf[5] & 0xf)];
|
|
}
|
|
else // it's an 'executable' code or data segment
|
|
{
|
|
j = (gdtbuf[6] & 0x60) >> 5; // get the 'L' and 'D/B' bits
|
|
if (j == 3) // both bits set is illegal
|
|
j = 6;
|
|
else if ((gdtbuf[5] & 0x8) == 0)
|
|
j += 3; // data seg -> j= 3 to 5, code seg -> j= 0 to 2
|
|
|
|
#ifndef IS_WIN98
|
|
GroupId = j; // use GroupIDs on XP and higher systems
|
|
#else
|
|
cols[17] = (char*)GDTt2[j]; // otherwise, put descriptive text in "info" column
|
|
#endif
|
|
}
|
|
|
|
// enforce proper littleendianness on the gdtbuf bytes
|
|
Bit32u base = gdtbuf[2] | ((Bit32u) gdtbuf[3] << 8);
|
|
base |= (Bit32u) gdtbuf[4] << 16;
|
|
base |= (Bit32u) gdtbuf[7] << 24;
|
|
|
|
if ((gdtbuf[6] & 0x60) == 0x20) // test for longmode segment
|
|
{
|
|
base = 0; // the base is always 0 in longmode, with "no" limit
|
|
sprintf(cols[2],"0xFFFFFFFFFFFFFFFF");
|
|
}
|
|
else
|
|
sprintf(cols[2],"0x%X",limit);
|
|
sprintf(cols[1],"0x%X",base);
|
|
sprintf(cols[3],"%u", (gdtbuf[5] & 0x60) >> 5);
|
|
|
|
if (i == 0)
|
|
cols[17] = (char*)GDTt2[7]; // call "Null" selector "unused"
|
|
InsertListRow(cols, 18, DUMP_WND, i, GroupId); // 18 cols
|
|
}
|
|
|
|
RedrawColumns(DUMP_WND);
|
|
EndListUpdate(DUMP_WND);
|
|
}
|
|
|
|
// Fills the IDT ListView, reading IDT data directly from bochs linear mem
|
|
void FillIDT()
|
|
{
|
|
Bit64u laddr;
|
|
Bit8u idtbuf[16];
|
|
Bit16u sel;
|
|
Bit32u ofs;
|
|
unsigned entrysize;
|
|
unsigned int i;
|
|
char *cols[18];
|
|
char idttxt[80];
|
|
unsigned int mode = 0;
|
|
if (In32Mode)
|
|
mode = 1;
|
|
if (In64Mode)
|
|
mode = 2;
|
|
doDumpRefresh = FALSE;
|
|
|
|
*idttxt = 0;
|
|
cols[0]= idttxt + 1;
|
|
cols[1]= idttxt; // columns #2 to 17 are blank
|
|
cols[2]= idttxt;
|
|
cols[3]= idttxt;
|
|
cols[4]= idttxt;
|
|
cols[5]= idttxt;
|
|
cols[6]= idttxt;
|
|
cols[7]= idttxt;
|
|
cols[8]= idttxt;
|
|
cols[9]= idttxt;
|
|
cols[10]= idttxt;
|
|
cols[11]= idttxt;
|
|
cols[12]= idttxt;
|
|
cols[13]= idttxt;
|
|
cols[14]= idttxt;
|
|
cols[15]= idttxt;
|
|
cols[16]= idttxt;
|
|
cols[17]= idttxt + 10;
|
|
entrysize = 4 << mode; // calculate the bytesize of the entries
|
|
unsigned int k = (IDT_Len + 1) / entrysize;
|
|
StartListUpdate(DUMP_WND);
|
|
|
|
// recover the IDT linear base address
|
|
laddr = rV[IDTRnum];
|
|
|
|
if (k > 256) // if IDT_Len is unreasonably large, set a reasonable maximum
|
|
k = 256;
|
|
for(i = 0; i < k; i++)
|
|
{
|
|
idttxt[1] = AsciiHex[2*i];
|
|
idttxt[2] = AsciiHex[2*i+1];
|
|
idttxt[3] = 0;
|
|
if (ReadBxLMem(laddr, entrysize, idtbuf) == FALSE) // abort the current IDT dump on a memory error
|
|
{
|
|
strcpy (cols[17],"illegal address");
|
|
InsertListRow(cols, 18, DUMP_WND, i, 8); // 18 cols, "no group"
|
|
RedrawColumns(DUMP_WND);
|
|
EndListUpdate(DUMP_WND);
|
|
break;
|
|
}
|
|
laddr += entrysize;
|
|
// enforce proper littleendianness on the idtbuf bytes
|
|
ofs = idtbuf[0] | ((Bit32u) idtbuf[1] << 8);
|
|
sel = idtbuf[2] | ((Bit16u) idtbuf[3] << 8);
|
|
|
|
switch (mode)
|
|
{
|
|
case 0: // Real Mode
|
|
sprintf(cols[17],"0x%04X:0x%04X", sel, ofs);
|
|
break;
|
|
|
|
case 1: // Pmode
|
|
ofs |= ((Bit32u) idtbuf[6] << 16) | ((Bit32u) idtbuf[7] << 24);
|
|
sprintf(cols[17],"0x%04X:0x%08X", sel, ofs);
|
|
// TODO: also print some flags from idtbuf[5], maybe, in another column
|
|
break;
|
|
|
|
case 2: // Lmode
|
|
Bit64u off64 = (Bit64u)(ofs | ((Bit32u) idtbuf[6] << 16) | ((Bit32u) idtbuf[7] << 24));
|
|
off64 |= ((Bit64u) idtbuf[8] << 32) | ((Bit64u) idtbuf[9] << 40);
|
|
off64 |= ((Bit64u) idtbuf[10] << 48) | ((Bit64u) idtbuf[11] << 56);
|
|
sprintf(cols[17],"0x%04X:0x" FMT_LLCAPX, sel, off64);
|
|
// TODO: also print some flags from idtbuf[5], maybe, in another column
|
|
break;
|
|
}
|
|
|
|
InsertListRow(cols, 18, DUMP_WND, i, 8); // 18 cols, "no group"
|
|
}
|
|
RedrawColumns(DUMP_WND);
|
|
EndListUpdate(DUMP_WND);
|
|
}
|
|
|
|
|
|
// insert one entry into the Paging data list (a linear and a physical addy)
|
|
void AddPagingLine(int LC, char *pa_lin, char *pa_phy)
|
|
{
|
|
char zero = 0;
|
|
char *cols[18];
|
|
cols[0]= pa_lin;
|
|
cols[1]= &zero; // columns #2 to 17 are blank
|
|
cols[2]= &zero;
|
|
cols[3]= &zero;
|
|
cols[4]= &zero;
|
|
cols[5]= &zero;
|
|
cols[6]= &zero;
|
|
cols[7]= &zero;
|
|
cols[8]= &zero;
|
|
cols[9]= &zero;
|
|
cols[10]= &zero;
|
|
cols[11]= &zero;
|
|
cols[12]= &zero;
|
|
cols[13]= &zero;
|
|
cols[14]= &zero;
|
|
cols[15]= &zero;
|
|
cols[16]= &zero;
|
|
cols[17]= pa_phy;
|
|
InsertListRow(cols, 18, DUMP_WND, LC, 8); // 18 cols, "no group"
|
|
}
|
|
|
|
// lifted from bx_dbg_dump_table in dbg_main of the internal debugger
|
|
void FillPAGE()
|
|
{
|
|
Bit32u lin, start_lin, curlin; // show only low 32 bit
|
|
bx_phy_address phy;
|
|
Bit64u start_phy, phy64;
|
|
int LineCount = 0;
|
|
char pa_lin[50];
|
|
char pa_phy[50];
|
|
doDumpRefresh = FALSE;
|
|
|
|
StartListUpdate(DUMP_WND);
|
|
curlin = lin = 0; // always start at linear address 0
|
|
start_lin = 1; // force a mismatch on the first line
|
|
start_phy = 2;
|
|
while (LineCount < 1024 && curlin != 0xfffff000)
|
|
{
|
|
// get translation lin -> phys, and verify mapping is legal
|
|
if (BX_CPU(CurrentCPU)->dbg_xlate_linear2phy(lin, &phy) != FALSE)
|
|
{
|
|
phy64 = phy;
|
|
if ((lin - start_lin) != (phy64 - start_phy))
|
|
{
|
|
if (start_lin != 1)
|
|
{
|
|
sprintf (pa_lin,"0x%08X - 0x%08X",start_lin, lin - 1);
|
|
sprintf (pa_phy,"0x" FMT_LLCAPX " - 0x" FMT_LLCAPX,
|
|
start_phy, start_phy + (lin-1-start_lin));
|
|
AddPagingLine (LineCount,pa_lin,pa_phy);
|
|
++LineCount;
|
|
}
|
|
start_lin = lin;
|
|
start_phy = phy64;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (start_lin != 1)
|
|
{
|
|
sprintf (pa_lin,"0x%08X - 0x%08X",start_lin, lin - 1);
|
|
sprintf (pa_phy,"0x" FMT_LLCAPX " - 0x" FMT_LLCAPX,
|
|
start_phy, start_phy + (lin-1-start_lin));
|
|
AddPagingLine (LineCount,pa_lin,pa_phy);
|
|
++LineCount;
|
|
}
|
|
start_lin = 1;
|
|
start_phy = 2;
|
|
}
|
|
curlin = lin;
|
|
lin += 0x1000; // then test the next 4K page in the loop
|
|
}
|
|
if (start_lin != 1) // need to output one last line?
|
|
{
|
|
sprintf (pa_lin,"0x%08X - 0x%08X", start_lin, -1);
|
|
sprintf (pa_phy,"0x" FMT_LLCAPX " - 0x" FMT_LLCAPX,start_phy, start_phy + (lin-1-start_lin));
|
|
AddPagingLine (LineCount,pa_lin,pa_phy);
|
|
}
|
|
RedrawColumns(DUMP_WND);
|
|
EndListUpdate(DUMP_WND);
|
|
}
|
|
|
|
// build the stack display
|
|
void FillStack()
|
|
{
|
|
// sometimes need to specially invalidate the stack window
|
|
static bx_bool StkInvOnce = FALSE;
|
|
Bit64u StackLA, EndLA;
|
|
unsigned int len, i, wordsize, overlap;
|
|
int j;
|
|
bx_bool LglAddy;
|
|
bx_bool UpdateDisp;
|
|
char *cp, *cpp;
|
|
char *cols[18];
|
|
char stktxt[120];
|
|
|
|
*stktxt = 0;
|
|
cols[0]= stktxt + 1;
|
|
cols[1]= stktxt + 40;
|
|
cols[2]= stktxt; // columns #3 to 17 are blank
|
|
cols[3]= stktxt;
|
|
cols[4]= stktxt;
|
|
cols[5]= stktxt;
|
|
cols[6]= stktxt;
|
|
cols[7]= stktxt;
|
|
cols[8]= stktxt;
|
|
cols[9]= stktxt;
|
|
cols[10]= stktxt;
|
|
cols[11]= stktxt;
|
|
cols[12]= stktxt;
|
|
cols[13]= stktxt;
|
|
cols[14]= stktxt;
|
|
cols[15]= stktxt;
|
|
cols[16]= stktxt;
|
|
cols[17]= stktxt + 80;
|
|
doDumpRefresh = FALSE;
|
|
StackLA = (Bit64u) BX_CPU(CurrentCPU)->get_laddr(BX_SEG_REG_SS, (bx_address) rV[RSP_Rnum]);
|
|
|
|
if (PStackLA == 1) // illegal value requests a full refresh
|
|
PStackLA = StackLA ^ 0x4000; // force a non-match below (kludge)
|
|
|
|
wordsize = 4; // assume Pmode
|
|
if (In32Mode == FALSE)
|
|
wordsize = 2;
|
|
else if (In64Mode)
|
|
wordsize = 8;
|
|
len = STACK_ENTRIES * wordsize;
|
|
|
|
// TODO: enforce that cp is wordsize aligned
|
|
// also -- enforce that StackLA is wordsize aligned
|
|
cp = CurStack;
|
|
i = (unsigned) StackLA & 0xfff; // where is stack bottom, in its 4K memory page?
|
|
if (i > 0x1000 - len) // does len cross a 4K boundary?
|
|
{
|
|
unsigned int ReadSize = 0x1000 - i;
|
|
// read up to the 4K boundary, then try to read the last chunk
|
|
if (ReadBxLMem(StackLA, ReadSize, (Bit8u *) cp) == FALSE)
|
|
{
|
|
// no data to show -- just one error message
|
|
sprintf (cols[17],"illegal address");
|
|
StartListUpdate(DUMP_WND);
|
|
InsertListRow(cols, 18, DUMP_WND, 0, 8); // 18 cols, "no group"
|
|
EndListUpdate(DUMP_WND);
|
|
return;
|
|
}
|
|
LglAddy = ReadBxLMem(StackLA + ReadSize, len + i - 0x1000, (Bit8u *) cp + ReadSize);
|
|
if (LglAddy == FALSE)
|
|
len = ReadSize;
|
|
}
|
|
else
|
|
ReadBxLMem(StackLA, len, (Bit8u *) cp);
|
|
|
|
UpdateDisp = CpuModeChange; // calculate which stack entries have changed
|
|
cp = CurStack;
|
|
cpp = PrevStack;
|
|
j = overlap = len / wordsize;
|
|
while (--j >= 0)
|
|
StackEntChg[j] = TRUE; // assume that all lines have changed
|
|
if (PStackLA > StackLA) // calculate the overlap between the prev and current stacks
|
|
{
|
|
EndLA = PStackLA - StackLA;
|
|
if (EndLA < len)
|
|
{
|
|
i = (unsigned int) (EndLA / wordsize);
|
|
cp += i * wordsize;
|
|
}
|
|
else
|
|
i = overlap; // force the next loop to exit
|
|
}
|
|
else
|
|
{
|
|
EndLA = StackLA - PStackLA;
|
|
if (EndLA < len)
|
|
{
|
|
i = 0;
|
|
j = (int) (EndLA / wordsize);
|
|
cpp += j * wordsize;
|
|
overlap -= j;
|
|
}
|
|
else
|
|
i = overlap; // force the next loop to exit
|
|
}
|
|
while (i < overlap)
|
|
{
|
|
j = wordsize; // if the two entries match, cancel the EntryChange flag for that entry
|
|
while (j > 0 && *cp == *cpp)
|
|
{
|
|
--j;
|
|
++cp;
|
|
++cpp;
|
|
}
|
|
if (j == 0)
|
|
StackEntChg[i] = FALSE; // got a match on all bytes
|
|
else
|
|
{
|
|
cp += j; // bump the pointers to the next stack entry
|
|
cpp += j;
|
|
}
|
|
++i;
|
|
}
|
|
j = len / wordsize;
|
|
while (--j >= 0)
|
|
UpdateDisp |= StackEntChg[j];
|
|
if (UpdateDisp == FALSE) // Don't need to update the list? (no changes?)
|
|
{
|
|
if (StkInvOnce == FALSE)
|
|
Invalidate(DUMP_WND); // Invalidate ONCE to turn off all the red stuff
|
|
StkInvOnce = TRUE;
|
|
return;
|
|
}
|
|
StartListUpdate(DUMP_WND);
|
|
StkInvOnce = FALSE;
|
|
PStackLA = StackLA;
|
|
cp = CurStack;
|
|
cpp = PrevStack;
|
|
j = len;
|
|
while (--j >= 0)
|
|
*(cpp++)= *(cp++); // copy the stack to the Prev buffer
|
|
j= STACK_ENTRIES * 8 - len;
|
|
while (--j >= 0)
|
|
*(cpp++)= 0; // zero out the unused tail end of the prev buffer
|
|
cp = CurStack; // the following display loop runs on the cp pointer
|
|
|
|
EndLA = StackLA + len - 1;
|
|
i = 0;
|
|
while (StackLA < EndLA)
|
|
{
|
|
if (In64Mode == FALSE)
|
|
{
|
|
int tmp;
|
|
sprintf (cols[0],Fmt32b[1],StackLA);
|
|
if (In32Mode == FALSE)
|
|
tmp = *((Bit16s *) cp);
|
|
else
|
|
tmp = *((Bit32s *) cp);
|
|
sprintf (cols[1],Fmt32b[UprCase],tmp);
|
|
sprintf (cols[17],"%d",tmp);
|
|
}
|
|
else
|
|
{
|
|
Bit64s tmp = *((Bit64s *) cp);
|
|
sprintf (cols[0],Fmt64b[UprCase],StackLA);
|
|
sprintf (cols[1],Fmt64b[1],tmp);
|
|
sprintf (cols[17], FMT_LL "d",tmp);
|
|
}
|
|
InsertListRow(cols, 18, DUMP_WND, i, 8); // 18 cols, "no group"
|
|
StackLA += wordsize;
|
|
cp += wordsize;
|
|
++i;
|
|
}
|
|
|
|
RedrawColumns(DUMP_WND);
|
|
EndListUpdate(DUMP_WND);
|
|
}
|
|
|
|
// utility function to print breakpoints in a unified way
|
|
void prtbrk (Bit32u seg, Bit64u addy, unsigned int id, bx_bool enabled, char *cols[])
|
|
{
|
|
int i = 0;
|
|
if (enabled == FALSE)
|
|
*cols[1] = 'n';
|
|
else
|
|
*cols[1] = 'y';
|
|
sprintf (cols[17],"%u",id);
|
|
if (seg <= 0xffff){
|
|
i= 5;
|
|
sprintf (cols[0],"%04X:",seg);
|
|
}
|
|
|
|
sprintf (cols[0] + i,FMT_LLCAPX,addy);
|
|
}
|
|
|
|
// Displays all Breakpoints and Watchpoints
|
|
void FillBrkp()
|
|
{
|
|
int LineCount, totqty, i;
|
|
char *cols[18];
|
|
char brktxt[60];
|
|
unsigned int brktype;
|
|
extern bx_guard_t bx_guard;
|
|
|
|
doDumpRefresh = FALSE;
|
|
|
|
*brktxt = 0;
|
|
brktxt[49] = 0; // 0 terminate the "enabled" string
|
|
cols[2]= brktxt; // columns #3 to 17 are blank
|
|
cols[3]= brktxt;
|
|
cols[4]= brktxt;
|
|
cols[5]= brktxt;
|
|
cols[6]= brktxt;
|
|
cols[7]= brktxt;
|
|
cols[8]= brktxt;
|
|
cols[9]= brktxt;
|
|
cols[10]= brktxt;
|
|
cols[11]= brktxt;
|
|
cols[12]= brktxt;
|
|
cols[13]= brktxt;
|
|
cols[14]= brktxt;
|
|
cols[15]= brktxt;
|
|
cols[16]= brktxt;
|
|
StartListUpdate(DUMP_WND);
|
|
i = 256;
|
|
while (--i >= 0)
|
|
BrkpIDMap[i] = 0;
|
|
i = 16;
|
|
while (--i >= 0)
|
|
{
|
|
WWP_Snapshot[i] = 0;
|
|
RWP_Snapshot[i] = 0;
|
|
}
|
|
|
|
LineCount = 0;
|
|
for (brktype = 0; brktype < 5; brktype++)
|
|
{
|
|
cols[0]= brktxt;
|
|
cols[1]= brktxt;
|
|
cols[17]= brktxt;
|
|
InsertListRow(cols, 18, DUMP_WND, LineCount++, 8); // make a blank row
|
|
cols[0]= (char*)BrkName[brktype];
|
|
InsertListRow(cols, 18, DUMP_WND, LineCount++, 8); // brkpt "type" as only text on row
|
|
cols[0]= brktxt + 1;
|
|
if (brktype < 3)
|
|
{
|
|
cols[17]= brktxt + 50; // only breakpoints have IDs
|
|
cols[1]= brktxt + 48; // and can be "enabled"
|
|
if (brktype == 0)
|
|
{
|
|
#if (BX_DBG_MAX_LIN_BPOINTS > 0)
|
|
totqty = bx_guard.iaddr.num_linear;
|
|
for (i = 0; i < totqty; i++)
|
|
{
|
|
BrkpIDMap[LineCount] = bx_guard.iaddr.lin[i].bpoint_id;
|
|
prtbrk (0xf0000, (Bit64u) bx_guard.iaddr.lin[i].addr,
|
|
(unsigned) BrkpIDMap[LineCount],
|
|
bx_guard.iaddr.lin[i].enabled, cols);
|
|
InsertListRow(cols, 18, DUMP_WND, LineCount++, 8);
|
|
}
|
|
#endif
|
|
EndLinEntry = LineCount;
|
|
}
|
|
else if (brktype == 1)
|
|
{
|
|
#if (BX_DBG_MAX_PHY_BPOINTS > 0)
|
|
totqty = bx_guard.iaddr.num_physical;
|
|
for (i = 0; i < totqty; i++)
|
|
{
|
|
BrkpIDMap[LineCount] = bx_guard.iaddr.phy[i].bpoint_id;
|
|
prtbrk (0xf0000, (Bit64u) bx_guard.iaddr.phy[i].addr,
|
|
(unsigned) BrkpIDMap[LineCount],
|
|
bx_guard.iaddr.phy[i].enabled, cols);
|
|
InsertListRow(cols, 18, DUMP_WND, LineCount++, 8);
|
|
}
|
|
#endif
|
|
EndPhyEntry = LineCount;
|
|
}
|
|
else
|
|
{
|
|
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
|
|
totqty = bx_guard.iaddr.num_virtual;
|
|
for (i = 0; i < totqty; i++)
|
|
{
|
|
BrkpIDMap[LineCount] = bx_guard.iaddr.vir[i].bpoint_id;
|
|
prtbrk (bx_guard.iaddr.vir[i].cs,
|
|
(Bit64u) bx_guard.iaddr.vir[i].eip,
|
|
(unsigned) BrkpIDMap[LineCount],
|
|
bx_guard.iaddr.vir[i].enabled, cols);
|
|
InsertListRow(cols, 18, DUMP_WND, LineCount++, 8);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else if (brktype == 3)
|
|
{
|
|
WWP_BaseEntry = LineCount;
|
|
totqty = num_write_watchpoints;
|
|
WWPSnapCount = num_write_watchpoints;
|
|
for (i = 0; i < totqty; i++)
|
|
{
|
|
WWP_Snapshot[i] = write_watchpoint[i].addr;
|
|
sprintf (cols[0], FMT_PHY_ADDRX, (bx_phy_address) write_watchpoint[i].addr);
|
|
InsertListRow(cols, 18, DUMP_WND, LineCount++, 8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RWP_BaseEntry = LineCount;
|
|
totqty = num_read_watchpoints;
|
|
RWPSnapCount = num_read_watchpoints;
|
|
for (i = 0; i < totqty; i++)
|
|
{
|
|
RWP_Snapshot[i] = read_watchpoint[i].addr;
|
|
sprintf (cols[0], FMT_PHY_ADDRX, (bx_phy_address) read_watchpoint[i].addr);
|
|
InsertListRow(cols, 18, DUMP_WND, LineCount++, 8);
|
|
}
|
|
}
|
|
}
|
|
RedrawColumns(DUMP_WND);
|
|
EndListUpdate(DUMP_WND);
|
|
}
|
|
|
|
// performs endian byteswapping the hard way, for a Data dump
|
|
void FillDataX(char* t, char C, bx_bool doHex)
|
|
{
|
|
char tmpbuf[40];
|
|
char *d = tmpbuf;
|
|
if (isLittleEndian == FALSE || doHex == FALSE)
|
|
d = t + strlen(t); // bigendian can always be appended directly
|
|
*d = C;
|
|
d[1] = 0;
|
|
if (isprint(C) == 0)
|
|
*d = '.';
|
|
|
|
if (doHex != FALSE)
|
|
{
|
|
*d = AsciiHex[2* (unsigned char)C];
|
|
d[1] = AsciiHex[2* (unsigned char)C + 1];
|
|
d[2] = 0;
|
|
if (isLittleEndian) // little endian => reverse hex digits
|
|
{
|
|
strcat(d,t);
|
|
strcpy(t,d); // so append the new bytes to the FRONT of t
|
|
}
|
|
}
|
|
}
|
|
|
|
// do the ShowData display work asynchronously, as a thread
|
|
void ShowData()
|
|
{
|
|
unsigned int i;
|
|
char *x;
|
|
char *cols[18];
|
|
char mdtxt[200];
|
|
char tmphex[40];
|
|
|
|
*mdtxt = 0;
|
|
cols[0]= mdtxt + 1; // the amount of storage needed for each column is complicated
|
|
cols[1]= mdtxt + 20;
|
|
cols[2]= mdtxt + 60;
|
|
cols[3]= mdtxt + 64;
|
|
cols[4]= mdtxt + 70;
|
|
cols[5]= mdtxt + 74;
|
|
cols[6]= mdtxt + 84;
|
|
cols[7]= mdtxt + 88;
|
|
cols[8]= mdtxt + 94;
|
|
cols[9]= mdtxt + 100;
|
|
cols[10]= mdtxt + 120;
|
|
cols[11]= mdtxt + 124;
|
|
cols[12]= mdtxt + 130;
|
|
cols[13]= mdtxt + 134;
|
|
cols[14]= mdtxt + 144;
|
|
cols[15]= mdtxt + 148;
|
|
cols[16]= mdtxt + 154;
|
|
cols[17]= mdtxt + 160;
|
|
doDumpRefresh = FALSE;
|
|
StartListUpdate(DUMP_WND);
|
|
|
|
x = DataDump; // data dumps are ALWAYS 4K
|
|
for(i = 0; i < 4096; i += 16)
|
|
{
|
|
if (In64Mode == FALSE)
|
|
sprintf(cols[0],"0x%08X",(Bit32u) (DumpStart + i));
|
|
else
|
|
sprintf(cols[0],"0x" FMT_LLCAPX,DumpStart + i);
|
|
|
|
*tmphex = 0;
|
|
*cols[17] = 0;
|
|
for(unsigned y = 0; y < 16; y++)
|
|
{
|
|
if ((DumpInAsciiMode & 1) != 0)
|
|
// verify the char is printable, then append it to the "ascii" column
|
|
FillDataX(cols[17],x[y],FALSE);
|
|
|
|
if ((DumpInAsciiMode & 2) != 0)
|
|
{
|
|
// convert char to hex, build "endian" hex value, 2 digits at a time
|
|
FillDataX(tmphex,x[y],TRUE);
|
|
if (((y + 1) & (DumpAlign - 1)) == 0)
|
|
{
|
|
strcpy (cols[y+2-DumpAlign], tmphex);
|
|
*tmphex = 0; // FillDataX APPENDS, so you need to clear the buffer
|
|
}
|
|
}
|
|
}
|
|
InsertListRow(cols, 18, DUMP_WND, i>>4, 8); // 18 cols, list2, "no group"
|
|
x+= 16; // bump to the next row of data
|
|
}
|
|
|
|
RedrawColumns(DUMP_WND);
|
|
EndListUpdate(DUMP_WND);
|
|
}
|
|
|
|
|
|
// build Register "display" names from lower case names
|
|
// (must build the pointer list while building the names)
|
|
void MakeRDnames()
|
|
{
|
|
char *p = RDispName[0]; // first storage location
|
|
for (int i=0; i <= EFER_Rnum; i++)
|
|
{
|
|
RDispName[i] = p; // create the Name pointer
|
|
const char *c = RegLCName[i]; // Register name in lower case
|
|
|
|
if (UprCase != 0)
|
|
{
|
|
while (*c != 0)
|
|
*(p++) = UCtable[(int) *(c++)]; // use lookup tbl for uppercase
|
|
}
|
|
else
|
|
{
|
|
while (*c != 0)
|
|
*(p++) = *(c++);
|
|
}
|
|
*(p++) = 0;
|
|
}
|
|
}
|
|
|
|
// generic initialization routine -- called once, only at startup
|
|
void DoAllInit()
|
|
{
|
|
char *p;
|
|
int i;
|
|
|
|
doSimuInit = TRUE;
|
|
CpuModeChange = TRUE;
|
|
CurrentCPU = 0; // need to init CPU info once only
|
|
if (SingleCPU == FALSE)
|
|
TotCPUs = BX_SMP_PROCESSORS;
|
|
else
|
|
TotCPUs = 1;
|
|
|
|
// divide up the pre-allocated char buffer into smaller pieces
|
|
p = bigbuf + outbufSIZE; // point at the end of preallocated mem
|
|
p -= 200; // 200 bytes is enough for all the register names
|
|
RDispName[0] = p;
|
|
p -= 4096;
|
|
DataDump = p; // storage for 4K memory dumps
|
|
p -= OutWinCnt; // 10K for Output Window buffer
|
|
OutWindow = p;
|
|
i = 64;
|
|
while (--i >= 0)
|
|
{
|
|
p -= 80; // command history buffers are 80b each
|
|
CmdHistory[i] = p; // set up 64 of them (5120b)
|
|
*p = 0; // and clear each one
|
|
}
|
|
p -= STACK_ENTRIES * 8; // usually a 400 byte buffer for the stack values
|
|
PrevStack = p;
|
|
p -= STACK_ENTRIES * 8; // and another one
|
|
CurStack = p;
|
|
p -= 512;
|
|
p -= 512; // 2 "hex" bytes per byte value
|
|
tmpcb = p;
|
|
|
|
i = TOT_REG_NUM; // fake up a color table -- there are just enough, currently
|
|
int j = 7; // color 7 = orange
|
|
while (i > 0)
|
|
{
|
|
// change color when the loop goes below the base register number
|
|
if (i == DR0_Rnum) --j; // 6 Debug
|
|
else if (i == XMM0_Rnum) --j; // 8 or 16 XMM
|
|
else if (i == ST0_Rnum) --j; // 8 MMX/FPU
|
|
else if (i == CR0_Rnum) --j; // EFER and CR
|
|
else if (i == GDTRnum) --j; // Sys Registers
|
|
else if (i == CS_Rnum) --j; // Segments
|
|
else if (i == EAX_Rnum) --j; // GP Registers (32b)
|
|
// below EAX is 64bit GP Registers and EFLAGS
|
|
RegColor[--i] = j;
|
|
}
|
|
|
|
MakeXlatTables(); // create UpperCase and AsciiHex translation tables
|
|
MakeRDnames(); // create Rnames from lower-case register names
|
|
InitRegObjects(); // get/store all the bx_param_num_c objects for the registers
|
|
}
|
|
|
|
// refill whichever "data window" is active -- or param_tree
|
|
void RefreshDataWin()
|
|
{
|
|
switch (DViewMode)
|
|
{
|
|
case VIEW_MEMDUMP:
|
|
if (DumpInitted != FALSE)
|
|
ShowData();
|
|
else
|
|
EndListUpdate(2); // list is empty, so end (show) it!
|
|
break;
|
|
case VIEW_GDT:
|
|
FillGDT();
|
|
break;
|
|
case VIEW_IDT:
|
|
FillIDT();
|
|
break;
|
|
case VIEW_PAGING:
|
|
FillPAGE();
|
|
break;
|
|
case VIEW_STACK:
|
|
PStackLA = 1; // flag to force a full stack refresh
|
|
FillStack();
|
|
break;
|
|
case VIEW_BREAK:
|
|
FillBrkp();
|
|
break;
|
|
case VIEW_PTREE:
|
|
FillPTree();
|
|
}
|
|
}
|
|
|
|
// performs tasks whenever the simulation "breaks"
|
|
void OnBreak()
|
|
{
|
|
int i = EFER_Rnum + 1;
|
|
// check if Ptime has changed
|
|
TakeInputFocus();
|
|
|
|
// display the new ptime on the status bar
|
|
char time_buf[20];
|
|
sprintf (time_buf,"t= " FMT_LL "d", bx_pc_system.time_ticks());
|
|
SetStatusText (2, time_buf);
|
|
|
|
// remember register values from before the last run
|
|
while (--i >= 0)
|
|
PV[i] = rV[i];
|
|
ladrmin = ladrmax; // invalidate any old linear->phys mapping
|
|
|
|
// then detect current CPU mode the *right* way -- look for changes
|
|
// TODO: create param Objects for CS.d_b and cpu_mode for each CPU
|
|
unsigned PrevCpuMode = CpuMode;
|
|
CpuMode = BX_CPU(CurrentCPU)->get_cpu_mode();
|
|
if (CpuMode == BX_MODE_LONG_64)
|
|
{
|
|
if (In64Mode == FALSE) // Entering LongMode?
|
|
{
|
|
CpuModeChange = TRUE;
|
|
In64Mode = TRUE;
|
|
In32Mode = TRUE; // In32Mode must be TRUE in LongMode
|
|
ResizeColmns = TRUE; // if so, some formatting has changed
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bx_bool d_b = BX_CPU(CurrentCPU)->sregs[BX_SEG_REG_CS].cache.u.segment.d_b;
|
|
if (In32Mode != d_b || In64Mode != FALSE || PrevCpuMode != CpuMode)
|
|
{
|
|
CpuModeChange = TRUE;
|
|
In64Mode = FALSE;
|
|
In32Mode = d_b;
|
|
}
|
|
}
|
|
if (CpuModeChange)
|
|
{
|
|
GrayMenuItem ((int) In64Mode, CMD_EREG);
|
|
BottomAsmLA = ~0; // force an ASM autoload
|
|
StatusChange = TRUE;
|
|
}
|
|
doUpdate(); // do a full "autoupdate"
|
|
if (doDumpRefresh != FALSE)
|
|
RefreshDataWin();
|
|
}
|
|
|
|
static int HexFromAsk(const char* ask, char* b) // this routine converts a user-typed hex string into binary bytes
|
|
{ // it ignores any bigendian issues -- binary is converted front to end as chars
|
|
int y = 0;
|
|
int i = 0;
|
|
for(;;)
|
|
{
|
|
unsigned int C = 0;
|
|
if (strlen(ask + i) < 2)
|
|
break;
|
|
if (!sscanf(ask + i,"%02X",&C))
|
|
break;
|
|
b[y++] = C;
|
|
i += 2;
|
|
}
|
|
return y;
|
|
}
|
|
|
|
static bx_bool FindHex(const unsigned char* b1,int bs,const unsigned char* b2,int by)
|
|
{
|
|
// search bs bytes of b1
|
|
for(int i = 0; i < bs; i++) // TODO: this loop could be a little more efficient.
|
|
{ // -- it just scans an input byte string against DataDump memory
|
|
bx_bool Match = TRUE;
|
|
for(int y = 0; y < by; y++)
|
|
{
|
|
if (b1[i + y] != b2[y])
|
|
{
|
|
Match = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (Match != FALSE)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bx_bool AskText(const char *title, const char *prompt, char *DefaultText)
|
|
{
|
|
ask_str.title= title;
|
|
ask_str.prompt= prompt;
|
|
ask_str.reply= DefaultText;
|
|
return ShowAskDialog();
|
|
}
|
|
|
|
// load new memory for a MemDump
|
|
// newDS = illegal (1) is a flag to ask the user for a DumpStart address
|
|
bx_bool InitDataDump(bx_bool isLinear, Bit64u newDS)
|
|
{
|
|
bx_bool retval = TRUE;
|
|
bx_bool MsgOnErr = FALSE;
|
|
if (AtBreak == FALSE)
|
|
return FALSE;
|
|
if (((int) newDS & 0xf) != 0) // legal addys must be on 16byte boundary
|
|
{
|
|
if (In64Mode == FALSE)
|
|
sprintf(tmpcb,"0x%X",(Bit32u) DumpStart);
|
|
else
|
|
sprintf(tmpcb,"0x" FMT_LL "X",DumpStart);
|
|
if (AskText("4K Memory Dump","4K Memory Dump -- Enter Address (use 0x for hex):",tmpcb) == FALSE)
|
|
return FALSE;
|
|
|
|
newDS = cvt64(tmpcb,FALSE); // input either hex or decimal
|
|
newDS &= ~15; // force Mem Dump to be 16b aligned
|
|
MsgOnErr = TRUE;
|
|
}
|
|
|
|
// load 4k DataDump array from bochs emulated linear or physical memory
|
|
if (isLinear)
|
|
{
|
|
// cannot read linear mem across a 4K boundary -- so break the read in two
|
|
// -- calculate location of 4K boundary (h):
|
|
unsigned len = (int) newDS & 0xfff;
|
|
unsigned i = 4096 - len;
|
|
Bit64u h = newDS + i;
|
|
retval = ReadBxLMem(newDS,i,(Bit8u *)DataDump);
|
|
if (retval != FALSE && len != 0)
|
|
retval = ReadBxLMem(h,len,(Bit8u *)DataDump + i);
|
|
}
|
|
else {
|
|
retval = (bx_bool) bx_mem.dbg_fetch_mem(BX_CPU(CurrentCPU),
|
|
(bx_phy_address)newDS, 4096, (Bit8u *)DataDump);
|
|
}
|
|
if (retval == FALSE)
|
|
{
|
|
// assume that the DataDump array is still valid -- fetch_mem should error without damage
|
|
if (MsgOnErr != FALSE)
|
|
DispMessage ("Address range was not legal memory","Memory Error");
|
|
return retval;
|
|
}
|
|
|
|
SA_valid = FALSE; // any previous MemDump click is now irrelevant
|
|
ResizeColmns = TRUE; // autosize column 0 once
|
|
DumpInitted = TRUE; // OK to refresh the Dump window in the future (it has data)
|
|
DumpStart = newDS;
|
|
LinearDump = isLinear; // finalize dump mode, since it worked
|
|
ShowMemData(TRUE); // Display DataDump using these new parameters/data
|
|
return TRUE;
|
|
}
|
|
|
|
// User is changing which registers are displaying in the Register list
|
|
void ToggleSeeReg(int cmd)
|
|
{
|
|
int i = cmd - CMD_EREG;
|
|
if (i < 0 || i > 7)
|
|
return;
|
|
if (i == 4 || i == 5)
|
|
ResizeColmns = TRUE; // may need to resize the register value column
|
|
|
|
SeeReg[i] ^= TRUE;
|
|
SetMenuCheckmark ((int) SeeReg[i], i + CHK_CMD_EREG);
|
|
if (AtBreak != FALSE)
|
|
LoadRegList(); // do a register window update
|
|
}
|
|
|
|
void doNewWSize(int i)
|
|
{
|
|
// DumpAlign is the "wordsize" in bytes -- need to "calculate" the power of 2
|
|
int j = 0;
|
|
if (DumpAlign == 2) j = 1;
|
|
else if (DumpAlign == 4) j = 2;
|
|
else if (DumpAlign == 8) j = 3;
|
|
else if (DumpAlign == 16) j = 4;
|
|
if (j != i)
|
|
{
|
|
ToggleWSchecks(i, j);
|
|
DumpWSIndex = i;
|
|
DumpAlign = 1<<i;
|
|
if (DViewMode == VIEW_MEMDUMP && DumpInitted != FALSE)
|
|
{
|
|
if (AtBreak == FALSE)
|
|
doDumpRefresh = TRUE;
|
|
else
|
|
ShowData();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToggleGDT()
|
|
{
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
GrayMenuItem (0, CMD_WPTWR);
|
|
GrayMenuItem (0, CMD_WPTRD);
|
|
if (DViewMode == VIEW_GDT || /*(GDT_Len & 7) != 7 ||*/ (unsigned) GDT_Len >= 0x10000)
|
|
{
|
|
if (DViewMode != VIEW_GDT)
|
|
DispMessage("GDT limit is illegal","Simulation error");
|
|
ShowMemData(FALSE);
|
|
}
|
|
else
|
|
{
|
|
HideTree();
|
|
DViewMode = VIEW_GDT; // displaying a GDT
|
|
FillGDT();
|
|
}
|
|
}
|
|
|
|
void ToggleIDT()
|
|
{
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
GrayMenuItem (0, CMD_WPTWR);
|
|
GrayMenuItem (0, CMD_WPTRD);
|
|
if (DViewMode == VIEW_IDT || /*(IDT_Len & 3) != 3 ||*/ (unsigned) IDT_Len >= 0x10000)
|
|
{
|
|
if (DViewMode != VIEW_IDT)
|
|
DispMessage("IDT limit is illegal","Simulation error");
|
|
ShowMemData(FALSE);
|
|
}
|
|
else
|
|
{
|
|
HideTree();
|
|
DViewMode = VIEW_IDT; // displaying an IDT
|
|
FillIDT();
|
|
}
|
|
}
|
|
|
|
void TogglePAGE()
|
|
{
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
GrayMenuItem (0, CMD_WPTWR);
|
|
GrayMenuItem (0, CMD_WPTRD);
|
|
if (DViewMode == VIEW_PAGING || InPaging == FALSE)
|
|
ShowMemData(FALSE);
|
|
else
|
|
{
|
|
HideTree();
|
|
DViewMode = VIEW_PAGING; // currently displaying Paging info
|
|
FillPAGE();
|
|
}
|
|
}
|
|
|
|
void ToggleStack()
|
|
{
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
GrayMenuItem (0, CMD_WPTWR);
|
|
GrayMenuItem (0, CMD_WPTRD);
|
|
if (DViewMode == VIEW_STACK)
|
|
ShowMemData(FALSE);
|
|
else
|
|
{
|
|
HideTree();
|
|
DViewMode = VIEW_STACK; // currently displaying stack
|
|
PStackLA = 1; // flag to force a full refresh
|
|
FillStack();
|
|
}
|
|
}
|
|
|
|
void ToggleBrkpt()
|
|
{
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
GrayMenuItem (0, CMD_WPTWR);
|
|
GrayMenuItem (0, CMD_WPTRD);
|
|
if (DViewMode == VIEW_BREAK)
|
|
ShowMemData(FALSE);
|
|
else
|
|
{
|
|
HideTree(); // HideTree needs to know the "prev" DViewMode
|
|
DViewMode = VIEW_BREAK; // currently displaying breakpoint info
|
|
FillBrkp();
|
|
}
|
|
}
|
|
|
|
void TogglePTree()
|
|
{
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
GrayMenuItem (0, CMD_WPTWR);
|
|
GrayMenuItem (0, CMD_WPTRD);
|
|
if (DViewMode == VIEW_PTREE)
|
|
ShowMemData(FALSE);
|
|
else
|
|
{
|
|
// FillPTree needs to know the "prev" DViewMode, to handle "refresh" events properly
|
|
FillPTree(); // get all info from param_tree into tree-view window
|
|
DViewMode = VIEW_PTREE; // currently displaying param_tree
|
|
}
|
|
}
|
|
|
|
void doFind()
|
|
{
|
|
unsigned int i, L;
|
|
bx_bool Select;
|
|
char srchstr[100];
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
*tmpcb = 0;
|
|
// read ASM text or MemDump data, find matches, select all matching lines
|
|
if (DumpHasFocus == FALSE)
|
|
{
|
|
if (AskText("Find text in mnemonic lines","ASM Search text:",tmpcb) == FALSE)
|
|
return;
|
|
if (strchr(tmpcb,'*') == 0 && strchr(tmpcb,'?') == 0)
|
|
sprintf(srchstr,"*%s*",tmpcb);
|
|
else
|
|
strcpy(srchstr,tmpcb);
|
|
|
|
if (UprCase != FALSE) // convert search string to uppercase if ASM is that way
|
|
upr(srchstr);
|
|
for(i = 0; i < (unsigned) AsmLineCount; i++)
|
|
{
|
|
GetLIText(ASM_WND, i, 2, tmpcb); // retrieve the ASM column 2 text for row i
|
|
Select = FALSE;
|
|
if (IsMatching(tmpcb, srchstr, TRUE) != FALSE)
|
|
Select = TRUE;
|
|
SetLIState(ASM_WND, i, Select);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (AskText("Memory Dump Search",
|
|
"Sequential hex bytes (e.g 00FEFA - no spaces), or ascii string (max. 16b):",tmpcb) == FALSE)
|
|
return;
|
|
|
|
int by = HexFromAsk(tmpcb,srchstr); // by = len of binary search string
|
|
|
|
// Find in all rows of 16 bytes -- must do rows, so they can be selected
|
|
for(i = 0, L = 0; i < 4096; i += 16, L++)
|
|
{
|
|
Select = FALSE;
|
|
if (by != 0 && FindHex((unsigned char *)DataDump + i,16,(unsigned char *)srchstr,by))
|
|
Select = TRUE;
|
|
SetLIState(DUMP_WND, L, Select);
|
|
}
|
|
|
|
// Try ascii for additional matches and selected lines
|
|
Select = TRUE; // this loop, only add selected lines to the display
|
|
by = strlen(tmpcb);
|
|
for(i = 0, L = 0; i < 4096; i += 16, L++)
|
|
{
|
|
if (by != 0 && FindHex((unsigned char *)DataDump + i,16,(unsigned char *)tmpcb,by))
|
|
SetLIState(DUMP_WND, L, Select);
|
|
}
|
|
}
|
|
}
|
|
|
|
void doStepN()
|
|
{
|
|
// can't run sim until everything is ready
|
|
if (AtBreak == FALSE || debug_cmd_ready != FALSE)
|
|
return;
|
|
sprintf (tmpcb,"%d",PrevStepNSize);
|
|
if (AskText("Singlestep N times","Number of steps (use 0x for hex):",tmpcb) == FALSE)
|
|
return;
|
|
Bit32u i = (Bit32u) cvt64(tmpcb,FALSE); // input either hex or decimal
|
|
if (i == 0)
|
|
return;
|
|
PrevStepNSize = i;
|
|
AtBreak = FALSE;
|
|
StatusChange = TRUE;
|
|
sprintf(debug_cmd, "s %d %d", CurrentCPU, i);
|
|
debug_cmd_ready = TRUE;
|
|
}
|
|
|
|
// User wants a custom disassembly
|
|
void doDisAsm()
|
|
{
|
|
int NumLines = DefaultAsmLines;
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
sprintf (tmpcb,"0x" FMT_LL "X",CurrentAsmLA);
|
|
if (AskText("Disassemble",
|
|
"Disassemble -- Enter Linear Start Address (use 0x for hex):",tmpcb) == FALSE)
|
|
return;
|
|
Bit64u h = cvt64(tmpcb,FALSE); // input either hex or decimal
|
|
sprintf (tmpcb,"%d",NumLines);
|
|
if (AskText("Disassemble","Number of lines: (Max. 2048)",tmpcb) == FALSE)
|
|
return;
|
|
sscanf (tmpcb,"%d",&NumLines);
|
|
if (NumLines <= 0 || NumLines > 2048)
|
|
return;
|
|
if (NumLines > 1000 && FWflag == FALSE)
|
|
ShowFW();
|
|
FillAsm(h, NumLines);
|
|
// Set the scroll limits for the new ASM window
|
|
BottomAsmLA = *AsmLA;
|
|
int j = bottommargin; // try to use this bottom margin on ASM window
|
|
if (j > AsmLineCount)
|
|
j = AsmLineCount;
|
|
TopAsmLA = AsmLA[AsmLineCount - j]; //TopAsmLA is the scroll point
|
|
}
|
|
|
|
// Toggle all "selected" items as linear breakpoint on the ASM window
|
|
void SetBreak(int OneEntry)
|
|
{
|
|
int L;
|
|
if (AtBreak == FALSE)
|
|
return;
|
|
if (OneEntry >= 0)
|
|
L = OneEntry;
|
|
else
|
|
// -1 is a flag to start the search at the beginning
|
|
L = GetNextSelectedLI(ASM_WND, -1);
|
|
while (L >= 0)
|
|
{
|
|
int iExist = -1;
|
|
int i=0;
|
|
while (i < BreakCount && iExist < 0)
|
|
{
|
|
if (BrkLAddr[i] == AsmLA[L])
|
|
iExist = i;
|
|
++i;
|
|
}
|
|
if (iExist >= 0)
|
|
{
|
|
// existing, remove
|
|
bx_dbg_del_lbreak(BrkIdx[iExist]);
|
|
i = iExist; // also compress it out of the local list
|
|
while (++i < BreakCount)
|
|
{
|
|
BrkLAddr[i-1] = BrkLAddr[i];
|
|
BrkIdx[i-1] = BrkIdx[i];
|
|
}
|
|
--BreakCount;
|
|
}
|
|
else
|
|
{
|
|
bx_address nbrk = (bx_address) AsmLA[L];
|
|
// Set a "regular" bochs linear breakpoint to that address
|
|
int BpId = bx_dbg_lbreakpoint_command(bkRegular, nbrk, NULL);
|
|
if (BpId >= 0)
|
|
{
|
|
// insertion sort the new Brkpt into the local list
|
|
i = BreakCount - 1;
|
|
while (i >= 0 && BrkLAddr[i] > nbrk)
|
|
{
|
|
BrkLAddr[i+1] = BrkLAddr[i];
|
|
BrkIdx[i+1] = BrkIdx[i];
|
|
--i;
|
|
}
|
|
BrkLAddr[i+1] = nbrk;
|
|
BrkIdx[i+1] = BpId;
|
|
++BreakCount;
|
|
}
|
|
}
|
|
if (OneEntry >= 0) // do not loop, if only doing one entry
|
|
L = -1;
|
|
else
|
|
// start the next ASM search at current item L
|
|
L = GetNextSelectedLI(ASM_WND, L);
|
|
}
|
|
Invalidate(ASM_WND); // redraw the ASM window -- colors may have changed
|
|
}
|
|
|
|
void DelWatchpoint(bx_watchpoint *wp_array, unsigned *TotEntries, int i)
|
|
{
|
|
while (++i < (int) *TotEntries)
|
|
wp_array[i-1] = wp_array[i];
|
|
-- *TotEntries;
|
|
}
|
|
|
|
void SetWatchpoint(unsigned *num_watchpoints, bx_watchpoint *watchpoint)
|
|
{
|
|
int iExist1 = -1;
|
|
int i = (int) *num_watchpoints;
|
|
if (AtBreak == FALSE || SA_valid == FALSE)
|
|
return;
|
|
// the list is unsorted -- test all of them
|
|
while (--i >= 0)
|
|
{
|
|
if (watchpoint[i].addr == SelectedDataAddress)
|
|
{
|
|
iExist1 = i;
|
|
i = 0;
|
|
}
|
|
}
|
|
if (iExist1 >= 0)
|
|
{
|
|
// existing watchpoint, remove by copying the list down
|
|
DelWatchpoint(watchpoint, num_watchpoints, iExist1);
|
|
}
|
|
else
|
|
{
|
|
// Set a watchpoint to last clicked address -- the list is not sorted
|
|
if (*num_watchpoints >= BX_DBG_MAX_WATCHPONTS) {
|
|
DispMessage("Too many of that type of watchpoint. Max: 16", "Table Overflow");
|
|
}
|
|
else {
|
|
watchpoint[*num_watchpoints].len = 1;
|
|
watchpoint[*num_watchpoints].addr = (bx_phy_address) SelectedDataAddress;
|
|
++(*num_watchpoints);
|
|
}
|
|
}
|
|
Invalidate(DUMP_WND); // redraw the MemDump window -- colors may have changed
|
|
}
|
|
|
|
void ChangeReg()
|
|
{
|
|
// Change a register -- search for the first selected register
|
|
int L = GetNextSelectedLI(REG_WND, -1);
|
|
if (AtBreak == FALSE || L == -1 || L >= TOT_REG_NUM)
|
|
return;
|
|
|
|
int i = RitemToRnum[L];
|
|
if (i > EFER_Rnum) // TODO: extend this to more reg -- need display names for all
|
|
return;
|
|
// if (i > EFER_Rnum)
|
|
// *tmpcb = 0;
|
|
// else
|
|
sprintf (tmpcb,"0x" FMT_LL "X", rV[i]);
|
|
if (AskText("Change Register Value",RDispName[i],tmpcb))
|
|
{
|
|
Bit64u val = cvt64(tmpcb,TRUE); // input either hex or decimal
|
|
#if BX_SUPPORT_X86_64
|
|
if (i >= EAX_Rnum && i <= EIP_Rnum) // must use RAX-RIP when setting 32b registers
|
|
i -= EAX_Rnum - RAX_Rnum;
|
|
#endif
|
|
RegObject[CurrentCPU][i]->set(val); // the set function should be a bool, not a void
|
|
// bx_bool worked = RegObject[CurrentCPU][i]->set(val);
|
|
// if (worked == FALSE)
|
|
// DispMessage ("Bochs does not allow you to set that register","Selection Error");
|
|
// else
|
|
LoadRegList(); // update the register window
|
|
}
|
|
}
|
|
|
|
// user wants to edit some memory
|
|
void SetMemLine(int L)
|
|
{
|
|
// get base address of "line" of data -- each line (L) is 16 bytes
|
|
char addrstr[64];
|
|
Bit64u h = DumpStart + (L<<4);
|
|
if (AtBreak == FALSE || L >= 256)
|
|
return;
|
|
|
|
if (LinearDump == FALSE)
|
|
sprintf(addrstr,"Physical Address: 0x" FMT_LL "X",h);
|
|
else
|
|
sprintf(addrstr,"Linear Address: 0x" FMT_LL "X",h);
|
|
|
|
unsigned char *u = (unsigned char *)(DataDump + (L<<4)); // important that it be unsigned!
|
|
sprintf(tmpcb,"%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
|
|
*u,u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8],u[9],u[10],u[11],u[12],u[13],u[14],u[15]);
|
|
|
|
if (AskText("Change Memory Values",addrstr,tmpcb))
|
|
{
|
|
Bit8u newval;
|
|
int err=0;
|
|
char *x = tmpcb;
|
|
upr(x); // force input string to uppercase
|
|
|
|
if (LinearDump) // is h is a LINEAR address? Convert to physical!
|
|
{
|
|
// use the ReadBx function to calculate the lin->phys offset
|
|
if (ReadBxLMem(h,0,(Bit8u *)addrstr) == FALSE) // "read" 0 bytes
|
|
err = 2;
|
|
else
|
|
h -= l_p_offset; // convert h to a physmem address
|
|
}
|
|
|
|
while (*x != 0 && err == 0)
|
|
{
|
|
char *s = x;
|
|
// verify that the next 2 chars are hex digits
|
|
if ((*x < '0' || *x > '9') && (*x < 'A' || *x > 'F'))
|
|
err = 1;
|
|
else
|
|
{
|
|
++x;
|
|
if ((*x < '0' || *x > '9') && (*x < 'A' || *x > 'F'))
|
|
err = 1;
|
|
else
|
|
{
|
|
++x;
|
|
if (*x != ' ' && *x != 0) // followed by a space or 0
|
|
err = 1;
|
|
}
|
|
}
|
|
if (err == 0)
|
|
{
|
|
// convert the hex to a byte, and try to store the byte in bochs physmem
|
|
sscanf (s,"%2X", (unsigned int*)&newval);
|
|
if (bx_mem.dbg_set_mem( (bx_phy_address) h, 1, &newval) == FALSE)
|
|
err = 2;
|
|
else
|
|
*u++ = newval; // update DataDump array so it will refresh on the screen
|
|
++h; // bump to the next mem address
|
|
while (*x == ' ') // scan past whitespace
|
|
++x;
|
|
}
|
|
}
|
|
|
|
if (err != 0)
|
|
{
|
|
if (err == 1)
|
|
DispMessage ("Improper char hex format","Input Format Error");
|
|
else
|
|
DispMessage ("Illegal memory address error?","Memory Error");
|
|
}
|
|
ShowData(); // refresh the data dump, even if there were errors
|
|
}
|
|
}
|
|
|
|
// Alt, Shift, Control keys are "down" if negative
|
|
// Normal return value is 0, return != 0 has OS-specific meaning.
|
|
int HotKey (int ww, int Alt, int Shift, int Control)
|
|
{
|
|
if (Alt < 0){
|
|
if (ww == '1')
|
|
doNewWSize(0);
|
|
else if (ww == '2')
|
|
doNewWSize(1);
|
|
else if (ww == '4')
|
|
doNewWSize(2);
|
|
else if (ww == '8')
|
|
doNewWSize(3);
|
|
else if (ww == '6')
|
|
doNewWSize(4);
|
|
else if (ww == VK_F2)
|
|
TogglePAGE();
|
|
#if BX_SUPPORT_FPU
|
|
else if (ww == VK_F3) ToggleSeeReg(CMD_FPUR); // MMX/FPU toggle
|
|
#endif
|
|
else if (ww == VK_F6) // AltF6 = Read Watchpt
|
|
{
|
|
if (DumpHasFocus != FALSE)
|
|
SetWatchpoint(&num_read_watchpoints,read_watchpoint);
|
|
}
|
|
else if (ww == VK_F7) // Alt+F7 memdump hex toggle
|
|
{
|
|
int i = DumpInAsciiMode;
|
|
i ^= 2;
|
|
if (i != 0)
|
|
{
|
|
DumpInAsciiMode = i;
|
|
i &= 2;
|
|
SetMenuCheckmark (i, CHK_CMD_MHEX);
|
|
GrayMenuItem (i, CMD_MASCII);
|
|
PrevDAD = 0; // force columns to resize
|
|
if (DViewMode == VIEW_MEMDUMP && DumpInitted != FALSE)
|
|
{
|
|
if (AtBreak == FALSE)
|
|
doDumpRefresh = TRUE;
|
|
else
|
|
ShowData();
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
switch (ww)
|
|
{
|
|
case VK_ESCAPE:
|
|
CommandHistoryIdx = 0;
|
|
ClearInputWindow();
|
|
ShowMemData(FALSE); // force a "normal" MemDump window
|
|
break;
|
|
|
|
case VK_UP:
|
|
// History from nextmost previous command
|
|
SelectHistory(-1);
|
|
break;
|
|
|
|
case VK_DOWN:
|
|
// Next History command
|
|
SelectHistory(+1);
|
|
break;
|
|
|
|
case VK_PRIOR:
|
|
// Page up on the MemDump window by 2K
|
|
if (DumpInitted != FALSE)
|
|
InitDataDump(LinearDump, DumpStart - 2048);
|
|
break;
|
|
|
|
case VK_NEXT:
|
|
// Page down on the MemDump window by 2K
|
|
if (DumpInitted != FALSE)
|
|
InitDataDump(LinearDump, DumpStart + 2048);
|
|
break;
|
|
|
|
case VK_F2:
|
|
if (Control < 0)
|
|
ToggleGDT();
|
|
else if (Shift < 0)
|
|
ToggleIDT();
|
|
else
|
|
ToggleStack();
|
|
break;
|
|
|
|
case VK_F3: // ^F3 = param tree, F3 = toggle syntax
|
|
if (Control < 0)
|
|
TogglePTree();
|
|
else
|
|
{
|
|
bx_disassemble.toggle_syntax_mode();
|
|
if (AtBreak != FALSE)
|
|
{
|
|
// do the standard ASM window fill sequence
|
|
Bit64u h = CurrentAsmLA;
|
|
CanDoLA(&h);
|
|
FillAsm(h, DefaultAsmLines);
|
|
}
|
|
else
|
|
BottomAsmLA = ~0; // force an ASM autoload
|
|
}
|
|
break;
|
|
|
|
case VK_F4:
|
|
if (Shift < 0) // Debug register toggle
|
|
ToggleSeeReg(CMD_DREG);
|
|
else if (Control >= 0) // Refresh
|
|
{
|
|
BottomAsmLA = ~0; // force an ASM autoload
|
|
ResizeColmns = TRUE; // force everything to repaint
|
|
doDumpRefresh = TRUE; // force a data window reload on a break
|
|
if (AtBreak != FALSE) // can't refresh the windows until a break!
|
|
{
|
|
doUpdate(); // refresh the ASM and Register windows
|
|
RefreshDataWin(); // and whichever data window is up
|
|
}
|
|
}
|
|
else {
|
|
if (CpuSupportSSE)
|
|
ToggleSeeReg(CMD_XMMR); // SSE toggle
|
|
}
|
|
break;
|
|
|
|
case VK_F5:
|
|
if (Shift < 0) // ShiftF5 = Modechange brk toggle
|
|
{
|
|
// toggle mode_break on cpu0, use that value to reset all CPUs
|
|
bx_bool nmb = BX_CPU(0)->mode_break ^ TRUE;
|
|
int j = TotCPUs;
|
|
while (--j >= 0)
|
|
BX_CPU(j)->mode_break = nmb;
|
|
SetMenuCheckmark ((int) nmb, CHK_CMD_MODEB);
|
|
}
|
|
else
|
|
{
|
|
// can't continue until everything is ready
|
|
if (AtBreak != FALSE && debug_cmd_ready == FALSE)
|
|
{
|
|
// The VGAW *MUST* be refreshed periodically -- it's best to use the timer.
|
|
// Which means that the sim cannot be directly run from this msglp thread.
|
|
*debug_cmd = 'c'; // send a fake "continue" command to the internal debugger
|
|
debug_cmd[1] = 0;
|
|
debug_cmd_ready = TRUE;
|
|
AtBreak = FALSE;
|
|
StatusChange = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VK_F7:
|
|
if (Control < 0)
|
|
InitDataDump(0,(Bit64u) 1); // ^F7 = PhysDump
|
|
else if (Shift < 0) // ShiftF7 = ascii toggle
|
|
{
|
|
int i = DumpInAsciiMode;
|
|
i ^= 1;
|
|
if (i != 0)
|
|
{
|
|
DumpInAsciiMode = i;
|
|
i &= 1;
|
|
SetMenuCheckmark (i, CHK_CMD_MASCII);
|
|
GrayMenuItem (i, CMD_MHEX);
|
|
PrevDAD = 0; // force columns to resize
|
|
if (DViewMode == VIEW_MEMDUMP && DumpInitted != FALSE)
|
|
{
|
|
if (AtBreak == FALSE)
|
|
doDumpRefresh = TRUE;
|
|
else
|
|
ShowData();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
InitDataDump(1,(Bit64u) 1); // F7 = LinDump
|
|
break;
|
|
|
|
case VK_F6:
|
|
if (Control < 0) // ^F6 = Breakpoint window
|
|
ToggleBrkpt();
|
|
else if (Shift < 0) // ShiftF6 = Write Watchpt
|
|
{
|
|
if (DumpHasFocus == FALSE)
|
|
SetBreak(-1); // set or delete breakpoint(s) at the selected address(es)
|
|
else
|
|
SetWatchpoint(&num_write_watchpoints,write_watchpoint);
|
|
}
|
|
else
|
|
{
|
|
if (DumpHasFocus == FALSE) // F6 = Brkpt
|
|
SetBreak(-1); // set or delete breakpoint(s) at the selected address(es)
|
|
else
|
|
SetWatchpoint(&num_write_watchpoints,write_watchpoint);
|
|
}
|
|
break;
|
|
|
|
case VK_F8:
|
|
// can't continue until everything is ready
|
|
if (AtBreak != FALSE && debug_cmd_ready == FALSE)
|
|
{
|
|
*debug_cmd = 'p'; // send a fake "proceed" command to the internal debugger
|
|
debug_cmd[1] = 0;
|
|
debug_cmd_ready = TRUE;
|
|
AtBreak = FALSE;
|
|
StatusChange = TRUE;
|
|
}
|
|
break;
|
|
|
|
case VK_F11:
|
|
if (AtBreak != FALSE && debug_cmd_ready == FALSE)
|
|
{
|
|
bx_dbg_stepN_command(CurrentCPU, 1); // singlestep
|
|
StatusChange = TRUE;
|
|
OnBreak();
|
|
}
|
|
break;
|
|
|
|
case VK_F9:
|
|
doStepN(); // ask user for a step #
|
|
break;
|
|
|
|
case 'C': // ^c = break
|
|
if (Control < 0)
|
|
{
|
|
SIM->debug_break();
|
|
}
|
|
break;
|
|
|
|
case 'D':
|
|
if (Control < 0)
|
|
doDisAsm();
|
|
break;
|
|
|
|
case 'F':
|
|
if (Control < 0)
|
|
doFind();
|
|
break;
|
|
|
|
case VK_RIGHT: // Win32: send a few virtual movement keys back into the Input window
|
|
case VK_LEFT:
|
|
case VK_END:
|
|
case VK_HOME:
|
|
case VK_DELETE:
|
|
return -1;
|
|
|
|
case VK_RETURN:
|
|
// can't run a command until everything is ready
|
|
if (AtBreak != FALSE && debug_cmd_ready == FALSE)
|
|
{
|
|
*tmpcb = 0;
|
|
GetInputEntry(tmpcb);
|
|
StatusChange = TRUE;
|
|
if (*tmpcb == 0) // Hitting <CR> on a blank line means SINGLESTEP
|
|
{
|
|
bx_dbg_stepN_command(CurrentCPU, 1); // singlestep
|
|
OnBreak();
|
|
}
|
|
else
|
|
{
|
|
// deal with the command history:
|
|
if (strlen(tmpcb) > 79)
|
|
DispMessage ("Running command, but history has an 80 char Max.",
|
|
"Command history overflow");
|
|
else
|
|
{
|
|
strcpy (CmdHistory[CmdHInsert], tmpcb);
|
|
CmdHInsert = (CmdHInsert + 1) & 63; // circular buffer, 0 to 63
|
|
}
|
|
strcpy (debug_cmd,tmpcb); // send the command into the bochs internal debugger
|
|
debug_cmd_ready = TRUE;
|
|
AtBreak = FALSE;
|
|
ClearInputWindow(); // prepare for the next command
|
|
CommandHistoryIdx = 0; // and reset the history queue to the new end
|
|
}
|
|
}
|
|
} // end the switch
|
|
// if (Control >= 0 && ww >= ' ' && ww < 0x7f) -- might be interesting to catch printable chars
|
|
// return 1;
|
|
return 0;
|
|
}
|
|
|
|
void ActivateMenuItem (int cmd)
|
|
{
|
|
int i;
|
|
|
|
switch(cmd)
|
|
{
|
|
case CMD_CONT: // run/go/continue
|
|
if (AtBreak != FALSE && debug_cmd_ready == FALSE)
|
|
{
|
|
// The VGAW *MUST* be refreshed periodically -- it's best to use the timer.
|
|
// Which means that the sim cannot be directly run from this msglp thread.
|
|
*debug_cmd = 'c'; // send a fake "continue" command to the internal debugger
|
|
debug_cmd[1] = 0;
|
|
debug_cmd_ready = TRUE;
|
|
AtBreak = FALSE;
|
|
StatusChange = TRUE;
|
|
}
|
|
break;
|
|
|
|
case CMD_STEP1: // step 1
|
|
if (AtBreak != FALSE && debug_cmd_ready == FALSE)
|
|
{
|
|
bx_dbg_stepN_command(CurrentCPU, 1); // singlestep
|
|
StatusChange = TRUE;
|
|
OnBreak();
|
|
}
|
|
break;
|
|
|
|
case CMD_STEPN: // step N
|
|
doStepN();
|
|
break;
|
|
|
|
case CMD_BREAK: // break/stop the sim
|
|
// SIM->debug_break() only "break"s the internal debugger
|
|
SIM->debug_break();
|
|
break;
|
|
|
|
case CMD_BRKPT: // set or delete breakpoint(s) at the selected address(es)
|
|
SetBreak(-1);
|
|
break;
|
|
|
|
case CMD_WPTWR: // set or delete a data write watchpoint
|
|
SetWatchpoint(&num_write_watchpoints,write_watchpoint);
|
|
break;
|
|
|
|
case CMD_WPTRD: // set or delete a data read watchpoint
|
|
SetWatchpoint(&num_read_watchpoints,read_watchpoint);
|
|
break;
|
|
|
|
case CMD_FIND: // find -- Control-F
|
|
doFind();
|
|
break;
|
|
|
|
case CMD_RFRSH: // force an update/refresh
|
|
BottomAsmLA = ~0; // force an ASM autoload
|
|
ResizeColmns = TRUE; // force everything to repaint
|
|
doDumpRefresh = TRUE; // force a data window reload on a break
|
|
if (AtBreak != FALSE) // can't refresh the windows until a break!
|
|
{
|
|
doUpdate(); // refresh the ASM and Register windows
|
|
RefreshDataWin(); // and whichever data window is up
|
|
}
|
|
break;
|
|
|
|
case CMD_PHYDMP: // "physical mem" data dump
|
|
InitDataDump(0,(Bit64u) 1);
|
|
break;
|
|
|
|
case CMD_LINDMP: // "linear memory" data dump
|
|
InitDataDump(1,(Bit64u) 1);
|
|
break;
|
|
|
|
case CMD_STACK: // toggle display of Stack
|
|
ToggleStack();
|
|
break;
|
|
|
|
case CMD_GDTV: // toggle display of GDT
|
|
ToggleGDT();
|
|
break;
|
|
|
|
case CMD_IDTV: // toggle display of IDT
|
|
ToggleIDT();
|
|
break;
|
|
|
|
case CMD_PAGEV: // display paging info
|
|
TogglePAGE();
|
|
break;
|
|
|
|
case CMD_VBRK: // display breakpoint/watchpoint info
|
|
ToggleBrkpt();
|
|
break;
|
|
|
|
case CMD_CMEM: // view current MemDump -- acts like "cancel"
|
|
CommandHistoryIdx = 0;
|
|
ClearInputWindow();
|
|
ShowMemData(FALSE); // force a "normal" MemDump window
|
|
break;
|
|
|
|
case CMD_PTREE:
|
|
TogglePTree();
|
|
break;
|
|
|
|
case CMD_DISASM: // disassemble starting at a particular address
|
|
doDisAsm();
|
|
break;
|
|
|
|
case CMD_MODEB: // toggle the simulation's Mode-Change-Break flag
|
|
{
|
|
// toggle mode_break on cpu0, use that value to reset all CPUs
|
|
bx_bool nmb = BX_CPU(0)->mode_break ^ TRUE;
|
|
i = TotCPUs;
|
|
while (--i >= 0)
|
|
BX_CPU(i)->mode_break = nmb;
|
|
SetMenuCheckmark ((int) nmb, CHK_CMD_MODEB);
|
|
break;
|
|
}
|
|
|
|
case CMD_ONECPU: // toggle whether to show SMP CPUs
|
|
if (AtBreak == FALSE)
|
|
break;
|
|
SingleCPU ^= TRUE;
|
|
TotCPUs = 1;
|
|
if (SingleCPU == FALSE)
|
|
TotCPUs = BX_SMP_PROCESSORS;
|
|
SetMenuCheckmark ((int) SingleCPU, CHK_CMD_ONECPU);
|
|
VSizeChange();
|
|
break;
|
|
|
|
case CMD_DADEF: // set default # of disassembly lines in a list
|
|
if (AtBreak == FALSE)
|
|
break;
|
|
sprintf (tmpcb,"%d",DefaultAsmLines);
|
|
if (AskText("Disassembly default linecount","Max. 2048:",tmpcb) == FALSE)
|
|
return;
|
|
sscanf (tmpcb,"%u",&i);
|
|
if (i > 0 && i <= 2048)
|
|
DefaultAsmLines = i;
|
|
if (i > 1000 && FWflag == FALSE) // friendly warning
|
|
ShowFW();
|
|
break;
|
|
|
|
case CMD_ATTI: // Toggle ASM Syntax
|
|
bx_disassemble.toggle_syntax_mode();
|
|
if (AtBreak != FALSE)
|
|
{
|
|
// do the standard ASM window fill sequence
|
|
Bit64u h = CurrentAsmLA;
|
|
CanDoLA(&h);
|
|
FillAsm(h, DefaultAsmLines);
|
|
}
|
|
else
|
|
BottomAsmLA = ~0; // force an ASM autoload
|
|
break;
|
|
|
|
case CMD_IOWIN: // toggle display of internal debugger Input and Output windows
|
|
if (AtBreak == FALSE)
|
|
break;
|
|
ShowIOWindows ^= TRUE;
|
|
SetMenuCheckmark ((int) ShowIOWindows, CHK_CMD_IOWIN);
|
|
VSizeChange();
|
|
break;
|
|
|
|
case CMD_SBTN: // Toggle showing top pushbutton-row
|
|
if (AtBreak == FALSE)
|
|
break;
|
|
ShowButtons ^= TRUE;
|
|
SetMenuCheckmark ((int) ShowButtons, CHK_CMD_SBTN);
|
|
VSizeChange();
|
|
break;
|
|
|
|
case CMD_UCASE: // Toggle showing everything in uppercase
|
|
UprCase ^= 1;
|
|
SetMenuCheckmark ((int) UprCase, CHK_CMD_UCASE);
|
|
MakeRDnames();
|
|
if (AtBreak != FALSE)
|
|
{
|
|
LoadRegList();
|
|
// do the standard ASM window fill sequence
|
|
Bit64u h = CurrentAsmLA;
|
|
CanDoLA(&h);
|
|
FillAsm(h, DefaultAsmLines);
|
|
}
|
|
else
|
|
BottomAsmLA = ~0; // force an ASM autoload
|
|
break;
|
|
|
|
case CMD_MHEX: // Toggle showing hex in Dump window
|
|
i = DumpInAsciiMode;
|
|
i ^= 2;
|
|
if (i != 0)
|
|
{
|
|
DumpInAsciiMode = i;
|
|
i &= 2;
|
|
SetMenuCheckmark (i, CHK_CMD_MHEX);
|
|
GrayMenuItem (i, CMD_MASCII);
|
|
PrevDAD = 0; // force columns to resize
|
|
if (DViewMode == VIEW_MEMDUMP && DumpInitted != FALSE)
|
|
{
|
|
if (AtBreak == FALSE)
|
|
doDumpRefresh = TRUE;
|
|
else
|
|
ShowData();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CMD_MASCII: // Toggle showing ASCII in Dump window
|
|
i = DumpInAsciiMode;
|
|
i ^= 1;
|
|
if (i != 0)
|
|
{
|
|
DumpInAsciiMode = i;
|
|
i &= 1;
|
|
SetMenuCheckmark (i, CHK_CMD_MASCII);
|
|
GrayMenuItem (i, CMD_MHEX);
|
|
PrevDAD = 0; // force columns to resize
|
|
if (DViewMode == VIEW_MEMDUMP && DumpInitted != FALSE)
|
|
{
|
|
if (AtBreak == FALSE)
|
|
doDumpRefresh = TRUE;
|
|
else
|
|
ShowData();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CMD_LEND: // Toggle Endianness for the MemDumps
|
|
isLittleEndian ^= TRUE;
|
|
SetMenuCheckmark ((int) isLittleEndian, CHK_CMD_LEND);
|
|
if (DViewMode == VIEW_MEMDUMP && DumpInitted != FALSE)
|
|
{
|
|
if (AtBreak == FALSE)
|
|
doDumpRefresh = TRUE;
|
|
else
|
|
ShowData();
|
|
}
|
|
break;
|
|
|
|
case CMD_WS_1: // set memory dump "wordsize"
|
|
// "Align" = "wordsize" -- from 1 to 16
|
|
doNewWSize(0);
|
|
break;
|
|
|
|
case CMD_WS_2:
|
|
doNewWSize(1);
|
|
break;
|
|
|
|
case CMD_WS_4:
|
|
doNewWSize(2);
|
|
break;
|
|
|
|
case CMD_WS_8:
|
|
doNewWSize(3);
|
|
break;
|
|
|
|
case CMD_WS16:
|
|
doNewWSize(4);
|
|
break;
|
|
|
|
case CMD_IGNSA: // Toggle ID disassembly output ignoring
|
|
ignSSDisasm ^= TRUE;
|
|
SetMenuCheckmark ((int) ignSSDisasm, CHK_CMD_IGNSA);
|
|
break;
|
|
|
|
case CMD_IGNNT: // Toggle NextT ignoring
|
|
ignoreNxtT ^= TRUE;
|
|
SetMenuCheckmark ((int) ignoreNxtT, CHK_CMD_IGNNT);
|
|
break;
|
|
|
|
case CMD_RCLR: // Toggle Register Coloring
|
|
SeeRegColors ^= TRUE;
|
|
SetMenuCheckmark ((int) SeeRegColors, CHK_CMD_RCLR);
|
|
if (AtBreak != FALSE)
|
|
LoadRegList();
|
|
break;
|
|
|
|
|
|
case CMD_EREG: // Show Registers of various types
|
|
case CMD_SREG:
|
|
case CMD_SYSR:
|
|
case CMD_CREG:
|
|
case CMD_FPUR:
|
|
case CMD_XMMR:
|
|
case CMD_DREG:
|
|
case CMD_TREG:
|
|
ToggleSeeReg(cmd);
|
|
break;
|
|
|
|
case CMD_ABOUT: // "About" box
|
|
DispMessage ("Bochs Enhanced Debugger, Version 1.2\r\nCopyright (C) Chourdakis Michael.\r\nModified by Bruce Ewing",
|
|
"About");
|
|
break;
|
|
|
|
case CMD_FONT: // font
|
|
ResizeColmns = TRUE; // column widths are font dependent
|
|
if (NewFont() != FALSE)
|
|
VSizeChange();
|
|
break;
|
|
|
|
case CMD_LOGVIEW: // Toggle sending log to output window
|
|
LogView ^= 1;
|
|
SIM->set_log_viewer(LogView);
|
|
SetMenuCheckmark((int)LogView, CHK_CMD_LOGVIEW);
|
|
break;
|
|
}
|
|
}
|
|
|
|
BxEvent *enh_dbg_notify_callback(void *unused, BxEvent *event)
|
|
{
|
|
switch (event->type)
|
|
{
|
|
case BX_SYNC_EVT_GET_DBG_COMMAND:
|
|
{
|
|
debug_cmd = new char[512];
|
|
debug_cmd_ready = 0;
|
|
HitBreak();
|
|
while (debug_cmd_ready == 0 && bx_user_quit == 0)
|
|
{
|
|
if (vgaw_refresh != 0) // is the GUI frontend requesting a VGAW refresh?
|
|
SIM->refresh_vga();
|
|
vgaw_refresh = 0;
|
|
#ifdef WIN32
|
|
Sleep(10);
|
|
#elif BX_HAVE_USLEEP
|
|
usleep(10000);
|
|
#else
|
|
sleep(1);
|
|
#endif
|
|
}
|
|
if (bx_user_quit != 0) {
|
|
bx_dbg_exit(0);
|
|
}
|
|
event->u.debugcmd.command = debug_cmd;
|
|
event->retcode = 1;
|
|
return event;
|
|
}
|
|
case BX_ASYNC_EVT_DBG_MSG:
|
|
{
|
|
ParseIDText(event->u.logmsg.msg);
|
|
return event;
|
|
}
|
|
case BX_ASYNC_EVT_LOG_MSG:
|
|
if (LogView) {
|
|
ParseIDText(event->u.logmsg.msg);
|
|
delete [] event->u.logmsg.msg;
|
|
return event;
|
|
}
|
|
default:
|
|
return (*old_callback)(old_callback_arg, event);
|
|
}
|
|
}
|
|
|
|
static size_t strip_whitespace(char *s)
|
|
{
|
|
size_t ptr = 0;
|
|
char *tmp = new char[strlen(s)+1];
|
|
strcpy(tmp, s);
|
|
while (s[ptr] == ' ') ptr++;
|
|
if (ptr > 0) strcpy(s, tmp+ptr);
|
|
delete [] tmp;
|
|
ptr = strlen(s);
|
|
while ((ptr > 0) && (s[ptr-1] == ' ')) {
|
|
s[--ptr] = 0;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
void ReadSettings()
|
|
{
|
|
FILE *fd = NULL;
|
|
char line[512];
|
|
char *ret, *param, *val;
|
|
bx_bool format_checked = 0;
|
|
size_t len1 = 0, len2;
|
|
int i;
|
|
|
|
fd = fopen("bx_enh_dbg.ini", "r");
|
|
if (fd == NULL) return;
|
|
do {
|
|
ret = fgets(line, sizeof(line)-1, fd);
|
|
line[sizeof(line) - 1] = '\0';
|
|
size_t len = strlen(line);
|
|
if ((len>0) && (line[len-1] < ' '))
|
|
line[len-1] = '\0';
|
|
if ((ret != NULL) && (strlen(line) > 0)) {
|
|
if (!format_checked) {
|
|
if (!strncmp(line, "# bx_enh_dbg_ini", 16)) {
|
|
format_checked = 1;
|
|
} else {
|
|
fprintf(stderr, "bx_enh_dbg.ini: wrong file format\n");
|
|
fclose(fd);
|
|
return;
|
|
}
|
|
} else {
|
|
if (line[0] == '#') continue;
|
|
param = strtok(line, "=");
|
|
if (param != NULL) {
|
|
len1 = strip_whitespace(param);
|
|
val = strtok(NULL, "");
|
|
if (val == NULL) {
|
|
fprintf(stderr, "bx_enh_dbg.ini: missing value for parameter '%s'\n", param);
|
|
continue;
|
|
}
|
|
} else {
|
|
continue;
|
|
}
|
|
len2 = strip_whitespace(val);
|
|
if ((len1 == 0) || (len2 == 0)) continue;
|
|
if ((len1 == 9) && !strncmp(param, "SeeReg[", 7) && (param[8] == ']')) {
|
|
if ((param[7] < '0') || (param[7] > '7')) {
|
|
fprintf(stderr, "bx_enh_dbg.ini: invalid index for option SeeReg[x]\n");
|
|
continue;
|
|
}
|
|
i = atoi(¶m[7]);
|
|
SeeReg[i] = !strcmp(val, "TRUE");
|
|
} else if (!strcmp(param, "SingleCPU")) {
|
|
SingleCPU = !strcmp(val, "TRUE");
|
|
} else if (!strcmp(param, "ShowIOWindows")) {
|
|
ShowIOWindows = !strcmp(val, "TRUE");
|
|
} else if (!strcmp(param, "ShowButtons")) {
|
|
ShowButtons = !strcmp(val, "TRUE");
|
|
} else if (!strcmp(param, "SeeRegColors")) {
|
|
SeeRegColors = !strcmp(val, "TRUE");
|
|
} else if (!strcmp(param, "ignoreNxtT")) {
|
|
ignoreNxtT = !strcmp(val, "TRUE");
|
|
} else if (!strcmp(param, "ignSSDisasm")) {
|
|
ignSSDisasm = !strcmp(val, "TRUE");
|
|
} else if (!strcmp(param, "UprCase")) {
|
|
UprCase = atoi(val);
|
|
} else if (!strcmp(param, "DumpInAsciiMode")) {
|
|
DumpInAsciiMode = atoi(val);
|
|
} else if (!strcmp(param, "isLittleEndian")) {
|
|
isLittleEndian = !strcmp(val, "TRUE");
|
|
} else if (!strcmp(param, "DefaultAsmLines")) {
|
|
DefaultAsmLines = atoi(val);
|
|
} else if (!strcmp(param, "DumpWSIndex")) {
|
|
DumpWSIndex = atoi(val);
|
|
DumpAlign = (1 << DumpWSIndex);
|
|
PrevDAD = 0;
|
|
} else if (!strcmp(param, "DockOrder")) {
|
|
DockOrder = strtoul(val, NULL, 16);
|
|
} else if ((len1 == 15) && !strncmp(param, "ListWidthPix[", 13) && (param[14] == ']')) {
|
|
if ((param[13] < '0') || (param[13] > '2')) {
|
|
fprintf(stderr, "bx_enh_dbg.ini: invalid index for option SeeReg[x]\n");
|
|
continue;
|
|
}
|
|
i = atoi(¶m[13]);
|
|
ListWidthPix[i] = atoi(val);
|
|
} else if (!ParseOSSettings(param, val)) {
|
|
fprintf(stderr, "bx_enh_dbg.ini: unknown option '%s'\n", line);
|
|
}
|
|
}
|
|
}
|
|
} while (!feof(fd));
|
|
fclose(fd);
|
|
}
|
|
|
|
void WriteSettings()
|
|
{
|
|
FILE *fd = NULL;
|
|
int i;
|
|
|
|
fd = fopen("bx_enh_dbg.ini", "w");
|
|
if (fd == NULL) return;
|
|
fprintf(fd, "# bx_enh_dbg_ini\n");
|
|
for (i = 0; i < 8; i++) {
|
|
fprintf(fd, "SeeReg[%d] = %s\n", i, SeeReg[i] ? "TRUE" : "FALSE");
|
|
}
|
|
fprintf(fd, "SingleCPU = %s\n", SingleCPU ? "TRUE" : "FALSE");
|
|
fprintf(fd, "ShowIOWindows = %s\n", ShowIOWindows ? "TRUE" : "FALSE");
|
|
fprintf(fd, "ShowButtons = %s\n", ShowButtons ? "TRUE" : "FALSE");
|
|
fprintf(fd, "SeeRegColors = %s\n", SeeRegColors ? "TRUE" : "FALSE");
|
|
fprintf(fd, "ignoreNxtT = %s\n", ignoreNxtT ? "TRUE" : "FALSE");
|
|
fprintf(fd, "ignSSDisasm = %s\n", ignSSDisasm ? "TRUE" : "FALSE");
|
|
fprintf(fd, "UprCase = %d\n", UprCase);
|
|
fprintf(fd, "DumpInAsciiMode = %d\n", DumpInAsciiMode);
|
|
fprintf(fd, "isLittleEndian = %s\n", isLittleEndian ? "TRUE" : "FALSE");
|
|
fprintf(fd, "DefaultAsmLines = %d\n", DefaultAsmLines);
|
|
fprintf(fd, "DumpWSIndex = %d\n", DumpWSIndex);
|
|
fprintf(fd, "DockOrder = 0x%03x\n", DockOrder);
|
|
for (i = 0; i < 3; i++) {
|
|
fprintf(fd, "ListWidthPix[%d] = %d\n", i, ListWidthPix[i]);
|
|
}
|
|
WriteOSSettings(fd);
|
|
fclose(fd);
|
|
}
|
|
|
|
void InitDebugDialog()
|
|
{
|
|
// redirect notify callback to the debugger specific code
|
|
SIM->get_notify_callback(&old_callback, &old_callback_arg);
|
|
assert (old_callback != NULL);
|
|
SIM->set_notify_callback(enh_dbg_notify_callback, NULL);
|
|
ReadSettings();
|
|
DoAllInit(); // non-os-specific init stuff
|
|
OSInit();
|
|
}
|
|
|
|
void CloseDebugDialog()
|
|
{
|
|
WriteSettings();
|
|
SIM->set_log_viewer(0);
|
|
SIM->set_notify_callback(old_callback, old_callback_arg);
|
|
CloseDialog();
|
|
}
|
|
|
|
#endif
|