Files
org-hyperion-cules/dyngui.c
Fish (David B. Trout) 904985b4b8 New InternalHercCmd func to reduce chance of bad CallHercCmd call:
CallHercCmd "fails" when argument 3 (cmdline) is NULL (where "fails" == "command is ignored and is instead processed as if <enter> key was pressed without having entered any command at all").
2014-11-21 21:14:12 -08:00

2179 lines
69 KiB
C

/* DYNGUI.C (c) Copyright "Fish" (David B. Trout), 2003-2012 */
/* Hercules External GUI Interface DLL */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
#include "hstdinc.h"
#include "hercules.h" // (#includes "config." w/#define for VERSION)
#ifdef EXTERNALGUI
#if defined(OPTION_DYNAMIC_LOAD)
#include "devtype.h"
#include "opcode.h"
#if !defined(V1)
#define V1 0
#endif
#if !defined(V2)
#define V2 0
#endif
///////////////////////////////////////////////////////////////////////////////
// Some handy macros... (feel free to add these to hercules.h)
#ifndef BOOL
#define BOOL BYTE
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
///////////////////////////////////////////////////////////////////////////////
// Our global variables... (initialized by our "Initialize" function)
#define INPUT_STREAM_FILE_PTR ( stdin )
#define OUTPUT_STREAM_FILE_PTR ( stdout )
#define STATUS_STREAM_FILE_PTR ( stderr )
#define MAX_COMMAND_LEN ( 1024 )
#define DEF_MAXRATES_RPT_INTVL ( 1440 )
#if defined( WIN32 ) && !defined( HDL_USE_LIBTOOL )
#if !defined( _MSVC_ )
SYSBLK *psysblk; // (ptr to Herc's SYSBLK structure)
#define sysblk (*psysblk)
#endif
#endif
static FILE* fOutputStream = NULL; // (stdout stream)
static FILE* fStatusStream = NULL; // (stderr stream)
static int nInputStreamFileNum = -1; // (file descriptor for stdin stream)
static int gui_nounload = 1; // (nounload indicator)
// The device query buffer SHOULD be the maximum device filename length
// plus the maximum descriptive length of any/all options for the device,
// but there is no #define for either so we have no choice but to impose
// our own maximum.
#define MAX_DEVICEQUERY_LEN ( 1024 + 256 )
///////////////////////////////////////////////////////////////////////////////
// Some forward references... (our own functions that we call)
void Initialize ();
void ProcessingLoop ();
void Cleanup ();
void UpdateTargetCPU ();
void ReadInputData (int nTimeoutMillsecs);
void ProcessInputData ();
void* gui_panel_command (char* pszCommand);
void UpdateStatus ();
void HandleForcedRefresh();
void UpdateCPUStatus ();
void UpdateRegisters ();
void UpdateDeviceStatus ();
void NewUpdateDevStats ();
void gui_fprintf( FILE* stream, const char* pszFormat, ... );
///////////////////////////////////////////////////////////////////////////////
// Our main processing loop...
BOOL bDoneProcessing = FALSE; // (set to TRUE to exit)
void ProcessingLoop()
{
// Notify logger_thread we're in control
sysblk.panel_init = 1;
// Our main purpose in life: read input stream and process
// any commands that may be entered, and send periodic status
// information back to the external gui via its status stream.
// Note we only exit whenever our bDoneProcessing flag is set
// which is normally not done until just before Herc unloads
// us which is normally not done until immediately before it
// terminates.
// Also note we re-retrieve sysblk.panrate each iteration
// since it could change from one iteration to the next as a result
// of the Hercules "panrate" command being entered and processed.
while (!bDoneProcessing)
{
UpdateTargetCPU(); // ("cpu" command could have changed it)
UpdateStatus(); // (keep sending status back to gui...)
ReadInputData( sysblk.panrate );
ProcessInputData(); // (if there even is any of course...)
}
}
///////////////////////////////////////////////////////////////////////////////
int pcpu = INT_MAX; // target cpu# for commands and displays
REGS* pTargetCPU_REGS = NULL; // pointer to target cpu REGS
int prev_pcpu = INT_MAX; // (previous value)
REGS* pPrevTargetCPU_REGS = NULL; // (previous value)
REGS copyregs; // (copy of active cpu's REGS)
REGS copysieregs; // (same but when in SIE mode)
REGS* CopyREGS( int cpu ); // (fwd ref)
///////////////////////////////////////////////////////////////////////////////
void UpdateTargetCPU ()
{
if (!sysblk.shutdown)
pTargetCPU_REGS = CopyREGS( pcpu = sysblk.pcpu );
}
///////////////////////////////////////////////////////////////////////////////
// (get non-moving/non-dynamic working copy of active cpu's register context)
REGS* CopyREGS( int cpu ) // (same logic as in panel.c)
{
REGS* regs;
if (cpu < 0 || cpu >= sysblk.maxcpu)
cpu = 0;
obtain_lock( &sysblk.cpulock[cpu] );
if (!(regs = sysblk.regs[cpu]))
{
release_lock( &sysblk.cpulock[cpu] );
return &sysblk.dummyregs;
}
memcpy( &copyregs, regs, sysblk.regs_copy_len );
if (!copyregs.hostregs)
{
release_lock(&sysblk.cpulock[cpu]);
return &sysblk.dummyregs;
}
#if defined(_FEATURE_SIE)
if (regs->sie_active)
{
memcpy( &copysieregs, regs->guestregs, sysblk.regs_copy_len );
copyregs.guestregs = &copysieregs;
copysieregs.hostregs = &copyregs;
regs = &copysieregs;
}
else
#endif
regs = &copyregs;
SET_PSW_IA( regs );
release_lock( &sysblk.cpulock[cpu] );
return regs;
}
///////////////////////////////////////////////////////////////////////////////
char* pszInputBuff = NULL; // ptr to buffer
int nInputBuffSize = (MAX_COMMAND_LEN+1); // how big the buffer is
int nInputLen = 0; // amount of data it's holding
///////////////////////////////////////////////////////////////////////////////
void ReadInputData ( int nTimeoutMillsecs )
{
size_t nMaxBytesToRead;
int nBytesRead;
char* pReadBuffer;
// Wait for keyboard input data to arrive...
#if !defined( _MSVC_ )
fd_set input_fd_set;
struct timeval wait_interval_timeval;
int rc;
FD_ZERO ( &input_fd_set );
FD_SET ( nInputStreamFileNum, &input_fd_set );
wait_interval_timeval.tv_sec = nTimeoutMillsecs / 1000;
wait_interval_timeval.tv_usec = (nTimeoutMillsecs % 1000) * 1000;
if ((rc = select( nInputStreamFileNum+1, &input_fd_set, NULL, NULL, &wait_interval_timeval )) < 0)
{
if (HSO_EINTR == HSO_errno)
return; // (we were interrupted by a signal)
// A bona fide error occurred; abort...
WRMSG
(
HHC01511, "S"
,"select()"
,strerror(HSO_errno)
);
bDoneProcessing = TRUE; // (force main loop to exit)
return;
}
// Has keyboard input data indeed arrived yet?
if (!FD_ISSET( nInputStreamFileNum, &input_fd_set ))
return; // (nothing for us to do...)
#endif // !defined( _MSVC_ )
// Ensure our buffer never overflows... (-2 because
// we need room for at least 1 byte + NULL terminator)
MINMAX(nInputLen,0,(nInputBuffSize-2));
// Read input data into next available buffer location...
// (nMaxBytesToRead-1 == room for NULL terminator)
pReadBuffer = (pszInputBuff + nInputLen);
nMaxBytesToRead = (nInputBuffSize - nInputLen) - 1;
#if !defined( _MSVC_ )
if ((nBytesRead = read( nInputStreamFileNum, pReadBuffer, nMaxBytesToRead )) < 0)
{
if (EINTR == errno)
return; // (we were interrupted by a signal)
// A bona fide error occurred; abort...
WRMSG
(
HHC01511, "S"
,"read()"
,strerror(errno)
);
bDoneProcessing = TRUE; // (force main loop to exit)
return;
}
#else // defined( _MSVC_ )
if ( ( nBytesRead = w32_get_stdin_char( pReadBuffer, nTimeoutMillsecs ) ) <= 0 )
return;
#endif // !defined( _MSVC_ )
// Update amount of input data we have and
// ensure that it's always NULL terminated...
MINMAX(nBytesRead,0,nInputBuffSize);
nInputLen += nBytesRead;
MINMAX(nInputLen,0,(nInputBuffSize-1));
*(pszInputBuff + nInputLen) = 0;
}
///////////////////////////////////////////////////////////////////////////////
char* pszCommandBuff = NULL; // ptr to buffer
int nCommandBuffSize = (MAX_COMMAND_LEN+1); // how big the buffer is
int nCommandLen = 0; // amount of data it's holding
///////////////////////////////////////////////////////////////////////////////
// Process the data we just read from the input stream...
void ProcessInputData ()
{
char* pNewLineChar;
// Ensure our buffer is NULL terminated...
MINMAX(nInputLen,0,(nInputBuffSize-1));
*(pszInputBuff + nInputLen) = 0;
// Input commands are delimited by newline characters...
while (nInputLen && (pNewLineChar = strchr(pszInputBuff,'\n')) != NULL)
{
// Extract command from input buffer
// into our command processing buffer...
nCommandLen = (pNewLineChar - pszInputBuff);
MINMAX(nCommandLen,0,(nCommandBuffSize-1));
memcpy(pszCommandBuff, pszInputBuff, nCommandLen);
*(pszCommandBuff + nCommandLen) = 0;
// Process the extracted command...
// Note that we always call the registered "panel_command" function
// rather than call our "gui_panel_command" function directly. This
// is in case some other DLL has overridden OUR command handler...
panel_command ( pszCommandBuff ); // (call registered handler)
// Shift remaining data back to beginning of input buffer...
nInputLen = ((pszInputBuff + nInputLen) - (pNewLineChar+1));
MINMAX(nInputLen,0,(nInputBuffSize-1));
memmove(pszInputBuff,pNewLineChar+1,nInputLen);
*(pszInputBuff + nInputLen) = 0;
}
}
///////////////////////////////////////////////////////////////////////////////
// (These are actually boolean flags..)
double gui_version = 0.0; // (version of HercGUI we're talking to)
BYTE gui_forced_refresh = 1; // (force initial update refresh)
BYTE gui_wants_gregs = 0;
BYTE gui_wants_gregs64 = 0;
BYTE gui_wants_cregs = 0;
BYTE gui_wants_cregs64 = 0;
BYTE gui_wants_aregs = 0;
BYTE gui_wants_fregs = 0;
BYTE gui_wants_fregs64 = 0;
BYTE gui_wants_devlist = 0;
BYTE gui_wants_new_devlist = 1; // (should always be initially on)
#if defined(OPTION_MIPS_COUNTING)
BYTE gui_wants_aggregates = 1;
BYTE gui_wants_cpupct = 0;
BYTE gui_wants_cpupct_all = 0;
int prev_cpupct [ MAX_CPU_ENGINES ];
U32 prev_mips_rate = 0;
U32 prev_sios_rate = 0;
#endif // defined(OPTION_MIPS_COUNTING)
///////////////////////////////////////////////////////////////////////////////
// Our Hercules "panel_command" override...
void* gui_panel_command (char* pszCommand)
{
void* (*next_panel_command_handler)(char* pszCommand);
// Special GUI commands start with ']'. At the moment, all these special
// gui commands tell us is what status information it's interested in...
if ( ']' != *pszCommand )
goto NotSpecialGUICommand;
gui_forced_refresh = 1; // (forced update refresh)
pszCommand++; // (bump past ']')
if (strncasecmp(pszCommand,"VERS=",5) == 0)
{
gui_version = atof(pszCommand+5);
return NULL;
}
if (strncasecmp(pszCommand,"SCD=",4) == 0)
{
// (set current directory)
if (chdir(pszCommand+4) != 0)
{
// (inform gui of error)
char *cwd = getcwd( NULL, 0 );
if (cwd)
{
debug_cd_cmd( cwd );
free( cwd );
}
}
return NULL;
}
if (strncasecmp(pszCommand,"GREGS=",6) == 0)
{
gui_wants_gregs = atoi(pszCommand+6);
return NULL;
}
if (strncasecmp(pszCommand,"GREGS64=",8) == 0)
{
gui_wants_gregs64 = atoi(pszCommand+8);
return NULL;
}
if (strncasecmp(pszCommand,"CREGS=",6) == 0)
{
gui_wants_cregs = atoi(pszCommand+6);
return NULL;
}
if (strncasecmp(pszCommand,"CREGS64=",8) == 0)
{
gui_wants_cregs64 = atoi(pszCommand+8);
return NULL;
}
if (strncasecmp(pszCommand,"AREGS=",6) == 0)
{
gui_wants_aregs = atoi(pszCommand+6);
return NULL;
}
if (strncasecmp(pszCommand,"FREGS=",6) == 0)
{
gui_wants_fregs = atoi(pszCommand+6);
return NULL;
}
if (strncasecmp(pszCommand,"FREGS64=",8) == 0)
{
gui_wants_fregs64 = atoi(pszCommand+8);
return NULL;
}
if (strncasecmp(pszCommand,"DEVLIST=",8) == 0)
{
gui_wants_devlist = atoi(pszCommand+8);
if ( gui_wants_devlist )
gui_wants_new_devlist = 0;
return NULL;
}
if (strncasecmp(pszCommand,"NEWDEVLIST=",11) == 0)
{
gui_wants_new_devlist = atoi(pszCommand+11);
if ( gui_wants_new_devlist )
gui_wants_devlist = 0;
return NULL;
}
if (strncasecmp(pszCommand,"MAINSTOR=",9) == 0)
{
gui_fprintf(fStatusStream,"MAINSTOR=%"UINT_PTR_FMT"d\n",(uintptr_t)pTargetCPU_REGS->mainstor);
// Here's a trick! Hercules reports its version number to the GUI
// by means of the MAINSIZE value! Later releases of HercGUI know
// to interpret mainsizes less than 1000 as Hercule's version number.
// Earlier versions of HercGUI will simply try to interpret it as
// the actual mainsize, but no real harm is done since we immediately
// send it the CORRECT mainsize immediately afterwards. This allows
// future versions of HercGUI to know whether the version of Hercules
// that it's talking to supports a given feature or not. Slick, eh? :)
// PROGRAMMING NOTE: we use 'V1' and 'V2' here (instead of 'VERSION')
// since the 'VERSION' string can be any value the user wants and thus
// might not be numeric nor even correspond at all to Hercules's actual
// build version. V1 and V2 however should ALWAYS be numbers and should
// ALWAYS equal Hercules's actual build version...
// VERSION is set in configure.ac as x.xx
// version is (char) parsed in makefile.bat to get (char) V1 V2 V3 V4
// printing V2 with a %d format worked well until 3.07
// when bumping to 3.08 the stupid C complained about a wrong octal
// constant
// since Fish asked for numbers only and the VERSION string is
// set in configure.ac by reasonable people it is SAFE to expect that
// it will always be a sequence of 2 digits numbers xx.xx.xx
// so instead of using %d,%d V1,V2 it is right to ....
gui_fprintf(fStatusStream,"MAINSIZE=%s\n",VERSION);
if (gui_version < 1.12)
gui_fprintf(fStatusStream,"MAINSIZE=%d\n",(U32)sysblk.mainsize);
else
gui_fprintf(fStatusStream,"MAINSIZE=%"UINT_PTR_FMT"d\n",(uintptr_t)sysblk.mainsize);
return NULL;
}
#if defined(OPTION_MIPS_COUNTING)
if (strncasecmp(pszCommand,"CPUPCT=",7) == 0)
{
gui_wants_cpupct = atoi(pszCommand+7);
return NULL;
}
if (strncasecmp(pszCommand,"CPUPCTALL=",10) == 0)
{
if (!(gui_wants_cpupct_all = atoi(pszCommand+10)))
memset( &prev_cpupct[0], 0xFF, sizeof(prev_cpupct) );
return NULL;
}
if (strncasecmp(pszCommand,"AGGREGATE=",10) == 0)
{
gui_wants_aggregates = atoi(pszCommand+10);
gui_forced_refresh = 1;
return NULL;
}
#endif
// Silently ignore any unrecognized special GUI commands...
return NULL; // (silently ignore it)
NotSpecialGUICommand:
// Ignore "commands" that are actually just comments (start with '*' or '#')
if ('*' == pszCommand[0] || '#' == pszCommand[0])
{
if ('*' == pszCommand[0]) // (LOUD comment?)
logmsg("%s\n",pszCommand); // (then log to console)
return NULL; // (and otherwise ignore it)
}
// Otherwise it's not a command that we handle. Call the next higher
// level command handler which, under normal circumstances SHOULD be
// Hercules's "panel_command" function, but which MAY have been over-
// ridden by yet some OTHER dynamically loaded command handler...
next_panel_command_handler = HDL_FINDNXT( gui_panel_command );
if (!next_panel_command_handler) // (extremely unlikely!)
return (char *)-1; // (extremely unlikely!)
return next_panel_command_handler( pszCommand );
}
///////////////////////////////////////////////////////////////////////////////
// Status updating control fields...
QWORD psw, prev_psw;
BYTE wait_bit;
BYTE prev_cpustate = 0xFF;
U64 prev_instcount = 0;
U32 prev_gr [16];
U64 prev_gr64 [16];
U32 prev_cr [16];
U64 prev_cr64 [16];
U32 prev_ar [16];
U32 prev_fpr [8*2];
U32 prev_fpr64[16*2];
///////////////////////////////////////////////////////////////////////////////
// Send status information messages back to the gui...
void UpdateStatus ()
{
BOOL bStatusChanged = FALSE; // (whether or not anything has changed)
if (sysblk.shutdown) return;
copy_psw(pTargetCPU_REGS, psw);
wait_bit = (psw[1] & 0x02);
// The SYS light and %CPU-Utilization
// information we send *ALL* the time...
if (!(0
|| CPUSTATE_STOPPING == pTargetCPU_REGS->cpustate
|| CPUSTATE_STOPPED == pTargetCPU_REGS->cpustate
))
{
gui_fprintf(fStatusStream,
"SYS=%c\n"
,wait_bit ? '0' : '1'
);
}
#if defined(OPTION_MIPS_COUNTING)
if (gui_wants_cpupct)
{
if (gui_wants_aggregates)
{
int cpu, cpupct = 0, started = 0;
for (cpupct=0, cpu=0; cpu < sysblk.maxcpu; cpu++)
{
if (1
&& IS_CPU_ONLINE( cpu )
&& CPUSTATE_STARTED == sysblk.regs[ cpu ]->cpustate
)
{
started++;
cpupct += sysblk.regs[ cpu ]->cpupct;
}
}
gui_fprintf(fStatusStream,
"CPUPCT=%d\n"
,started ? (cpupct / started) : 0
);
}
else
{
gui_fprintf(fStatusStream,
"CPUPCT=%d\n"
,pTargetCPU_REGS->cpupct
);
}
}
if (gui_wants_cpupct_all)
{
int i, cpupct;
for (i = 0; i < sysblk.hicpu; i++)
{
if (0
|| !IS_CPU_ONLINE(i)
|| CPUSTATE_STARTED != sysblk.regs[i]->cpustate
)
cpupct = 0;
else
cpupct = sysblk.regs[i]->cpupct;
if (cpupct != prev_cpupct[i])
{
prev_cpupct[i] = cpupct;
gui_fprintf( fStatusStream, "CPUPCT%02d=%d\n", i, cpupct );
}
}
}
#endif
// Determine if we need to inform the GUI of anything...
bStatusChanged = FALSE; // (whether or not anything has changed)
if (0
|| gui_forced_refresh
|| pTargetCPU_REGS != pPrevTargetCPU_REGS
|| pcpu != prev_pcpu
|| memcmp(prev_psw, psw, sizeof(prev_psw)) != 0
|| prev_cpustate != pTargetCPU_REGS->cpustate
|| prev_instcount != INSTCOUNT(pTargetCPU_REGS)
)
{
bStatusChanged = TRUE; // (something has indeed changed...)
if (gui_forced_refresh) // (forced refresh?)
HandleForcedRefresh(); // (reset all prev values)
// Save new values for next time...
pPrevTargetCPU_REGS = pTargetCPU_REGS;
prev_pcpu = pcpu;
memcpy(prev_psw, psw, sizeof(prev_psw));
prev_cpustate = pTargetCPU_REGS->cpustate;
prev_instcount = INSTCOUNT(pTargetCPU_REGS);
}
// If anything has changed, inform the GUI...
if (bStatusChanged)
{
UpdateCPUStatus(); // (update the status line info...)
UpdateRegisters(); // (update the registers display...)
}
// PROGRAMMING NOTE: my original [rather poorly designed I admit] logic
// sent device status messages to the GUI *continuously* (i.e. all the
// time), even when both Herc and the channel subsystem was idle. This
// proved to be terribly inefficient, causing the GUI to consume *FAR*
// too much valuable CPU cycles parsing all of those messages.
// Thus, starting with this version of dyngui, we now only send device
// status messages to the GUI only whenever the device's status actually
// changes, but only if it (the GUI) specifically requests such notifi-
// cations of course (via the new "]NEWDEVLIST=" special message).
// The new(er) version of HercGUI understands (and thus requests) these
// newer format device status messages, but older versions of HercGUI
// of course do not. Thus in order to remain compatible with the current
// (older) version of the GUI, we still need to support the inefficient
// technique of constantly sending a constant stream of device status
// messages.
// Eventually at some point this existing original inefficient technique
// logic will be removed (once everyone has had time to upgrade to the
// newer version of HercGUI), but for now, at least for the next couple
// of HercGUI release cycles, we need to keep it.
if (gui_wants_devlist) // (if the device list is visible)
UpdateDeviceStatus(); // (update the list of devices...)
else // (the two options are mutually exclusive from one another)
if (gui_wants_new_devlist) // (if the device list is visible)
NewUpdateDevStats(); // (update the list of devices...)
gui_forced_refresh = 0; // (reset switch; must follow devlist update)
}
///////////////////////////////////////////////////////////////////////////////
// Forced GUI Refresh: reset all "previous" values to force update...
void HandleForcedRefresh()
{
#ifdef OPTION_MIPS_COUNTING
prev_mips_rate = INT_MAX;
prev_sios_rate = INT_MAX;
#endif
prev_instcount = ULLONG_MAX;
prev_pcpu = INT_MAX;
pPrevTargetCPU_REGS = NULL;
prev_cpustate = 0xFF;
memset( prev_psw, 0xFF, sizeof(prev_psw) );
memset( &prev_gr [0], 0xFF,
sizeof(prev_gr) );
memset( &prev_cr [0], 0xFF,
sizeof(prev_cr) );
memset( &prev_ar [0], 0xFF,
sizeof(prev_ar) );
memset( &prev_fpr [0], 0xFF,
sizeof(prev_fpr) );
memset( &prev_gr64 [0], 0xFF,
sizeof(prev_gr64) );
memset( &prev_cr64 [0], 0xFF,
sizeof(prev_cr64) );
memset( &prev_fpr64[0], 0xFF,
sizeof(prev_fpr64) );
#if defined(OPTION_MIPS_COUNTING)
memset( &prev_cpupct [0], 0xFF,
sizeof(prev_cpupct ) );
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Send status information messages back to the gui...
void UpdateCPUStatus ()
{
if (sysblk.shutdown) return;
if (pTargetCPU_REGS == &sysblk.dummyregs)
{
// pTargetCPU_REGS == &sysblk.dummyregs; cpu is offline
gui_fprintf(fStatusStream, "STATUS="
"%s%02X (((((((((((((((((((((((( OFFLINE ))))))))))))))))))))))))\n",
PTYPSTR(pcpu) ,pcpu);
}
else // pTargetCPU_REGS != &sysblk.dummyregs; cpu is online
{
// CPU status line... (PSW, status indicators, and instruction count)
gui_fprintf(fStatusStream, "STATUS="
"%s%02X "
"PSW=%2.2X%2.2X%2.2X%2.2X "
"%2.2X%2.2X%2.2X%2.2X "
"%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X "
"%c%c%c%c%c%c%c%c "
"instcount=%" I64_FMT "u\n"
,PTYPSTR(pTargetCPU_REGS->cpuad), pTargetCPU_REGS->cpuad
,psw[0], psw[1], psw[2], psw[3]
,psw[4], psw[5], psw[6], psw[7]
,psw[8], psw[9], psw[10], psw[11], psw[12], psw[13], psw[14], psw[15]
,CPUSTATE_STOPPED == pTargetCPU_REGS->cpustate ? 'M' : '.'
,sysblk.inststep ? 'T' : '.'
,wait_bit ? 'W' : '.'
,pTargetCPU_REGS->loadstate ? 'L' : '.'
,pTargetCPU_REGS->checkstop ? 'C' : '.'
,PROBSTATE(&pTargetCPU_REGS->psw) ? 'P' : '.'
,
#if defined(_FEATURE_SIE)
SIE_MODE(pTargetCPU_REGS) ? 'S' : '.'
#else // !defined(_FEATURE_SIE)
'.'
#endif // defined(_FEATURE_SIE)
,
#if defined(_900)
ARCH_900 == pTargetCPU_REGS->arch_mode ? 'Z' : '.'
#else // !defined(_900)
'.'
#endif // defined(_900)
,(U64)INSTCOUNT(pTargetCPU_REGS)
);
} // endif cpu is online/offline
#if defined(OPTION_MIPS_COUNTING)
// MIPS rate and SIOS rate...
{
U32* mipsrate;
U32* siosrate;
if (gui_wants_aggregates)
{
mipsrate = &sysblk.mipsrate;
siosrate = &sysblk.siosrate;
}
else
{
mipsrate = &pTargetCPU_REGS->mipsrate;
siosrate = &pTargetCPU_REGS->siosrate;
}
if (*mipsrate != prev_mips_rate)
{
gui_fprintf( fStatusStream,
"MIPS=%4d.%2.2d\n"
, *mipsrate / 1000000
,(*mipsrate % 1000000) / 10000
);
prev_mips_rate = *mipsrate;
}
if (*siosrate != prev_sios_rate)
{
gui_fprintf( fStatusStream,
"SIOS=%4d\n"
,*siosrate
);
prev_sios_rate = *siosrate;
}
}
#endif // defined(OPTION_MIPS_COUNTING)
}
///////////////////////////////////////////////////////////////////////////////
// Send status information messages back to the gui...
#define REG32FMT "%8.8"I32_FMT"X"
#define REG64FMT "%16.16"I64_FMT"X"
void UpdateRegisters ()
{
if (sysblk.shutdown) return;
if (gui_wants_gregs)
{
if (0
|| prev_gr[0] != pTargetCPU_REGS->GR_L(0)
|| prev_gr[1] != pTargetCPU_REGS->GR_L(1)
|| prev_gr[2] != pTargetCPU_REGS->GR_L(2)
|| prev_gr[3] != pTargetCPU_REGS->GR_L(3)
)
{
prev_gr[0] = pTargetCPU_REGS->GR_L(0);
prev_gr[1] = pTargetCPU_REGS->GR_L(1);
prev_gr[2] = pTargetCPU_REGS->GR_L(2);
prev_gr[3] = pTargetCPU_REGS->GR_L(3);
gui_fprintf(fStatusStream,
"GR0-3="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->GR_L(0)
,pTargetCPU_REGS->GR_L(1)
,pTargetCPU_REGS->GR_L(2)
,pTargetCPU_REGS->GR_L(3)
);
}
if (0
|| prev_gr[4] != pTargetCPU_REGS->GR_L(4)
|| prev_gr[5] != pTargetCPU_REGS->GR_L(5)
|| prev_gr[6] != pTargetCPU_REGS->GR_L(6)
|| prev_gr[7] != pTargetCPU_REGS->GR_L(7)
)
{
prev_gr[4] = pTargetCPU_REGS->GR_L(4);
prev_gr[5] = pTargetCPU_REGS->GR_L(5);
prev_gr[6] = pTargetCPU_REGS->GR_L(6);
prev_gr[7] = pTargetCPU_REGS->GR_L(7);
gui_fprintf(fStatusStream,
"GR4-7="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->GR_L(4)
,pTargetCPU_REGS->GR_L(5)
,pTargetCPU_REGS->GR_L(6)
,pTargetCPU_REGS->GR_L(7)
);
}
if (0
|| prev_gr[8] != pTargetCPU_REGS->GR_L(8)
|| prev_gr[9] != pTargetCPU_REGS->GR_L(9)
|| prev_gr[10] != pTargetCPU_REGS->GR_L(10)
|| prev_gr[11] != pTargetCPU_REGS->GR_L(11)
)
{
prev_gr[8] = pTargetCPU_REGS->GR_L(8);
prev_gr[9] = pTargetCPU_REGS->GR_L(9);
prev_gr[10] = pTargetCPU_REGS->GR_L(10);
prev_gr[11] = pTargetCPU_REGS->GR_L(11);
gui_fprintf(fStatusStream,
"GR8-B="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->GR_L(8)
,pTargetCPU_REGS->GR_L(9)
,pTargetCPU_REGS->GR_L(10)
,pTargetCPU_REGS->GR_L(11)
);
}
if (0
|| prev_gr[12] != pTargetCPU_REGS->GR_L(12)
|| prev_gr[13] != pTargetCPU_REGS->GR_L(13)
|| prev_gr[14] != pTargetCPU_REGS->GR_L(14)
|| prev_gr[15] != pTargetCPU_REGS->GR_L(15)
)
{
prev_gr[12] = pTargetCPU_REGS->GR_L(12);
prev_gr[13] = pTargetCPU_REGS->GR_L(13);
prev_gr[14] = pTargetCPU_REGS->GR_L(14);
prev_gr[15] = pTargetCPU_REGS->GR_L(15);
gui_fprintf(fStatusStream,
"GRC-F="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->GR_L(12)
,pTargetCPU_REGS->GR_L(13)
,pTargetCPU_REGS->GR_L(14)
,pTargetCPU_REGS->GR_L(15)
);
}
}
if (gui_wants_gregs64)
{
if (0
|| prev_gr64[0] != pTargetCPU_REGS->GR_G(0)
|| prev_gr64[1] != pTargetCPU_REGS->GR_G(1)
)
{
prev_gr64[0] = pTargetCPU_REGS->GR_G(0);
prev_gr64[1] = pTargetCPU_REGS->GR_G(1);
gui_fprintf(fStatusStream,
"64_GR0-1="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->GR_G(0)
,pTargetCPU_REGS->GR_G(1)
);
}
if (0
|| prev_gr64[2] != pTargetCPU_REGS->GR_G(2)
|| prev_gr64[3] != pTargetCPU_REGS->GR_G(3)
)
{
prev_gr64[2] = pTargetCPU_REGS->GR_G(2);
prev_gr64[3] = pTargetCPU_REGS->GR_G(3);
gui_fprintf(fStatusStream,
"64_GR2-3="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->GR_G(2)
,pTargetCPU_REGS->GR_G(3)
);
}
if (0
|| prev_gr64[4] != pTargetCPU_REGS->GR_G(4)
|| prev_gr64[5] != pTargetCPU_REGS->GR_G(5)
)
{
prev_gr64[4] = pTargetCPU_REGS->GR_G(4);
prev_gr64[5] = pTargetCPU_REGS->GR_G(5);
gui_fprintf(fStatusStream,
"64_GR4-5="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->GR_G(4)
,pTargetCPU_REGS->GR_G(5)
);
}
if (0
|| prev_gr64[6] != pTargetCPU_REGS->GR_G(6)
|| prev_gr64[7] != pTargetCPU_REGS->GR_G(7)
)
{
prev_gr64[6] = pTargetCPU_REGS->GR_G(6);
prev_gr64[7] = pTargetCPU_REGS->GR_G(7);
gui_fprintf(fStatusStream,
"64_GR6-7="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->GR_G(6)
,pTargetCPU_REGS->GR_G(7)
);
}
if (0
|| prev_gr64[8] != pTargetCPU_REGS->GR_G(8)
|| prev_gr64[9] != pTargetCPU_REGS->GR_G(9)
)
{
prev_gr64[8] = pTargetCPU_REGS->GR_G(8);
prev_gr64[9] = pTargetCPU_REGS->GR_G(9);
gui_fprintf(fStatusStream,
"64_GR8-9="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->GR_G(8)
,pTargetCPU_REGS->GR_G(9)
);
}
if (0
|| prev_gr64[10] != pTargetCPU_REGS->GR_G(10)
|| prev_gr64[11] != pTargetCPU_REGS->GR_G(11)
)
{
prev_gr64[10] = pTargetCPU_REGS->GR_G(10);
prev_gr64[11] = pTargetCPU_REGS->GR_G(11);
gui_fprintf(fStatusStream,
"64_GRA-B="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->GR_G(10)
,pTargetCPU_REGS->GR_G(11)
);
}
if (0
|| prev_gr64[12] != pTargetCPU_REGS->GR_G(12)
|| prev_gr64[13] != pTargetCPU_REGS->GR_G(13)
)
{
prev_gr64[12] = pTargetCPU_REGS->GR_G(12);
prev_gr64[13] = pTargetCPU_REGS->GR_G(13);
gui_fprintf(fStatusStream,
"64_GRC-D="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->GR_G(12)
,pTargetCPU_REGS->GR_G(13)
);
}
if (0
|| prev_gr64[14] != pTargetCPU_REGS->GR_G(14)
|| prev_gr64[15] != pTargetCPU_REGS->GR_G(15)
)
{
prev_gr64[14] = pTargetCPU_REGS->GR_G(14);
prev_gr64[15] = pTargetCPU_REGS->GR_G(15);
gui_fprintf(fStatusStream,
"64_GRE-F="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->GR_G(14)
,pTargetCPU_REGS->GR_G(15)
);
}
}
if (gui_wants_cregs)
{
if (0
|| prev_cr[0] != pTargetCPU_REGS->CR_L(0)
|| prev_cr[1] != pTargetCPU_REGS->CR_L(1)
|| prev_cr[2] != pTargetCPU_REGS->CR_L(2)
|| prev_cr[3] != pTargetCPU_REGS->CR_L(3)
)
{
prev_cr[0] = pTargetCPU_REGS->CR_L(0);
prev_cr[1] = pTargetCPU_REGS->CR_L(1);
prev_cr[2] = pTargetCPU_REGS->CR_L(2);
prev_cr[3] = pTargetCPU_REGS->CR_L(3);
gui_fprintf(fStatusStream,
"CR0-3="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->CR_L(0)
,pTargetCPU_REGS->CR_L(1)
,pTargetCPU_REGS->CR_L(2)
,pTargetCPU_REGS->CR_L(3)
);
}
if (0
|| prev_cr[4] != pTargetCPU_REGS->CR_L(4)
|| prev_cr[5] != pTargetCPU_REGS->CR_L(5)
|| prev_cr[6] != pTargetCPU_REGS->CR_L(6)
|| prev_cr[7] != pTargetCPU_REGS->CR_L(7)
)
{
prev_cr[4] = pTargetCPU_REGS->CR_L(4);
prev_cr[5] = pTargetCPU_REGS->CR_L(5);
prev_cr[6] = pTargetCPU_REGS->CR_L(6);
prev_cr[7] = pTargetCPU_REGS->CR_L(7);
gui_fprintf(fStatusStream,
"CR4-7="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->CR_L(4)
,pTargetCPU_REGS->CR_L(5)
,pTargetCPU_REGS->CR_L(6)
,pTargetCPU_REGS->CR_L(7)
);
}
if (0
|| prev_cr[8] != pTargetCPU_REGS->CR_L(8)
|| prev_cr[9] != pTargetCPU_REGS->CR_L(9)
|| prev_cr[10] != pTargetCPU_REGS->CR_L(10)
|| prev_cr[11] != pTargetCPU_REGS->CR_L(11)
)
{
prev_cr[8] = pTargetCPU_REGS->CR_L(8);
prev_cr[9] = pTargetCPU_REGS->CR_L(9);
prev_cr[10] = pTargetCPU_REGS->CR_L(10);
prev_cr[10] = pTargetCPU_REGS->CR_L(10);
gui_fprintf(fStatusStream,
"CR8-B="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->CR_L(8)
,pTargetCPU_REGS->CR_L(9)
,pTargetCPU_REGS->CR_L(10)
,pTargetCPU_REGS->CR_L(11)
);
}
if (0
|| prev_cr[12] != pTargetCPU_REGS->CR_L(12)
|| prev_cr[13] != pTargetCPU_REGS->CR_L(13)
|| prev_cr[14] != pTargetCPU_REGS->CR_L(14)
|| prev_cr[15] != pTargetCPU_REGS->CR_L(15)
)
{
prev_cr[12] = pTargetCPU_REGS->CR_L(12);
prev_cr[13] = pTargetCPU_REGS->CR_L(13);
prev_cr[14] = pTargetCPU_REGS->CR_L(14);
prev_cr[15] = pTargetCPU_REGS->CR_L(15);
gui_fprintf(fStatusStream,
"CRC-F="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->CR_L(12)
,pTargetCPU_REGS->CR_L(13)
,pTargetCPU_REGS->CR_L(14)
,pTargetCPU_REGS->CR_L(15)
);
}
}
if (gui_wants_cregs64)
{
if (0
|| prev_cr64[0] != pTargetCPU_REGS->CR_G(0)
|| prev_cr64[1] != pTargetCPU_REGS->CR_G(1)
)
{
prev_cr64[0] = pTargetCPU_REGS->CR_G(0);
prev_cr64[1] = pTargetCPU_REGS->CR_G(1);
gui_fprintf(fStatusStream,
"64_CR0-1="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->CR_G(0)
,pTargetCPU_REGS->CR_G(1)
);
}
if (0
|| prev_cr64[2] != pTargetCPU_REGS->CR_G(2)
|| prev_cr64[3] != pTargetCPU_REGS->CR_G(3)
)
{
prev_cr64[2] = pTargetCPU_REGS->CR_G(2);
prev_cr64[3] = pTargetCPU_REGS->CR_G(3);
gui_fprintf(fStatusStream,
"64_CR2-3="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->CR_G(2)
,pTargetCPU_REGS->CR_G(3)
);
}
if (0
|| prev_cr64[4] != pTargetCPU_REGS->CR_G(4)
|| prev_cr64[5] != pTargetCPU_REGS->CR_G(5)
)
{
prev_cr64[4] = pTargetCPU_REGS->CR_G(4);
prev_cr64[5] = pTargetCPU_REGS->CR_G(5);
gui_fprintf(fStatusStream,
"64_CR4-5="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->CR_G(4)
,pTargetCPU_REGS->CR_G(5)
);
}
if (0
|| prev_cr64[6] != pTargetCPU_REGS->CR_G(6)
|| prev_cr64[7] != pTargetCPU_REGS->CR_G(7)
)
{
prev_cr64[6] = pTargetCPU_REGS->CR_G(6);
prev_cr64[7] = pTargetCPU_REGS->CR_G(7);
gui_fprintf(fStatusStream,
"64_CR6-7="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->CR_G(6)
,pTargetCPU_REGS->CR_G(7)
);
}
if (0
|| prev_cr64[8] != pTargetCPU_REGS->CR_G(8)
|| prev_cr64[9] != pTargetCPU_REGS->CR_G(9)
)
{
prev_cr64[8] = pTargetCPU_REGS->CR_G(8);
prev_cr64[9] = pTargetCPU_REGS->CR_G(9);
gui_fprintf(fStatusStream,
"64_CR8-9="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->CR_G(8)
,pTargetCPU_REGS->CR_G(9)
);
}
if (0
|| prev_cr64[10] != pTargetCPU_REGS->CR_G(10)
|| prev_cr64[11] != pTargetCPU_REGS->CR_G(11)
)
{
prev_cr64[10] = pTargetCPU_REGS->CR_G(10);
prev_cr64[11] = pTargetCPU_REGS->CR_G(11);
gui_fprintf(fStatusStream,
"64_CRA-B="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->CR_G(10)
,pTargetCPU_REGS->CR_G(11)
);
}
if (0
|| prev_cr64[12] != pTargetCPU_REGS->CR_G(12)
|| prev_cr64[13] != pTargetCPU_REGS->CR_G(13)
)
{
prev_cr64[12] = pTargetCPU_REGS->CR_G(12);
prev_cr64[13] = pTargetCPU_REGS->CR_G(13);
gui_fprintf(fStatusStream,
"64_CRC-D="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->CR_G(12)
,pTargetCPU_REGS->CR_G(13)
);
}
if (0
|| prev_cr64[14] != pTargetCPU_REGS->CR_G(14)
|| prev_cr64[15] != pTargetCPU_REGS->CR_G(15)
)
{
prev_cr64[14] = pTargetCPU_REGS->CR_G(14);
prev_cr64[15] = pTargetCPU_REGS->CR_G(15);
gui_fprintf(fStatusStream,
"64_CRE-F="REG64FMT" "REG64FMT"\n"
,pTargetCPU_REGS->CR_G(14)
,pTargetCPU_REGS->CR_G(15)
);
}
}
if (gui_wants_aregs)
{
if (0
|| prev_ar[0] != pTargetCPU_REGS->AR(0)
|| prev_ar[1] != pTargetCPU_REGS->AR(1)
|| prev_ar[2] != pTargetCPU_REGS->AR(2)
|| prev_ar[3] != pTargetCPU_REGS->AR(3)
)
{
prev_ar[0] = pTargetCPU_REGS->AR(0);
prev_ar[1] = pTargetCPU_REGS->AR(1);
prev_ar[2] = pTargetCPU_REGS->AR(2);
prev_ar[3] = pTargetCPU_REGS->AR(3);
gui_fprintf(fStatusStream,
"AR0-3="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->AR(0)
,pTargetCPU_REGS->AR(1)
,pTargetCPU_REGS->AR(2)
,pTargetCPU_REGS->AR(3)
);
}
if (0
|| prev_ar[4] != pTargetCPU_REGS->AR(4)
|| prev_ar[5] != pTargetCPU_REGS->AR(5)
|| prev_ar[6] != pTargetCPU_REGS->AR(6)
|| prev_ar[7] != pTargetCPU_REGS->AR(7)
)
{
prev_ar[4] = pTargetCPU_REGS->AR(4);
prev_ar[5] = pTargetCPU_REGS->AR(5);
prev_ar[6] = pTargetCPU_REGS->AR(6);
prev_ar[7] = pTargetCPU_REGS->AR(7);
gui_fprintf(fStatusStream,
"AR4-7="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->AR(4)
,pTargetCPU_REGS->AR(5)
,pTargetCPU_REGS->AR(6)
,pTargetCPU_REGS->AR(7)
);
}
if (0
|| prev_ar[8] != pTargetCPU_REGS->AR(8)
|| prev_ar[9] != pTargetCPU_REGS->AR(9)
|| prev_ar[10] != pTargetCPU_REGS->AR(10)
|| prev_ar[11] != pTargetCPU_REGS->AR(11)
)
{
prev_ar[8] = pTargetCPU_REGS->AR(8);
prev_ar[9] = pTargetCPU_REGS->AR(9);
prev_ar[10] = pTargetCPU_REGS->AR(10);
prev_ar[11] = pTargetCPU_REGS->AR(11);
gui_fprintf(fStatusStream,
"AR8-B="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->AR(8)
,pTargetCPU_REGS->AR(9)
,pTargetCPU_REGS->AR(10)
,pTargetCPU_REGS->AR(11)
);
}
if (0
|| prev_ar[12] != pTargetCPU_REGS->AR(12)
|| prev_ar[13] != pTargetCPU_REGS->AR(13)
|| prev_ar[14] != pTargetCPU_REGS->AR(14)
|| prev_ar[15] != pTargetCPU_REGS->AR(15)
)
{
prev_ar[12] = pTargetCPU_REGS->AR(12);
prev_ar[13] = pTargetCPU_REGS->AR(13);
prev_ar[14] = pTargetCPU_REGS->AR(14);
prev_ar[15] = pTargetCPU_REGS->AR(15);
gui_fprintf(fStatusStream,
"ARC-F="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->AR(12)
,pTargetCPU_REGS->AR(13)
,pTargetCPU_REGS->AR(14)
,pTargetCPU_REGS->AR(15)
);
}
}
if (gui_wants_fregs)
{
if (0
|| prev_fpr[0] != pTargetCPU_REGS->fpr[0]
|| prev_fpr[1] != pTargetCPU_REGS->fpr[1]
|| prev_fpr[2] != pTargetCPU_REGS->fpr[2]
|| prev_fpr[3] != pTargetCPU_REGS->fpr[3]
)
{
prev_fpr[0] = pTargetCPU_REGS->fpr[0];
prev_fpr[1] = pTargetCPU_REGS->fpr[1];
prev_fpr[2] = pTargetCPU_REGS->fpr[2];
prev_fpr[3] = pTargetCPU_REGS->fpr[3];
gui_fprintf(fStatusStream,
"FR0-2="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->fpr[0]
,pTargetCPU_REGS->fpr[1]
,pTargetCPU_REGS->fpr[2]
,pTargetCPU_REGS->fpr[3]
);
}
if (0
|| prev_fpr[4] != pTargetCPU_REGS->fpr[4]
|| prev_fpr[5] != pTargetCPU_REGS->fpr[5]
|| prev_fpr[6] != pTargetCPU_REGS->fpr[6]
|| prev_fpr[7] != pTargetCPU_REGS->fpr[7]
)
{
prev_fpr[4] = pTargetCPU_REGS->fpr[4];
prev_fpr[5] = pTargetCPU_REGS->fpr[5];
prev_fpr[6] = pTargetCPU_REGS->fpr[6];
prev_fpr[7] = pTargetCPU_REGS->fpr[7];
gui_fprintf(fStatusStream,
"FR4-6="REG32FMT" "REG32FMT" "REG32FMT" "REG32FMT"\n"
,pTargetCPU_REGS->fpr[4]
,pTargetCPU_REGS->fpr[5]
,pTargetCPU_REGS->fpr[6]
,pTargetCPU_REGS->fpr[7]
);
}
}
if (gui_wants_fregs64)
{
if (0
|| prev_fpr64[0] != pTargetCPU_REGS->fpr[0]
|| prev_fpr64[1] != pTargetCPU_REGS->fpr[1]
|| prev_fpr64[2] != pTargetCPU_REGS->fpr[2]
|| prev_fpr64[3] != pTargetCPU_REGS->fpr[3]
)
{
prev_fpr64[0] = pTargetCPU_REGS->fpr[0];
prev_fpr64[1] = pTargetCPU_REGS->fpr[1];
prev_fpr64[2] = pTargetCPU_REGS->fpr[2];
prev_fpr64[3] = pTargetCPU_REGS->fpr[3];
gui_fprintf(fStatusStream,
"64_FR0-1="REG32FMT""REG32FMT" "REG32FMT""REG32FMT"\n"
,pTargetCPU_REGS->fpr[0] ,pTargetCPU_REGS->fpr[1]
,pTargetCPU_REGS->fpr[2] ,pTargetCPU_REGS->fpr[3]
);
}
if (0
|| prev_fpr64[4] != pTargetCPU_REGS->fpr[4]
|| prev_fpr64[5] != pTargetCPU_REGS->fpr[5]
|| prev_fpr64[6] != pTargetCPU_REGS->fpr[6]
|| prev_fpr64[7] != pTargetCPU_REGS->fpr[7]
)
{
prev_fpr64[4] = pTargetCPU_REGS->fpr[4];
prev_fpr64[5] = pTargetCPU_REGS->fpr[5];
prev_fpr64[6] = pTargetCPU_REGS->fpr[6];
prev_fpr64[7] = pTargetCPU_REGS->fpr[7];
gui_fprintf(fStatusStream,
"64_FR2-3="REG32FMT""REG32FMT" "REG32FMT""REG32FMT"\n"
,pTargetCPU_REGS->fpr[4] ,pTargetCPU_REGS->fpr[5]
,pTargetCPU_REGS->fpr[6] ,pTargetCPU_REGS->fpr[7]
);
}
if (0
|| prev_fpr64[8] != pTargetCPU_REGS->fpr[8]
|| prev_fpr64[9] != pTargetCPU_REGS->fpr[9]
|| prev_fpr64[10] != pTargetCPU_REGS->fpr[10]
|| prev_fpr64[11] != pTargetCPU_REGS->fpr[11]
)
{
prev_fpr64[8] = pTargetCPU_REGS->fpr[8];
prev_fpr64[9] = pTargetCPU_REGS->fpr[9];
prev_fpr64[10] = pTargetCPU_REGS->fpr[10];
prev_fpr64[11] = pTargetCPU_REGS->fpr[11];
gui_fprintf(fStatusStream,
"64_FR4-5="REG32FMT""REG32FMT" "REG32FMT""REG32FMT"\n"
,pTargetCPU_REGS->fpr[8] ,pTargetCPU_REGS->fpr[9]
,pTargetCPU_REGS->fpr[10] ,pTargetCPU_REGS->fpr[11]
);
}
if (0
|| prev_fpr64[12] != pTargetCPU_REGS->fpr[12]
|| prev_fpr64[13] != pTargetCPU_REGS->fpr[13]
|| prev_fpr64[14] != pTargetCPU_REGS->fpr[14]
|| prev_fpr64[15] != pTargetCPU_REGS->fpr[15]
)
{
prev_fpr64[12] = pTargetCPU_REGS->fpr[12];
prev_fpr64[13] = pTargetCPU_REGS->fpr[13];
prev_fpr64[14] = pTargetCPU_REGS->fpr[14];
prev_fpr64[15] = pTargetCPU_REGS->fpr[15];
gui_fprintf(fStatusStream,
"64_FR6-7="REG32FMT""REG32FMT" "REG32FMT""REG32FMT"\n"
,pTargetCPU_REGS->fpr[12] ,pTargetCPU_REGS->fpr[13]
,pTargetCPU_REGS->fpr[14] ,pTargetCPU_REGS->fpr[15]
);
}
if (0
|| prev_fpr64[16] != pTargetCPU_REGS->fpr[16]
|| prev_fpr64[17] != pTargetCPU_REGS->fpr[17]
|| prev_fpr64[18] != pTargetCPU_REGS->fpr[18]
|| prev_fpr64[19] != pTargetCPU_REGS->fpr[19]
)
{
prev_fpr64[16] = pTargetCPU_REGS->fpr[16];
prev_fpr64[17] = pTargetCPU_REGS->fpr[17];
prev_fpr64[18] = pTargetCPU_REGS->fpr[18];
prev_fpr64[19] = pTargetCPU_REGS->fpr[19];
gui_fprintf(fStatusStream,
"64_FR8-9="REG32FMT""REG32FMT" "REG32FMT""REG32FMT"\n"
,pTargetCPU_REGS->fpr[16] ,pTargetCPU_REGS->fpr[17]
,pTargetCPU_REGS->fpr[18] ,pTargetCPU_REGS->fpr[19]
);
}
if (0
|| prev_fpr64[20] != pTargetCPU_REGS->fpr[20]
|| prev_fpr64[21] != pTargetCPU_REGS->fpr[21]
|| prev_fpr64[22] != pTargetCPU_REGS->fpr[22]
|| prev_fpr64[23] != pTargetCPU_REGS->fpr[23]
)
{
prev_fpr64[20] = pTargetCPU_REGS->fpr[20];
prev_fpr64[21] = pTargetCPU_REGS->fpr[21];
prev_fpr64[22] = pTargetCPU_REGS->fpr[22];
prev_fpr64[23] = pTargetCPU_REGS->fpr[23];
gui_fprintf(fStatusStream,
"64_FRA-B="REG32FMT""REG32FMT" "REG32FMT""REG32FMT"\n"
,pTargetCPU_REGS->fpr[20] ,pTargetCPU_REGS->fpr[21]
,pTargetCPU_REGS->fpr[22] ,pTargetCPU_REGS->fpr[23]
);
}
if (0
|| prev_fpr64[24] != pTargetCPU_REGS->fpr[24]
|| prev_fpr64[25] != pTargetCPU_REGS->fpr[25]
|| prev_fpr64[26] != pTargetCPU_REGS->fpr[26]
|| prev_fpr64[27] != pTargetCPU_REGS->fpr[27]
)
{
prev_fpr64[24] = pTargetCPU_REGS->fpr[24];
prev_fpr64[25] = pTargetCPU_REGS->fpr[25];
prev_fpr64[26] = pTargetCPU_REGS->fpr[26];
prev_fpr64[27] = pTargetCPU_REGS->fpr[27];
gui_fprintf(fStatusStream,
"64_FRC-D="REG32FMT""REG32FMT" "REG32FMT""REG32FMT"\n"
,pTargetCPU_REGS->fpr[24] ,pTargetCPU_REGS->fpr[25]
,pTargetCPU_REGS->fpr[26] ,pTargetCPU_REGS->fpr[27]
);
}
if (0
|| prev_fpr64[28] != pTargetCPU_REGS->fpr[28]
|| prev_fpr64[29] != pTargetCPU_REGS->fpr[29]
|| prev_fpr64[30] != pTargetCPU_REGS->fpr[30]
|| prev_fpr64[31] != pTargetCPU_REGS->fpr[31]
)
{
prev_fpr64[28] = pTargetCPU_REGS->fpr[28];
prev_fpr64[29] = pTargetCPU_REGS->fpr[29];
prev_fpr64[30] = pTargetCPU_REGS->fpr[30];
prev_fpr64[31] = pTargetCPU_REGS->fpr[31];
gui_fprintf(fStatusStream,
"64_FRE-F="REG32FMT""REG32FMT" "REG32FMT""REG32FMT"\n"
,pTargetCPU_REGS->fpr[28] ,pTargetCPU_REGS->fpr[29]
,pTargetCPU_REGS->fpr[30] ,pTargetCPU_REGS->fpr[31]
);
}
}
}
///////////////////////////////////////////////////////////////////////////////
char szQueryDeviceBuff[ MAX_DEVICEQUERY_LEN + 1 ]; // (always +1 for safety!)
///////////////////////////////////////////////////////////////////////////////
// Send status information messages back to the gui... (VERY inefficient!)
void UpdateDeviceStatus ()
{
DEVBLK* pDEVBLK;
char* pDEVClass;
BYTE chOnlineStat, chBusyStat, chPendingStat, chOpenStat;
if (sysblk.shutdown) return;
// Process ALL the devices in the entire configuration each time...
for (pDEVBLK = sysblk.firstdev; pDEVBLK != NULL; pDEVBLK = pDEVBLK->nextdev)
{
// Does this device actually exist in the configuration?
if (!IS_DEV( pDEVBLK )) continue; // (no, skip)
// Retrieve this device's filename and optional settings parameter values...
szQueryDeviceBuff[MAX_DEVICEQUERY_LEN] = 0; // (buffer allows room for 1 extra)
(pDEVBLK->hnd->query)(pDEVBLK, &pDEVClass, MAX_DEVICEQUERY_LEN, szQueryDeviceBuff);
if (0 != szQueryDeviceBuff[MAX_DEVICEQUERY_LEN]) // (buffer overflow?)
{
WRMSG
(
HHC01540, "E"
,SSID_TO_LCSS(pDEVBLK->ssid)
,pDEVBLK->devnum
);
}
szQueryDeviceBuff[MAX_DEVICEQUERY_LEN] = 0; // (enforce NULL termination)
// Device status flags...
chOnlineStat =
chBusyStat =
chPendingStat =
chOpenStat = '0';
if ((!pDEVBLK->console && pDEVBLK->fd >= 0) ||
( pDEVBLK->console && pDEVBLK->connected)) chOnlineStat = '1';
if (pDEVBLK->busy) chBusyStat = '1';
if (IOPENDING(pDEVBLK)) chPendingStat = '1';
if (pDEVBLK->fd > MAX(STDIN_FILENO,MAX(STDOUT_FILENO,STDERR_FILENO))) chOpenStat = '1';
// Send status message back to gui...
#if defined(_FEATURE_INTEGRATED_3270_CONSOLE)
if (pDEVBLK == sysblk.sysgdev)
{
gui_fprintf( fStatusStream,
"DEV=0000 SYSG %-4.4s %c%c%c%c %s\n"
,pDEVClass
,chOnlineStat
,chBusyStat
,chPendingStat
,chOpenStat
,szQueryDeviceBuff
);
}
else
#endif // defined(_FEATURE_INTEGRATED_3270_CONSOLE)
gui_fprintf(fStatusStream,
"DEV=%4.4X %4.4X %-4.4s %c%c%c%c %s\n"
,pDEVBLK->devnum
,pDEVBLK->devtype
,pDEVClass
,chOnlineStat
,chBusyStat
,chPendingStat
,chOpenStat
,szQueryDeviceBuff
);
}
// Since the device list can be in any order and devices can be added
// and/or removed at any time, the GUI needs to know "That's all the
// devices there are" so that it can detect when devices are removed...
gui_fprintf(fStatusStream, "DEV=X\n"); // (indicates end of list)
}
///////////////////////////////////////////////////////////////////////////////
// Send device status msgs to the gui IF NEEDED... (slightly more efficient)
void NewUpdateDevStats ()
{
DEVBLK* pDEVBLK;
GUISTAT* pGUIStat;
char* pDEVClass;
BYTE chOnlineStat, chBusyStat, chPendingStat, chOpenStat;
BOOL bUpdatesSent = FALSE;
static BOOL bFirstBatch = TRUE;
if (sysblk.shutdown) return;
// Process ALL the devices in the entire configuration each time...
// (But only send device status messages to the GUI only when the
// device's status actually changes and not continuously like before)
for (pDEVBLK = sysblk.firstdev; pDEVBLK != NULL; pDEVBLK = pDEVBLK->nextdev)
{
pGUIStat = pDEVBLK->pGUIStat;
// Does this device exist in the configuration?
if (!IS_DEV( pDEVBLK ))
{
// This device no longer exists in the configuration...
// If we haven't yet notified the GUI about this device
// being deleted from the configuration, then do so at
// this time...
if (*pGUIStat->pszNewStatStr)
{
// Send "device deleted" message...
gui_fprintf ( fStatusStream, "DEVD=%4.4X\n", pDEVBLK->devnum );
bUpdatesSent = TRUE;
*pGUIStat->pszNewStatStr = 0; // (prevent re-reporting it)
*pGUIStat->pszOldStatStr = 0; // (prevent re-reporting it)
}
continue; // (go on to next device)
}
// Retrieve this device's filename and optional settings parameter values...
szQueryDeviceBuff[MAX_DEVICEQUERY_LEN] = 0; // (buffer allows room for 1 extra)
(pDEVBLK->hnd->query)(pDEVBLK, &pDEVClass, MAX_DEVICEQUERY_LEN, szQueryDeviceBuff);
if (0 != szQueryDeviceBuff[MAX_DEVICEQUERY_LEN]) // (buffer overflow?)
{
WRMSG
(
HHC01540, "E"
,SSID_TO_LCSS(pDEVBLK->ssid)
,pDEVBLK->devnum
);
}
szQueryDeviceBuff[MAX_DEVICEQUERY_LEN] = 0; // (enforce NULL termination)
// Device status flags...
chOnlineStat =
chBusyStat =
chPendingStat =
chOpenStat = '0';
if ((!pDEVBLK->console && pDEVBLK->fd >= 0) ||
( pDEVBLK->console && pDEVBLK->connected)) chOnlineStat = '1';
if (pDEVBLK->busy) chBusyStat = '1';
if (IOPENDING(pDEVBLK)) chPendingStat = '1';
if (pDEVBLK->fd > MAX(STDIN_FILENO,MAX(STDOUT_FILENO,STDERR_FILENO))) chOpenStat = '1';
// Build a new "device added" or "device changed"
// status string for this device...
#if defined(_FEATURE_INTEGRATED_3270_CONSOLE)
if (pDEVBLK == sysblk.sysgdev)
{
snprintf( pGUIStat->pszNewStatStr, GUI_STATSTR_BUFSIZ,
"DEV%c=0000 SYSG %-4.4s %c%c%c%c %s"
,*pGUIStat->pszOldStatStr ? 'C' : 'A'
,pDEVClass
,chOnlineStat
,chBusyStat
,chPendingStat
,chOpenStat
,szQueryDeviceBuff
);
}
else
#endif // defined(_FEATURE_INTEGRATED_3270_CONSOLE)
snprintf( pGUIStat->pszNewStatStr, GUI_STATSTR_BUFSIZ,
"DEV%c=%4.4X %4.4X %-4.4s %c%c%c%c %s"
,*pGUIStat->pszOldStatStr ? 'C' : 'A'
,pDEVBLK->devnum
,pDEVBLK->devtype
,pDEVClass
,chOnlineStat
,chBusyStat
,chPendingStat
,chOpenStat
,szQueryDeviceBuff
);
*(pGUIStat->pszNewStatStr + GUI_STATSTR_BUFSIZ - 1) = 0;
// If the new status string is different from the old one,
// then send the new one to the GUI and swap buffer ptrs
// for next time. In this way we only send device status
// msgs to the GUI only when the status actually changes...
if (strcmp( pGUIStat->pszNewStatStr, pGUIStat->pszOldStatStr ))
{
gui_fprintf ( fStatusStream, "%s\n", pGUIStat->pszNewStatStr );
bUpdatesSent = TRUE;
{
register char*
pszSavStatStr = pGUIStat->pszNewStatStr;
pGUIStat->pszNewStatStr = pGUIStat->pszOldStatStr;
pGUIStat->pszOldStatStr = pszSavStatStr;
}
}
}
// Only send End-of-Batch indicator if we sent any updates or
// if this is the first device-list update since powering on.
if ( bUpdatesSent || bFirstBatch )
{
bFirstBatch = FALSE;
gui_fprintf(fStatusStream, "DEVX=\n"); // (send end-of-batch indicator)
}
}
///////////////////////////////////////////////////////////////////////////////
// Our Hercules "debug_cpu_state" override...
//
// Hercules calls the following function from several different places to fix
// an unintentional problem caused by the new logger mechanism (wherein stdout
// and stderr now point to the same stream) due to a oversight (bug) on my part
// wherein the 'LOAD' and 'MAN' messages are being [mistakenly] written to stdout
// instead of stderr (where they normally should be). The current version of
// the gui expects both messages to come in on the stdout stream, but due to the
// recent logger changes, they now come in on the stderr stream instead (because
// stdout was duped to stderr by the new logger logic) thus causing the gui to
// miss seeing them without the below fix. The below fix simply corrects for the
// problem by simply writing the two messages to the stdout stream where older
// versions of the gui expect to see them.
void* gui_debug_cpu_state ( REGS* pREGS )
{
void *(*next_debug_call)(REGS *);
static BOOL bLoading = FALSE;
static BOOL bStopped = FALSE;
if (sysblk.shutdown) return NULL;
if (pTargetCPU_REGS && pREGS != pTargetCPU_REGS)
return NULL;
if (bLoading != (pREGS->loadstate ? TRUE : FALSE))
{
bLoading = (pREGS->loadstate ? TRUE : FALSE);
gui_fprintf(stdout,"LOAD=%c\n", bLoading ? '1' : '0');
}
if (bStopped != ((CPUSTATE_STOPPED == pREGS->cpustate) ? TRUE : FALSE))
{
bStopped = ((CPUSTATE_STOPPED == pREGS->cpustate) ? TRUE : FALSE);
gui_fprintf(stdout,"MAN=%c\n", bStopped ? '1' : '0');
}
if((next_debug_call = HDL_FINDNXT( gui_debug_cpu_state )))
return next_debug_call( pREGS );
return NULL; // (I have no idea why this is a void* func)
}
///////////////////////////////////////////////////////////////////////////////
// Our Hercules "debug_cd_cmd" hook...
//
// The following function is called by the 'cd_cmd' panel command to notify
// the GUI of what the new current directory was just changed to...
void* gui_debug_cd_cmd( char* pszCWD )
{
ASSERT( pszCWD );
if (gui_version >= 1.12)
gui_fprintf( fStatusStream, "]CWD=%s\n", pszCWD );
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
// Streams 'fprintf' function to prevent interleaving collision problem...
LOCK gui_fprintf_lock;
void gui_fprintf( FILE* stream, const char* pszFormat, ... )
{
va_list vl;
va_start( vl, pszFormat );
obtain_lock ( &gui_fprintf_lock );
vfprintf( stream, pszFormat, vl );
fflush( stream );
release_lock( &gui_fprintf_lock );
}
///////////////////////////////////////////////////////////////////////////////
// Acquire any resources we need in order to operate...
// (called by 'gui_panel_display' before main loop initiates...)
void Initialize ()
{
// Disable the "quiet" command...
{
char cmdbuf[128] = {0};
MSGBUF( cmdbuf, "%s %s %s", "$zapcmd", "quiet", "NoCmd" );
VERIFY( 0 == InternalHercCmd( cmdbuf ));
}
// Initialize streams...
fOutputStream = OUTPUT_STREAM_FILE_PTR;
fStatusStream = STATUS_STREAM_FILE_PTR;
nInputStreamFileNum = fileno( INPUT_STREAM_FILE_PTR );
// Allocate input stream buffer...
if (!(pszInputBuff = (char *) malloc( nInputBuffSize )))
{
fprintf(stderr,
_("HHC90000D DBG: malloc() pszInputBuff failed: %s\n")
,strerror(errno));
exit(0);
}
memset(pszInputBuff, 0, nInputBuffSize);
nInputLen = 0;
// Allocate command processing buffer...
if (!(pszCommandBuff = (char *) malloc( nCommandBuffSize )))
{
fprintf(stderr,
_("HHC90000D DBG: malloc() pszCommandBuff failed: %s\n")
,strerror(errno));
exit(0);
}
memset(pszCommandBuff, 0, nCommandBuffSize);
nCommandLen = 0;
// Initialize some variables...
HandleForcedRefresh();
}
///////////////////////////////////////////////////////////////////////////////
// Release any resources we acquired in order to operate...
// (called by 'gui_panel_display' when main loop terminates...)
void Cleanup()
{
if (pszInputBuff)
free(pszInputBuff);
if (pszCommandBuff)
free(pszCommandBuff);
}
///////////////////////////////////////////////////////////////////////////////
// Hercules "daemon_task" -or- "panel_display" override...
void gui_panel_display ()
{
SET_THREAD_NAME( "dyngui" ); // (for debugging)
WRMSG( HHC01541,"I" ); // "Dyngui.dll initiated"
Initialize(); // (acquire resources)
ProcessingLoop(); // PRIMARY PROCESSING LOOP... (does not
// return until Hercules is terminated)
Cleanup(); // (relinquish resources)
WRMSG( HHC01542,"I" ); // "Dyngui.dll terminated"
}
/*****************************************************************************\
Hercules Dynamic Loader control sections...
\*****************************************************************************/
/*
Note that ALL of the below "sections" are actually just simple functions
that Hercules's dynamic loader logic calls, thus allowing you to insert
whatever 'C' code you may need directly into any of the below sections.
*/
///////////////////////////////////////////////////////////////////////////////
// HDL_DEPENDENCY_SECTION
// The following are the various Hercules structures whose layout this module
// depends on. The layout of the following structures (size and version) MUST
// match the layout that was used to build Hercules with. If the size/version
// of any of the following structures changes (and a new version of Hercules
// is built using the new layout), then THIS module must also be built with
// the new layout as well. The layout (size/version) of the structure as it
// was when Hercules was built MUST MATCH the layout as it was when THIS DLL
// was built)
/* Libtool static name colision resolution */
/* note : lt_dlopen will look for symbol & modulename_LTX_symbol */
#if !defined(HDL_BUILD_SHARED) && defined(HDL_USE_LIBTOOL)
#define hdl_ddev dyngui_LTX_hdl_ddev
#define hdl_depc dyngui_LTX_hdl_depc
#define hdl_reso dyngui_LTX_hdl_reso
#define hdl_init dyngui_LTX_hdl_init
#define hdl_fini dyngui_LTX_hdl_fini
#endif
HDL_DEPENDENCY_SECTION // (define module dependencies)
HDL_DEPENDENCY ( HERCULES ); // Hercules itself
HDL_DEPENDENCY ( SYSBLK ); // Master control block
HDL_DEPENDENCY ( REGS ); // CPU regs and such
HDL_DEPENDENCY ( DEVBLK ); // Device info block
END_DEPENDENCY_SECTION
///////////////////////////////////////////////////////////////////////////////
// HDL_REGISTER_SECTION
// The following section defines the entry points within Hercules that THIS
// module is overriding (replacing). That is to say, THIS module's functions
// will be called by Hercules instead of the normal Hercules function (if any).
// The functions defined below thus provide additional/different functionality
// above/beyond the functionality normally provided by Hercules. (Of course,
// yet OTHER dlls may have further overridden whatever overrides we register
// here, such as would likely be the case for panel command overrides).
HDL_REGISTER_SECTION // ("Register" our entry-points)
{
// Perform static module initialization...
gui_nounload = 1; // (reject any unload attempt)
initialize_lock( &gui_fprintf_lock ); // (initialize GUI fprintf LOCK)
// Register all of our override entry-points...
// Hercules's Our
// registered overriding
// entry-point entry-point
// name value
HDL_REGISTER ( panel_display, gui_panel_display );// (Yep! We override EITHER!)
HDL_REGISTER ( daemon_task, gui_panel_display );// (Yep! We override EITHER!)
HDL_REGISTER ( debug_cpu_state, gui_debug_cpu_state );
HDL_REGISTER ( debug_cd_cmd, gui_debug_cd_cmd );
HDL_REGISTER ( panel_command, gui_panel_command );
}
END_REGISTER_SECTION
#if defined( WIN32 ) && !defined( HDL_USE_LIBTOOL )
#if !defined( _MSVC_ )
#undef sysblk
#endif
///////////////////////////////////////////////////////////////////////////////
// HDL_RESOLVER_SECTION
// The following section "resolves" entry-points that this module needs. The
// below HDL_RESOLVE entries define the names of Hercules's registered entry-
// points that we need "imported" to us (so that we may call those functions
// directly ourselves). The HDL_RESOLVE_PTRVAR entries set the named pointer
// variable value (i.e. the name of OUR pointer variable) to the registered
// entry-point value that was registered by Hercules or some other DLL.
HDL_RESOLVER_SECTION // ("Resolve" needed entry-points)
{
// Registered
// entry-points
// that we call
HDL_RESOLVE ( panel_command );
#if !defined( _MSVC_ )
// Our pointer- Registered entry-
// variable name point value name
HDL_RESOLVE_PTRVAR ( psysblk, sysblk );
#endif
}
END_RESOLVER_SECTION
#endif
///////////////////////////////////////////////////////////////////////////////
// HDL_FINAL_SECTION
// The following section defines what should be done immediately before this
// module is unloaded. It is nothing more than a function that is called by
// Hercules just before your module is unloaded. You can do anything you want
// but the normal thing to do is release any resources that were acquired when
// your module was loaded (e.g. release memory that was malloc'ed, etc).
HDL_FINAL_SECTION
{
bDoneProcessing = TRUE; // (tell main loop to stop processing)
usleep(100000); // (brief delay to give GUI time
// to display ALL shutdown msgs)
rc = gui_nounload; // (reject unloads when activated)
}
END_FINAL_SECTION
///////////////////////////////////////////////////////////////////////////////
#endif /*defined(OPTION_DYNAMIC_LOAD)*/
#endif // EXTERNALGUI